Pagination
Cursor-based pagination for every ZapAPI list endpoint — how to page through large result sets reliably.
Every list endpoint in the ZapAPI API uses cursor-based pagination. Cursor pagination is stable under concurrent writes — you will never see the same record twice or skip a record because another one was inserted while you were paging.
The shape of a list response
{
"status": "success",
"statusCode": 200,
"data": {
"invoices": [ … ],
"total": 137,
"limit": 50,
"nextCursor": "aWQ6MTIzNDU2Nzg5MA==",
"hasMore": true
}
}data.<resource>— the page of results (array).data.total— the total number of matching records (when cheap to compute).data.nextCursor— opaque cursor for the next page, ornullwhen there is no next page.data.hasMore— convenience boolean.
Paging with cursors
Pass nextCursor back as the cursor query parameter:
curl "https://api.zapapi.com/api/v1/invoices?limit=50&cursor=aWQ6MTIzNDU2Nzg5MA==" \
-H "Authorization: Bearer zap_live_…"Continue until hasMore is false (or nextCursor is null).
let cursor: string | undefined;
do {
const page = await zap.invoices.list({ limit: 100, cursor });
for (const invoice of page.invoices) {
// process invoice
}
cursor = page.nextCursor ?? undefined;
} while (cursor);Page size
limitdefaults to 50 and is capped at 100 on most endpoints.- Larger pages reduce round-trips but increase response size and latency — 50–100 is the sweet spot for most workloads.
Filters and sorting
Cursors are opaque — they encode the filters and sort order of the
original query. You cannot meaningfully modify a cursor, and you should not
cache a cursor across filter changes. To change filters, start a new
pagination sequence without a cursor.
Common filters available on most list endpoints:
status— filter by lifecycle state (pending,paid,expired, …).from/to— ISO-8601 date range oncreatedAt.projectId— scope to a project (implicit when using a project-scoped key).q— free-text search where supported (customers, payment links).
Why cursor, not offset
Offset pagination (?page=3&limit=50) is simple but breaks under concurrent
writes: a row inserted on page 1 shifts every subsequent page, causing
duplicates and missed rows. Cursor pagination is stable because the cursor
pins the query to a specific row — inserts and deletes before the cursor do
not affect the pages after it.