API reference
Pagination
Cursor-based pagination across list endpoints — request shape, response envelope, and end-of-stream signaling.
Last updated
Jobby.dev list endpoints use cursor-based pagination — you ask for limit rows, get back data plus a next_cursor, and pass that cursor back to fetch the next page. No offset / page- number indexing, ever; cursors stay stable as new rows land.
Request shape
List endpoints accept two query parameters:
limit— max rows per page. Default 25, max 100. Endpoints below 100 max are documented in the OpenAPI spec.cursor— opaque string from the previous response'snext_cursor. Omit on the first request.
# First page
curl 'https://jobby.dev/api/v1/billing/invoices?limit=25' \
-H 'Authorization: Bearer jbb_...'
# Next page
curl 'https://jobby.dev/api/v1/billing/invoices?limit=25&cursor=eyJrIjoiYWJjMTIzIn0' \
-H 'Authorization: Bearer jbb_...'Response shape
{
"data": [ ...rows... ],
"next_cursor": "eyJrIjoiYWJjMTIzIn0",
"has_more": true
}data— the rows for this page, in stable sort order (newest-first unless documented otherwise).next_cursor— pass this back as thecursorquery param to get the next page. Treat the string as opaque — its internal shape is not part of the v1 contract.has_more— boolean.falsemeans you're at the end. Whenhas_moreisfalse,next_cursoris omitted.
Stability under writes
Cursors are stable across writes that happen between page fetches. New rows added between page 1 and page 2 don't shift your view — you'll see them on a future fetch with a fresh first-page request, but they don't cause page-2 rows to appear twice or get skipped.
Trade-off: cursors are tied to a sort order. If the underlying row is deleted between page fetches, the cursor still works (it skips cleanly to the next available row); but if you re-sort the endpoint with a different filter mid-pagination, you need to restart from page 1.
Iterating to the end
Standard loop:
let cursor: string | undefined;
const all = [];
do {
const url = new URL("https://jobby.dev/api/v1/billing/invoices");
url.searchParams.set("limit", "100");
if (cursor) url.searchParams.set("cursor", cursor);
const res = await fetch(url, { headers: { Authorization: `Bearer ${token}` } });
const body = await res.json();
all.push(...body.data);
cursor = body.has_more ? body.next_cursor : undefined;
} while (cursor);Endpoints that don't paginate
A few endpoints return all rows unconditionally — webhook deliveries (capped at 50 most recent), billing status (single row). Their OpenAPI entries document this. If a list response doesn't carry has_more, it's not paginated.