Pagination
All list endpoints return paginated results using cursor-based pagination. This approach is more efficient than offset-based pagination, especially for large datasets.
How It Works
- Request a page with an optional
limit - The response includes a
paginationobject with cursors - Pass
next_cursorto get the next page,prev_cursorto go back
Results are ordered by the endpoint’s default order unless you override it with
order_by.
Cursors are opaque tokens. Treat them as read-only values from the API.
Do not parse them, construct them manually, or reuse them with a different
order_by value. Changing ordering means restarting pagination from the first
page.
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | number | 10 | Items per page (1–100) |
next_cursor | string | — | Opaque cursor for the next page |
prev_cursor | string | — | Opaque cursor for the previous page |
include_total_count | boolean | true | Include total count in response |
Response Format
Every list endpoint returns this structure:
{ "data": [ ... ], "pagination": { "total": 84, "next_cursor": "inv_def456", "prev_cursor": null, "has_more": true }}| Field | Description |
|---|---|
total | Total number of matching items (or -1 if include_total_count=false) |
next_cursor | Pass this as next_cursor to get the next page. null if no more results. Valid only for the same effective ordering. |
prev_cursor | Pass this as prev_cursor to go back. null on the first page. Valid only for the same effective ordering. |
has_more | true if there are more results after this page |
Walking Through Pages
To iterate through all results, keep requesting pages until has_more is false:
// title: SDK
// First page
const page1 = await sdk.invoices.list({ limit: 20 });
console.log(page1.data); // Invoice[]
console.log(page1.pagination.has_more); // true
// Next page using cursor
if (page1.pagination.next_cursor) {
const page2 = await sdk.invoices.list({
limit: 20,
next_cursor: page1.pagination.next_cursor,
});
}# title: cURL
curl "https://eu.spaceinvoices.com/invoices?limit=20" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "X-Entity-Id: ent_123"
# Next page using cursor from response
curl "https://eu.spaceinvoices.com/invoices?limit=20&next_cursor=CURSOR_FROM_RESPONSE" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "X-Entity-Id: ent_123"Fetching Every Page Automatically
Hand-rolling a while (has_more) loop is easy to get wrong — a typo in the cursor parameter,
or forgetting to update it, means the loop never advances and keeps re-requesting the same
page. That exact mistake has taken down a production database by looping for 18+ hours.
The JavaScript SDK exports a listAll() helper from the main @spaceinvoices/js-sdk package
(it is not available from the lean @spaceinvoices/js-sdk/sdk subpath). It is a standalone
function, not a method on the list call itself: pass it any list method plus the options you’d
normally pass to that method, and it walks every page for you, yielding one item at a time:
// title: Fetch every page automatically
// listAll() is exported from the main __PROTECTED_0__ package (the lean
// __PROTECTED_1__ subpath does not include it). It walks every page of a
// list method for you and stops as soon as the API reports no further pages
// (pagination.next_cursor is null) - no manual cursor loop.
for await (const invoice of listAll(sdk.invoices.list, { limit: 100 })) {
console.log(invoice.id);
}
// listAll() carries a safety limit (1000 pages) and also throws if the API ever
// returns a non-advancing cursor, or a response with no pagination envelope at all
// (some list endpoints return a bare array and are not cursor-paginated). A
// non-advancing cursor is the exact bug that once sent a customer's hand-rolled
// loop into an 18-hour, half-million-request outage - narrow your filters (query,
// date range, etc.) if you expect a result set large enough to need more than 1000
// pages.listAll() works with endpoints that return the standard { data, pagination: { next_cursor } } envelope. For the few list methods that return something else, such as a plain array, call them directly.
It throws a clear error instead of returning partial results when:
- the response isn’t in the standard shape;
- the cursor stops advancing;
- it reaches 1000 pages, which almost always means a pagination bug rather than a huge result set.
Performance Tip
The total count requires an extra database query. For large datasets where you don’t need the exact count, disable it:
?include_total_count=falseWhen disabled, pagination.total returns -1 but pagination still works normally via has_more and cursors.