Pagination, filtering & sorting
Every list endpoint in the API uses cursor-based keyset pagination - there are no page numbers, no offset parameter, and the order is stable across pages even while data is being written.
Pagination
Two query parameters:
| Param | Default | Max | Description |
|---|---|---|---|
limit | 50 | 200 | How many records to return on this page. |
cursor | (none) | - | Opaque base64 string from the previous response's pagination.cursor. Omit for the first page. |
Code
Every list response carries a pagination block:
Code
| Field | Notes |
|---|---|
cursor | Opaque - pass it back as ?cursor=… for the next page. null when has_more is false. Cursor strings are not interchangeable between endpoints. |
has_more | true if at least one more page exists. Always present. |
total_count | Optional. Endpoints that can compute it cheaply include it; high-volume endpoints (e.g. transactions) omit it. Don't rely on it being present. |
Cursors encode the keyset position (
created_at,id) rather than an offset, so concurrent inserts don't shift later pages. The flip side: you can't jump to "page 5" - only "the page after this one".
Filtering
Every filter follows the form ?<field>[.<operator>]=<value>.
Code
| Operator | Meaning |
|---|---|
eq (default) | field = value |
ne | field <> value |
gt, gte, lt, lte | Numeric / timestamp comparisons |
in | field IN (...) - value is comma-separated |
contains | Case-insensitive substring (ILIKE %value%) - string fields only |
Each endpoint declares which fields it accepts and which operators are allowed per field. Unknown filter fields are ignored silently, but using an unsupported operator on a known field returns 400 VALIDATION listing every offending parameter so you can fix them in one round trip. ?sort= is not a filter and is not ignored - an unrecognised value is rejected, see Sorting.
The supported filters and operators per endpoint are documented on the relevant entry in the API reference.
Sorting
Code
Conventions:
- One field, with an optional
:asc/:descsuffix. No prefix form, and no multi-field sorts. - Each endpoint declares a closed set of orderings it can serve. A value outside that set returns
400 VALIDATIONnaming the accepted ones - it is never ignored, because silently returning a different order gives you no way to tell. - The default is
created_at:descwhere the list has one, and it is what you get when?sort=is absent.
Not every column you can see is sortable, and that is deliberate rather than an oversight. A keyset ordering has to be something the database can compare, so an endpoint will refuse to sort on:
- a value computed after the query (a subscription's MRR, a drift figure, a per-row aggregate);
- a name that is translated per request, which SQL would order by its untranslated base value;
- a nullable column, whose NULL rows a row-value comparison would drop from the second page onward;
- an amount stored without its currency beside it, where
EUR 100andUSD 100would compare equal.
Where a column is excluded the header is not clickable, rather than offering a control that reorders only the rows already on screen.
A cursor is bound to the ordering that issued it. Changing
?sort=mid-walk means starting a fresh first page - replaying the old cursor under the new sort returns400 VALIDATIONrather than silently comparing a boundary drawn from a different order.
Counts (group-by aggregations)
Some list endpoints support ?counts=field1,field2 to attach per-field group-by aggregations to the response - useful for powering filter chips ("Active 12 · Suspended 3 · Churned 1") without a second round trip.
Code
Code
Counts respect any active filters - if you ask for ?status=active&counts=customer_type, the customer_type counts only include active customers. Endpoints that don't support counts ignore the parameter; check the API reference for which fields each endpoint exposes.
Related
- Errors - the validation error shape returned for bad filter operators or sort fields
- Search - free-text search across entities; uses the same cursor format but a different read path
- API reference - per-endpoint filter / sort allowlists and
countssupport