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. Absent 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 fields are ignored silently (so ?limit= and ?sort= don't trigger errors), 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.
The supported filters and operators per endpoint are documented on the relevant entry in the API reference.
Sorting
Code
Conventions:
+(or no prefix) is ascending;-is descending.- Multiple fields are comma-separated.
- Each endpoint declares an allowlist; sorting on a field outside the list returns
400 VALIDATION. - The default order is
created_at DESC, id DESCand is stable for cursor-based paging.
When you supply a custom sort, the cursor key must remain compatible with it. Endpoints currently restrict custom sorts to the cursor-key fields (typically
created_at); attempting to sort on something else surfaces a validation error rather than silently breaking pagination.
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 · Trialing 3 · Past due 1") without a second round trip.
Code
Code
Counts respect any active filters - if you ask for ?status=active&counts=billing_model, the billing_model 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