Pagination and enumeration
How limit/offset/sort work across the API, and how to walk the full company or asset universe
Several /v1/ endpoints return more rows than fit in one response. This
guide covers the one pagination convention they all share, how sorting
interacts with paging, and how to enumerate the entire company or
mining-asset universe with an empty filter list.
Which endpoints paginate
| Endpoint | limit range (default) | Notes |
|---|---|---|
POST /v1/companies/search/ | 1–100 (25) | Also accepts sort. |
POST /v1/mining-assets/search/ | 1–100 (25) | Also accepts sort. |
POST /v1/publications/search/ | 1–50 (20) | Ordering via its own order param (newest, oldest, relevance). |
POST /v1/people/search/ | 1–25 (10) | |
GET /v1/mining-assets/{id}/drill-results/ | 1–100 (25) | Query parameters rather than a JSON body. |
The other dataset sub-resources — production, reserves,
economic assessments, the two ownership endpoints, and the commodity
registry — take no limit/offset and return every row in one response:
per-entity row counts there are bounded, so there is nothing to page. See
Dataset sub-resources.
The paged response shape
Every paginated response carries the same four fields alongside results:
{
"results": [ ... ],
"count": 1240,
"truncated": true,
"limit": 100,
"offset": 0
}count— total matches across all pages.truncated—truewhile more rows remain (count > offset + len(results)). Advanceoffsetbylimitand repeat the request until it comes backfalse.limit/offset— the window actually used, echoing your request after clamping.
Clamping, not rejection
An out-of-range or unparseable limit/offset is clamped silently,
never rejected: limit is clamped into the endpoint's range (junk falls
back to the default), offset to ≥ 0. This is uniform across every
paginated endpoint — a page window is treated as caller arithmetic, not
defective data. Invalid data still fails closed: an unknown asset id on
/drill-results/ is a 400 invalid_argument.
Sorting
The company and mining-asset searches accept an optional sort — a column
key from the resource's curated vocabulary, prefixed with - for
descending:
{
"filters": [],
"sort": "-market_cap_in_usd",
"limit": 100,
"offset": 0
}Sortable keys are marked "sortable": true in
GET /v1/filters/{resource}/ and in the
filter & column reference. A key outside
that set is a 400 unsupported_sort_field — validated fail-closed, like
filters and columns.
Two properties worth relying on:
- Ordering is deterministic. Every sort gets an internal primary-key tiebreaker appended, so rows with equal sort values don't shuffle between pages.
- Always send an explicit
sortwhen you paginate. Without one, rows come back in the endpoint's own declared order — for both company and mining-asset search that is descending gold production, not name — and a result set ordered by a value that moves is not stable across pages while rows are being updated underneath your sweep.
Enumerating the universe
Sending an empty (or omitted) filters list to the company or mining-asset
search enumerates — it returns the endpoint's whole searchable
population, paged like any other search. That population is already scoped
before any filter applies:
| Endpoint | Bare-enumeration scope | Excluded |
|---|---|---|
POST /v1/companies/search/ | Active companies, every industry | Deactivated companies |
POST /v1/mining-assets/search/ | Active, verified mining assets | Assets that failed verification (not existing, not unique, or no core data) |
Industry is not part of that scope. A bare enumeration returns active
companies across every industry; narrow it with an explicit filter, so
{"field": "industry", "operator": "has_any", "value": ["mining"]} returns
the mining slice and ["steel"] returns steel companies.
Changed: company enumeration previously applied a mining filter whenever a
request named no industry, so a bare enumeration returned only mining
companies. It no longer does. If your mirror relies on that narrowing, add the
explicit industry filter above — otherwise it will start receiving companies
from other industries.
A full enumeration is the backbone of an ingestion mirror — it is how you detect entities that have left the universe, which watermark sweeps can never show you. See Incremental sync for that recipe.
Go deeper
- Incremental sync — using the watermark columns plus periodic enumeration to keep a local mirror current.
- Filters — the curated vocabulary the
sortkeys come from.