Skip to main content
List endpoints support two pagination styles, pickable per request:
  • Cursor (keyset) pagination — round-trip page.next_cursor / page.prev_cursor via ?cursor=. O(page size) regardless of depth. Recommended for browsing UIs and forward/backward walking.
  • Offset pagination — classic ?limit=&offset=. Supports random access — jump straight to any page within your plan’s depth cap (see Per-plan caps below). Cost grows with the offset value.
Both produce the same response envelope; only the request shape differs. Cursor wins when both are sent (?cursor=X&offset=N ignores the offset).
Need the older offset-based shape? Send API-Version: 2026-05-22 (or any earlier date) and the response will use the legacy { limit, offset, total } envelope. Old clients keep working unchanged.

Parameters

Limits

Page size and pagination depth are separate axes: page size bounds a single response, depth (offset + limit) bounds how far you can page. Each endpoint has a hard page-size ceiling: Per-plan caps. Requests without an API key get a lower page size and a bounded pagination depth, by plan: Page size is clamped, not rejected — an over-limit request returns the reduced size and sets page.capped to true (see the response shape below). The per-plan page size applies to the core list endpoints only (companies, entities, investors, founders, transactions, valuations, jobs, news); geo, taxonomy, and aggregate endpoints are not per-plan page-capped. Depth applies to every list endpoint, on both the offset and cursor styles. A request beyond the cap returns 400 with error code PAGINATION_DEPTH_EXCEEDED — the page is not silently truncated.

Response shape

  • page.limit — applied page size (carried by the cursor on subsequent pages)
  • page.offset — the server’s view of where this page sits in the result set (0 for the first page, limit for the second, etc.). Use it for “showing rows X–Y of Z” display
  • page.next_cursor — token to fetch the next page, or null when this is the last page
  • page.prev_cursor — token to fetch the previous page, or null when this is the first page
  • page.total — total matching records; present only when include_total=true
  • page.cappedtrue when the requested limit was reduced to the per-plan page-size cap. Present on capped list endpoints for requests without an API key
  • page.tier — the access tier applied to the request: anonymous, free, or premium. Present alongside capped
  • page.ecosystem — slug of the active ecosystem when the request is scoped to one (resolved from the request origin), or null when global. Present alongside capped

Walking forward (cursor)

Random access (offset)

Offset cost grows with the offset value — for deep pages, prefer cursor walking. Offset is fine for small jumps (a few hundred rows) and for one-shot fetches. Random access is bounded: an offset + limit beyond your plan’s depth cap returns 400 PAGINATION_DEPTH_EXCEEDED (see Per-plan caps above).

Walking back

prev_cursor is null exactly when you’re on the first page. For a “Page N of M” UI, read page.offset and page.limit straight from the response — the cursor token already carries the current offset, so the server reports it back without the client doing any arithmetic.

Cursor rules

  • Opaque — treat the cursor as a black box. Do not decode, modify, or construct it manually.
  • Carries limit/offset only — the cursor token embeds page size and offset, so ?limit= and ?offset= are ignored when ?cursor= is sent. To change page size, restart from the first page with the new ?limit=.
  • Echo sort and filter on every request — the cursor does NOT contain the sort or filter expression. The server compares the cursor’s fingerprints against the resolved sort and filter of the current request, so the rule is “the request’s resolved sort/filter must match what minted the cursor.” In practice that means resending the same ?sort= and ?filter= you used on page 1. Technically, if a cursor was minted under default sort and empty filter, omitting both is fine because the defaults still match — but the safe pattern is to always echo them so client code doesn’t break the moment a non-default sort or filter is in play. Mismatch returns a 400.
  • Bound to sort — changing ?sort= mid-paging returns 400 Invalid cursor for this sort order; restart from the first page.
  • Bound to filter — changing ?filter= mid-paging returns 400 Cursor is bound to a different filter; restart from the first page. Currency (?currency=) is not bound — switching currencies mid-paging is safe.
  • No random jumps — cursors are sequential: no cursor jumps straight to an arbitrary page (e.g. “page 47”). Walk next_cursor / prev_cursor, or restart from the first page. (Offset pagination does allow random access, but both styles are bounded by the per-plan depth cap above.)

Including the total count

By default page.total is omitted — cursor pages stay cheap. Pass include_total=true to include it:
For large datasets, skip the total count when you don’t need it — omitting it avoids a full-table count query.
In-memory list endpoints (/platform/ecosystems, /platform/teams, /platform/api-keys) keep the legacy { limit, offset, total } shape even on the new version — their datasets are small and bounded, so cursor pagination offers no benefit.