Pagination
List endpoints are paginated with an opaque cursor. Pass it back on the next request to get the next page. No offsets, no page numbers, no skipped or duplicated rows when the underlying data shifts.
TL;DR
Send limit + (optional) cursor on the request. Read pagination.nextCursor + pagination.hasMore on the response. Loop while hasMore is true.
Request parameters
| Parameter | Type | Description |
|---|---|---|
limit | integer | Page size. Default 25, max 100. Servers may return fewer rows than requested even when more results exist — always trust hasMore, not the row count. |
cursor | string | Opaque token from the previous response's pagination.nextCursor. Omit for the first page. Don't parse, don't persist long-term — cursors can change shape between releases. |
Response envelope
Every list endpoint returns the rows plus a pagination block:
{
"data": [
{ "id": "res_abc", "checkIn": "2026-06-01", ... },
{ "id": "res_def", "checkIn": "2026-06-02", ... }
],
"pagination": {
"nextCursor": "eyJpZCI6InJlc19kZWYifQ==",
"hasMore": true
}
}When you reach the end, hasMore is false and nextCursor is null.
Paging loop
let cursor: string | null = null
const all: Reservation[] = []
do {
const url = new URL('https://api.repull.dev/v1/reservations')
url.searchParams.set('limit', '100')
if (cursor) url.searchParams.set('cursor', cursor)
const res = await fetch(url, {
headers: {
Authorization: `Bearer ${process.env.REPULL_API_KEY}`,
'X-Workspace-Id': process.env.REPULL_WORKSPACE_ID!,
},
})
const body = await res.json()
all.push(...body.data)
cursor = body.pagination.hasMore ? body.pagination.nextCursor : null
} while (cursor)Behavior to know
- Cursors are stable across mutations.Inserts and deletes during a paging session don't shift other rows in or out — you won't skip or double-count.
- Cursors are scoped to the query. If you change a filter (e.g.
statusorcheckInAfter) between requests, start over with no cursor. - Cursors expire after 24 hours. A long pause and resume may return
invalid_paramsoncursor— restart from the first page. - Order is endpoint-specificand documented on each list endpoint's page. For most resources it's newest-first by creation time.
- No total count. The API never returns a total — counting every row would force a full scan on every request. Use
hasMoreto know when to stop.
Related
- Rate limits — large pages cost less than many small ones, but don't exceed the per-window budget.
- Errors — paging errors return
invalid_paramson thecursororlimitfield.
AI