Skip to content

Pagination

A long list does not come in one response. The API cuts it into pages and returns, along with the data, a mark of where it stopped. You ask for the next page by sending that mark back.

Two things go into the query:

ParameterWhat it is
limitHow many records you want in the page. Default 20, maximum 100
cursorThe mark to continue from. Do not send it on the first call

The response always has this shape:

{
"object": "list",
"data": [],
"has_more": true,
"next_cursor": "Y3JlYXRlZF9hdDoy..."
}
FieldWhat it is
objectAlways "list". This is how you recognize a list response
dataThe records of this page, in the order of the query
has_moretrue when there is still a page after this one
next_cursorThe mark to ask for the next page

Only next_cursor. Nothing else.

Do not keep the call number, the total of records read, or the last id seen in order to continue from it. The cursor already carries everything the API needs to resume from the right place.

Stop when has_more is false. While it is true, send next_cursor back in the cursor parameter and ask again.

Do not use the size of data as an end signal. A page may come smaller than limit and still have a continuation.

The cursor value is a string only the API understands. Treat it as a black box:

  • Do not read it. It looks readable in some cases, but its content is internal and may change without notice, because that breaks nobody who only sends the value back.
  • Do not build one by hand. An invented cursor is rejected.
  • Do not touch it. Send back exactly the string that came, without trimming, decoding or changing a character.

With page numbers, page 2 means “skip the first 20”. If somebody creates a record between your call for page 1 and your call for page 2, everything moves down one position: the twentieth record of page 1 becomes the first of page 2, and you read it twice. If somebody deletes a record, the opposite happens and a record never shows up.

A cursor does not count positions, it marks a point in the ordering. A record created after your first page pushes nothing, and the sweep finishes without repeating and without skipping.

That is also why there is no offset and no page in the API.

Every list accepts the same three date filters:

FilterWhat it does
created_afterOnly what was created after this instant
created_beforeOnly what was created before this instant
updated_afterOnly what changed after this instant

The default order is created_at descending, newest first.

With updated_after, the order becomes updated_at ascending. That is the sync mode: you keep the largest updated_at you have seen, and on the next round you continue from there, without rereading what did not change. Dates follow the format in Dates and money.

There is no free ordering and no filter by arbitrary field. Each resource declares its own filters in the Reference.