Skip to main content
List endpoints (e.g. GET /v1/customers) use cursor pagination. No offset, no page number — cursors are deterministic under writes and won’t skip or duplicate records when new customers are ingested mid-walk.

How it works

Pass next_cursor back as cursor on the next request:
When you reach the end, has_more is false and next_cursor is null.

Limit

What’s in a cursor

The cursor is opaque — base64url-encoded JSON containing the last row’s (createdAt, id). Don’t parse or generate cursors yourself; pass them back as-is.

Combining with filters

Filters work alongside cursors. The cursor sticks to the filter set from the original request — don’t change filters mid-walk:

Sample walk loop

Total count (opt-in)

For UIs that want to render “Showing 50 of 1,283” or for sizing a background job, request include=total_count:
total_count reflects the full filter set, not the current page — walking forward does not shrink it. It costs an extra COUNT(*) query per call, so don’t request it on hot polling loops where you don’t need the number. If include is omitted, total_count is not returned at all (rather than null) so consumers can detect “did I ask for it?” unambiguously.

Polling for changes

For “what changed since I last looked?” use the updated_since filter alongside pagination. See Polling for status for a robust loop.