Lists that can grow large are paginated, and each endpoint in this reference says how. There are two main styles, offset and keyset, plus a few endpoints with their own cursor. Lists that stay small, such as channels or segments, return a plain array with no paging.
Offset pagination
Used by contacts, contact lists, segment previews and campaigns. You ask for a page by number.
| Parameter | Meaning |
|---|---|
page | Zero-based page number. The first page is 0. Default 0. |
size | Items per page. Default 20; keep it at 100 or below. The limit is not enforced on contacts, contact lists, list members or segment previews, so a larger value can make slow, heavy responses. |
The response is an envelope:
{
"data": [{ "id": 1842, "displayName": "Layla Haddad" }],
"page": 0,
"size": 20,
"totalElements": 1,
"totalPages": 1
}| Field | Meaning |
|---|---|
data | The items on this page. |
page | The page you asked for, zero-based. |
size | The page size in effect. |
totalElements | How many items match in total. |
totalPages | How many pages there are at this size. |
To read everything, request pages from 0 until page reaches totalPages - 1.
let page = 0
let totalPages = 1
while (page < totalPages) {
const res = await fetch(`https://api.cx.na6iq.com/api/v1/contacts?page=${page}&size=100`, {
headers: { Authorization: `Bearer ${process.env.NATIQ_TOKEN}` },
})
const body = await res.json()
totalPages = body.totalPages
for (const contact of body.data) console.log(contact.id)
page += 1
}Keyset pagination
Used by timelines and delivery lists, which grow at one end: a conversation’s messages, a contact’s timeline and a campaign’s deliveries. Instead of a page number you pass a position, so new items arriving while you read never shift the page under you. Timelines are read from the newest backwards, as below; a campaign’s deliveries run the other way — see the note after the example.
| Parameter | Meaning |
|---|---|
size | Items per page. The dashboard asks for 50. A contact’s timeline defaults to 50 and is clamped to 1–100. |
afterId | Return items older than this ID. 0 or omitted returns the newest. |
The response is a plain array, newest first — there is no envelope and no total. To get the next page, pass the smallest id in the array you just received as afterId.
let afterId = 0
for (;;) {
const res = await fetch(
`https://api.cx.na6iq.com/api/v1/conversations/4388/timeline?size=50&afterId=${afterId}`,
{ headers: { Authorization: `Bearer ${process.env.NATIQ_TOKEN}` } },
)
const items = await res.json()
if (items.length === 0) break
for (const item of items) console.log(item.id)
// Notes are numbered separately, so page by the oldest message, not the oldest note.
afterId = Math.min(...items.filter((item) => !item.privateNote).map((item) => item.id))
if (items.length < 50) break
}A page shorter than size is the last one. A page exactly size long may have more, so ask once more and stop when you get an empty array.
Notes and messages share a timeline
A conversation’s timeline merges messages and private notes, which are numbered independently. An id therefore does not always identify one entry on its own — use privateNote to tell them apart, and pass the id of the oldest entry that isn't a note as afterId.
Campaign deliveries run oldest first
List deliveries returns the oldest rows first, and afterId returns rows newer than it. Pass the largest id on the page to get the next one. size is clamped to 1–100.
Other cursor styles
A few endpoints page in their own way. Each endpoint documents its parameters; in summary:
| Endpoint | Parameters | Response |
|---|---|---|
| List tickets | limit (up to 100) and cursor | { "items": […], "nextCursor": "…" }. Pass nextCursor as cursor; stop when it is null. The cursor is opaque — never build or decode one. |
| List agents | limit and beforeBotId | A plain array, newest first. Pass the smallest id as beforeBotId. |
| List WhatsApp templates | limit and after | { "data": […], "nextCursor": "…" }. Pass nextCursor as after; stop when it is null. |