Using _count and _offset Without Losing Consistency

Editorial illustration in vaporwave-pastel style depicting cursor-based next-link pagination vs fragile _offset scheme showing overlap and skip on writes

FHIR search pagination is a topic where the spec is careful and the servers vary. _count requests a page size, servers may cap it, offset-style pagination via _offset produces inconsistent results when the underlying set changes between pages, and cursor-based next links are the safer default. Getting pagination right is where a lot of "we processed row 47 twice" incidents live. The site's FHIR search query auditor flags likely-inconsistent pagination shapes. For the wider FHIR framing, more on healthcare interoperability has more.

What `_count` Actually Does

_count=50 requests up to fifty resources in the current page. The server may return fewer. It may also cap _count at its own maximum, silently returning fifty when the caller asked for five thousand. The response Bundle carries a link array with next, previous, first, last entries when more pages exist.

The client that reads Bundle.entry.length and assumes it equals the requested count is going to have a bad time on servers with hard caps.

Use the `next` Link, Not `_offset`

The safe pattern for pagination is to follow the Bundle.link with relation = "next". That link is server-provided and often carries an opaque cursor that survives underlying set changes between pages. Callers that assemble their own _offset values re-implement pagination worse than the server.

_offset is a spec-defined parameter, but it has real downsides:

  • Insertions between pages shift entries and produce duplicates on the next page
  • Deletions between pages skip entries
  • Sorting is not guaranteed to be stable across pages when using offset
  • Servers may not support arbitrary _offset values efficiently

For stable pagination, follow the next link. Reserve _offset for one-shot exports where the caller knows the underlying set is quiescent.

Sort Order Matters More Than It Looks

Pagination without a stable sort produces different results for the same query on different pages. The default sort on many servers is implementation-defined and may not be stable. For the mechanic, sort order that survives resource updates covers the stable-sort pattern.

Adding _sort=_id gives a stable order that survives updates. Without it, pagination is a coin flip on repeatability.

The `_total` Question

?_total=accurate requests an exact count. ?_total=estimate requests an estimate. ?_total=none skips the count. Many servers default to none for performance. Callers expecting a Bundle.total value on every response are going to be surprised on servers that opt into none.

Request _total=accurate when the count matters. Skip it when it does not — computing exact totals is expensive on large result sets.

`_count=0` Is a Trick

Setting _count=0 returns an empty result set with just the metadata. Combined with _total=accurate, it gives a fast way to check "how many resources match this query" without transferring the data. That is a useful shape for dashboard queries that just need a count.

Auditor Behavior

The auditor at /dev-tools/audit-fhir-query/ flags pagination shapes that combine _offset with unstable sorts, missing _total where the caller likely needs one, and requested counts that exceed common server caps. For the wider rule set, linter rules to catch the ten most common search mistakes covers them.

The Short Version

Follow the next link, not _offset. Sort by _id for stable pagination. Request _total explicitly when the count matters. Cap the expected page size at the server's cap, not the client's ambition. For the wider quiet-wrong category, the FHIR searches that look correct but are wrong is the entry.

Vaporwave-pastel diagram of a paginated FHIR search Bundle with a next-link cursor pattern on the left contrasted with an _offset-based pattern on the right showing overlap and skip when the set changes, on a soft dusk-purple background

Sources