Pagination
Pagination
Section titled “Pagination”Every list endpoint in the SIPSTACK Developer API returns results in pages, newest first. There are two paging modes: offset paging (page / limit, with a running total) and keyset paging (an opaque next_cursor). Offset paging is available on every list endpoint; keyset paging is available on selected high-volume list endpoints and is the safer choice for walking large result sets.
Offset Paging
Section titled “Offset Paging”Pass page (1-based) and limit. The response carries a pagination object describing where you are:
GET /api/v2/contacts?page=2&limit=50x-api-key: sk_live_...{ "success": true, "data": [ /* up to `limit` records */ ], "pagination": { "page": 2, "limit": 50, "total": 143, "totalPages": 6 }}| Field | Meaning |
|---|---|
page | The page you requested (1-based) |
limit | Items per page |
total | Total matching records across all pages |
totalPages | ceil(total / limit) — the last page number |
Walk pages by incrementing page until page === totalPages (or data comes back empty).
The offset trade-off
Section titled “The offset trade-off”Offset paging is convenient (you get a total and can jump to any page) but it is not stable under writes: if new rows arrive between requests, records shift across page boundaries and you can see a row twice or skip one. For a live, growing dataset — message and call history especially — prefer keyset paging.
Keyset (Cursor) Paging
Section titled “Keyset (Cursor) Paging”Keyset paging walks the result set by an opaque cursor anchored to the last row you saw, so new rows arriving at the top never cause drift or duplicates. Instead of page, pass the cursor you got from the previous response’s next_cursor:
GET /api/v2/messages?limit=100x-api-key: sk_live_...{ "success": true, "data": [ /* up to `limit` records */ ], "pagination": { "page": null, "limit": 100, "total": null, "totalPages": null }, "next_cursor": "eyJpZCI6MTAwMjR9"}Feed next_cursor back as cursor to fetch the next page:
GET /api/v2/messages?limit=100&cursor=eyJpZCI6MTAwMjR9- When
cursoris set, the request runs in keyset mode andpage,total, andtotalPagescome backnull— there is no total-record count in cursor mode. next_cursorisnullon the last page. Stop when it’s null.- A malformed or expired cursor returns
400.
async function* allMessages(limit = 100) { let cursor; do { const url = new URL('https://api.sipstack.com/api/v2/messages'); url.searchParams.set('limit', String(limit)); if (cursor) url.searchParams.set('cursor', cursor);
const res = await fetch(url, { headers: { 'x-api-key': process.env.SIPSTACK_API_KEY } }); const body = await res.json(); yield* body.data; cursor = body.next_cursor; // null on the last page } while (cursor);}Which Endpoints Support Which
Section titled “Which Endpoints Support Which”Keyset (cursor / next_cursor) paging is available on these four list endpoints:
| Endpoint | Product | Offset | Keyset |
|---|---|---|---|
GET /api/v2/messages | Flare | ✓ | ✓ |
GET /api/v2/calls | Nova | ✓ | ✓ |
GET /api/v2/contacts | Flare | ✓ | ✓ |
GET /api/v2/webhooks | Webhooks | ✓ | ✓ |
Every other list endpoint — Nova call/number/voicemail lists, Aura call lists, Flare campaign and contact-list lists, webhook delivery logs, and the rest — supports offset paging only (page / limit). Passing a cursor to an offset-only endpoint has no effect. Consult the API Reference for each endpoint’s exact parameters.
Best Practices
Section titled “Best Practices”- Use keyset paging for history — message and call lists grow constantly;
next_cursorwon’t skip or double-count rows. - Request the maximum
limit— fewer round trips, fewer rate-limit hits. - Stop on the signal, not a guess — offset:
page === totalPages; keyset:next_cursor === null. - Never store a cursor long-term — it’s opaque and tied to a query; re-list from the top if you need to resync.