Pagination
How cursor-based pagination works across list endpoints
All list endpoints return paginated results using cursor-based pagination. This approach provides stable results even when data changes between requests.
Response Shape
Every paginated endpoint returns the same structure:
{
"data": [/* items */],
"hasMore": true,
"cursors": {
"next": "eyJzIjoiMjAyNi0wMy...",
"previous": null
}
}| Field | Type | Description |
|---|---|---|
data | array | The page of results |
hasMore | boolean | Whether more results exist beyond this page |
cursors.next | string | null | Cursor to fetch the next page (pass as startingAfter) |
cursors.previous | string | null | Cursor to fetch the previous page (pass as endingBefore) |
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | number | 20 | Items per page (1–100) |
startingAfter | string | — | Cursor from cursors.next for the next page |
endingBefore | string | — | Cursor from cursors.previous for the previous page |
You cannot use startingAfter and endingBefore in the same request.
Example: Paginating Forward
First page — no cursor needed:
curl -H "Authorization: Bearer sk_your_api_key" \
"https://api.crmchat.ai/v1/organizations?limit=2"{
"data": [
{ "id": "org_1", "name": "Acme Corp", "...": "..." },
{ "id": "org_2", "name": "Globex", "...": "..." }
],
"hasMore": true,
"cursors": {
"next": "eyJzIjoiMjAyNi0wMy0xMlQxMDowMDowMC4wMDBaIiwiaSI6Im9yZ18yIiwidCI6ImYifQ",
"previous": null
}
}Next page — pass startingAfter:
curl -H "Authorization: Bearer sk_your_api_key" \
"https://api.crmchat.ai/v1/organizations?limit=2&startingAfter=eyJzIjoiMjAyNi0wMy0xMlQxMDowMDowMC4wMDBaIiwiaSI6Im9yZ18yIiwidCI6ImYifQ"Last page — hasMore is false, cursors.next is null:
{
"data": [{ "id": "org_3", "name": "Initech", "...": "..." }],
"hasMore": false,
"cursors": {
"next": null,
"previous": "eyJzIjoiMjAyNi0wMy0xMlQwOTowMDowMC4wMDBaIiwiaSI6Im9yZ18zIiwidCI6ImYifQ"
}
}Iterating All Pages
To fetch every item, loop until hasMore is false:
let cursor = undefined;
const allItems = [];
do {
const url = new URL("https://api.crmchat.ai/v1/organizations");
url.searchParams.set("limit", "100");
if (cursor) url.searchParams.set("startingAfter", cursor);
const res = await fetch(url, {
headers: { Authorization: "Bearer sk_your_api_key" },
});
const page = await res.json();
allItems.push(...page.data);
cursor = page.cursors.next;
} while (cursor);Cursors
Cursors are opaque strings — do not parse or construct them. They encode the position of the last item on the current page so the server can efficiently resume from the right spot.
- Cursors are tied to a specific sort order. Do not reuse cursors across different queries.
- Cursors do not expire, but they may become invalid if the underlying data is deleted.