CRMchat APIBeta

Pagination

How cursor-based pagination works across list endpoints

View in Markdown

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
  }
}
FieldTypeDescription
dataarrayThe page of results
hasMorebooleanWhether more results exist beyond this page
cursors.nextstring | nullCursor to fetch the next page (pass as startingAfter)
cursors.previousstring | nullCursor to fetch the previous page (pass as endingBefore)

Query Parameters

ParameterTypeDefaultDescription
limitnumber20Items per page (1–100)
startingAfterstringCursor from cursors.next for the next page
endingBeforestringCursor 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"
Response
{
  "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 pagehasMore is false, cursors.next is null:

Response
{
  "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.

On this page