Pagination
List queries are cursor-paginated. You ask for a page size with first and
walk forward with after, using the cursor the previous page handed back.
Without a search term, this stays correct as records are added or removed
between requests. Sorting by a value that can change -- a status, a total, a
name, a date that gets edited, an updated-at time, revenue -- is different: a
record whose value changes mid-walk can appear twice or be missed. For a
complete export, walk in an order that never changes: the default order for
orders, quotes and purchase orders, ORDER_NUMBER or QUOTE_NUMBER,
sortBy: CREATED_AT for companies, and sortBy: CREATED_AT for products.
Searching (passing a search term) pages by position in the ranked results and stops at 10,000 results. Records added or removed mid-walk shift those positions, so use search to find records, not to export them.
The shape
Every paginated list returns the same structure:
edges— the items, each an object with anode(the record) and acursor.pageInfo—hasNextPageandendCursorfor fetching the next page.approxTotalCount— an approximate total, handy for showing "about N results". A few lists (the product catalog, for one) return an exacttotalCountinstead; the reference page for each operation shows which.
query ListPurchaseOrders($first: Int, $after: String) {
purchaseOrders {
list(first: $first, after: $after) {
edges {
node {
id
poNumber
status
totals {
total {
amount
currency
}
}
createdAt
}
cursor
}
pageInfo {
hasNextPage
endCursor
}
approxTotalCount
}
}
}Fetching the next page
- Request the first page with
first(and noafter). - Read
pageInfo.hasNextPage. If it'sfalse, you're done. - Otherwise, request the next page with the same
firstandafterset topageInfo.endCursor. - Repeat until
hasNextPageisfalse.
// Page 1
{ "first": 50 }
// Page 2 — pass the previous page's endCursor
{ "first": 50, "after": "eyJpZCI6Li4ufQ==" }Pick a sensible page size
Page sizes in the low hundreds are a good balance. Very large pages are slower and heavier to transfer; tiny pages mean more round trips.
Exporting everything? Pick an order that never changes (see above) and don't pass a search term. Other orders are fine for browsing, but a record whose sort value changes while you page can be repeated or skipped.
Treat cursors as opaque — don't parse or construct them. Only ever pass back a
cursor the API returned. approxTotalCount is an estimate for display, not an
exact count to loop on; rely on hasNextPage to know when to stop.