API Pagination Patterns Compared: Offset, Cursor, Keyset, and Token-Based
apipaginationbackenddatabasearchitecture

API Pagination Patterns Compared: Offset, Cursor, Keyset, and Token-Based

AAlex Rowan
2026-06-13
10 min read

A practical comparison of offset, cursor, keyset, and token-based pagination for designing scalable, stable APIs.

Pagination looks simple until an API has real traffic, changing data, and clients that need consistent results. This guide compares the four patterns developers most often consider—offset, cursor, keyset, and token-based pagination—so you can choose a design that fits your dataset, query patterns, and product constraints. Rather than treating pagination as a minor response detail, the article frames it as an architectural choice that affects database load, user experience, caching, sorting rules, and long-term maintainability.

Overview

If you are designing or revisiting an API, pagination is one of the earliest choices that can quietly shape performance and correctness for years. The wrong pattern may still work in development and even in production at small scale, but it often starts to fail in familiar ways: slow queries on large tables, duplicate items between pages, missing records after inserts, or fragile client logic tied too closely to server internals.

The four common approaches solve different problems:

  • Offset pagination uses page numbers or offsets such as ?page=3&limit=20 or ?offset=40&limit=20.
  • Cursor pagination returns an opaque pointer, often based on the last item seen, such as ?after=cursor_value.
  • Keyset pagination uses one or more ordered columns directly, often something like ?after_id=1050 or ?created_before=2026-01-01T00:00:00Z.
  • Token-based pagination returns a continuation token that encodes state the server wants the client to send back on the next request.

In practice, cursor and token-based pagination are often related, and some teams use the terms interchangeably. The most useful distinction is this: a cursor usually points to a position in an ordered result set, while a token may capture a broader request state, including filters, sort keys, shard position, or internal metadata. Keyset pagination is also closely related to cursors, but it is usually more explicit and tied directly to indexed columns.

There is no universal winner. Offset pagination is easy to understand and easy to document. Cursor and keyset approaches tend to scale better for sequential navigation through large, changing datasets. Token-based designs can support complex backends and distributed systems, but they introduce more abstraction and often more implementation complexity.

The practical question is not “Which pattern is best?” but “Which failure mode can this API afford?”

How to compare options

The best way to compare API pagination patterns is to evaluate them against the behavior your API actually needs, not against generic advice. A small admin dashboard and a high-volume event feed may both list records, but they have very different expectations.

Use these criteria when deciding.

1. Dataset size and query cost

If your queries scan deeper into a large table over time, offset pagination can become expensive because the database may still need to skip over many rows before returning the current page. Cursor and keyset approaches usually avoid this cost by starting from a known position in an index-friendly order.

2. Data volatility

If records are inserted or deleted frequently, offset pagination can produce shifting pages. A user may see duplicates or miss items entirely as rows move between page requests. Cursor, keyset, and token-based pagination are generally more stable when the result set changes between requests.

3. Need for random access

Offset pagination is strong when clients want to jump to page 10, page 20, or page 200. Cursor and keyset pagination are better for moving forward or backward step by step through a stream, but they are less natural for direct page jumps.

4. Sort flexibility

If your API supports many arbitrary sorts, pagination becomes more complicated. Keyset pagination works best when the sort order is stable, well-defined, and backed by indexes. If clients can sort by nearly any field, offset pagination is simpler to expose, though not always simpler to optimize.

5. Consistency requirements

Some APIs need “good enough” listing behavior. Others need predictable traversal where each page continues exactly from the last one. If clients use pagination for exports, audits, synchronization, or background jobs, consistency matters much more than convenience.

6. Client simplicity

Offset pagination is easy for frontend developers, API consumers, and API docs. Cursor and token-based systems ask clients to treat the next-page value as opaque and reuse it exactly as returned. That is still manageable, but it does change how SDKs and integrations are written.

7. Exposure of internal structure

If you do not want clients to know your ordering keys, token-based cursors can hide implementation details. Keyset pagination can be elegant and fast, but it may expose IDs or timestamps directly unless you wrap them in an opaque cursor.

As a rule of thumb:

  • Choose offset for simplicity and page-based navigation.
  • Choose cursor for large, changing lists and feed-like experiences.
  • Choose keyset when you have a stable sort and want highly efficient indexed queries.
  • Choose token-based when continuation state is more complex than a single sort position.

Feature-by-feature breakdown

This section compares the patterns directly across implementation and behavior.

Offset pagination

Typical API shape: GET /items?page=3&limit=20 or GET /items?offset=40&limit=20

How it works: The client asks for a fixed slice of the result set by page number or row offset.

Advantages:

  • Simple for clients and API documentation.
  • Works well for traditional table UIs with numbered pages.
  • Easy to combine with total counts in the response.
  • Natural for admin interfaces and back-office tools.

Tradeoffs:

  • Can get slower as offsets grow.
  • Less stable when data changes between requests.
  • Pages may shift after inserts or deletes.
  • Large offsets often reveal database inefficiency.

Best use: smaller datasets, internal tools, or interfaces where direct page jumps matter more than perfect continuity.

GET /users?page=2&limit=25

{
  "data": [/* 25 users */],
  "pagination": {
    "page": 2,
    "limit": 25,
    "total": 1240,
    "hasNext": true
  }
}

Cursor pagination

Typical API shape: GET /items?limit=20&after=eyJpZCI6MTA1MH0=

How it works: The server returns a cursor representing the last seen position. The client sends it back to continue.

Advantages:

  • Better performance for large sequential reads.
  • More stable than offset when records are added or removed.
  • Well suited for mobile apps, infinite scroll, and activity feeds.
  • Can be opaque, giving the server freedom to evolve internals.

Tradeoffs:

  • Harder to support direct page jumps.
  • Requires a well-defined sort order.
  • Can confuse clients if the cursor format is not documented clearly.
  • Backward navigation may need separate before/previous logic.

Best use: feed-like APIs, timelines, logs, notifications, message lists, and large collections that change frequently.

GET /events?limit=50

{
  "data": [/* 50 events */],
  "pagination": {
    "nextCursor": "opaque_cursor_here",
    "hasNext": true
  }
}

Keyset pagination

Typical API shape: GET /orders?limit=50&created_at_lt=2026-01-01T00:00:00Z or GET /orders?after_id=1050

How it works: The query uses a comparison on the ordered key, such as WHERE id > ? or WHERE (created_at, id) < (?, ?), usually paired with ORDER BY on indexed columns.

Advantages:

  • Very efficient when backed by the right index.
  • Stable for large, append-heavy datasets.
  • Often simpler at the database layer than generic offset pagination.
  • Excellent for deterministic traversal.

Tradeoffs:

  • Depends on stable, unique, ordered keys.
  • More difficult with arbitrary sorting and filtering combinations.
  • Composite keys are often necessary to avoid ties.
  • Exposes implementation details unless wrapped in an opaque cursor.

Best use: large relational datasets where sort order is predictable, such as by created_at plus id.

SELECT *
FROM orders
WHERE (created_at, id) < (?, ?)
ORDER BY created_at DESC, id DESC
LIMIT 50;

That composite comparison matters. If you paginate only by created_at and many rows share the same timestamp, you can get duplicates or skipped records. For SQL readability and team consistency, a clear formatting standard helps when these queries become more complex; see the SQL Formatter Guide: How to Write More Readable Queries and Team Standards.

Token-based pagination

Typical API shape: GET /search?limit=20&pageToken=abc123

How it works: The server returns a token that may contain the cursor, filters, snapshot details, partition state, or other continuation metadata. The client treats it as opaque.

Advantages:

  • Flexible for distributed systems and complex backends.
  • Can preserve server-side assumptions without exposing them.
  • Useful when pagination depends on more than a single sort key.
  • Allows evolution of continuation logic behind a stable API contract.

Tradeoffs:

  • Harder to inspect and debug manually.
  • Clients cannot derive meaning from the token, which is intentional but less transparent.
  • Token expiration and invalidation policies need careful design.
  • Implementation can become over-engineered if the use case is simple.

Best use: search APIs, aggregated results, distributed storage, and APIs where the server must control continuation semantics tightly.

{
  "data": [/* results */],
  "nextPageToken": "opaque_token_here"
}

Comparison summary

  • Simplest for clients: Offset
  • Best for large changing datasets: Cursor or keyset
  • Best for indexed deterministic traversal: Keyset
  • Best for complex backend continuation state: Token-based
  • Best for numbered page UIs: Offset
  • Best for infinite scroll: Cursor

One useful middle ground is to implement keyset queries internally but expose an opaque cursor externally. That gives you efficient queries without tightly coupling clients to database fields.

Best fit by scenario

If you are still deciding, map the pattern to the product behavior rather than to abstract performance goals.

Scenario: admin dashboard with sortable tables

Use offset pagination unless the dataset is extremely large or performance is already an issue. Admin users usually expect page numbers, total counts, and the ability to jump around. If you later hit scale problems, you can selectively introduce cursor-based endpoints for heavy views.

Scenario: social feed or activity timeline

Use cursor pagination. These interfaces are naturally sequential, data changes constantly, and users rarely care about page 37. The API should optimize for smooth “load more” behavior and stable continuation.

Scenario: transaction history or audit log

Use keyset pagination or an opaque cursor built on top of keyset logic. These records usually have a clear sort order and benefit from deterministic traversal. For audit-sensitive systems, stability matters more than page numbers.

Scenario: search results across multiple sources

Use token-based pagination. Search often combines ranking, filters, source-specific cursors, and backend state that should not leak into the public API. A continuation token gives the server room to manage this complexity.

Scenario: public developer API with many third-party consumers

Favor clarity over cleverness. If consumers are likely to test requests manually and build lightweight integrations, offset may still be a valid choice for smaller collections. But for endpoints expected to grow, an opaque cursor format is often safer long term because you can change implementation details without changing the contract.

Scenario: export jobs or background synchronization

Use keyset or token-based pagination. These workflows care about complete traversal, predictable ordering, and resilience to changing data. Offset is usually the weakest choice here.

No matter which option you choose, document these details explicitly:

  • The supported sort order or default order.
  • Whether pagination is forward-only or bi-directional.
  • Whether total counts are exact, approximate, or omitted.
  • How inserts and deletes affect continuity.
  • Whether cursors or tokens expire.
  • Maximum page size and any server-enforced limits.

If your API is authenticated, pagination design often intersects with caching, per-user visibility, and token handling. For related design decisions, the comparison in REST API Authentication Methods Compared: API Keys, OAuth, JWT, and Sessions can help frame how client state and API contracts work together.

When to revisit

A pagination strategy should not be treated as permanent. It deserves a review whenever the shape of the data, the client behavior, or the infrastructure changes.

Revisit your decision when:

  • Query latency rises as tables grow and deeper pages become common.
  • Clients report duplicates or skipped items during navigation.
  • You add new sorts or filters that no longer fit your original indexing strategy.
  • Your API moves from internal to public use and backward compatibility matters more.
  • You introduce infinite scroll or mobile clients that need smooth sequential loading.
  • You start aggregating data across services and simple row offsets no longer represent a real continuation point.
  • Policies or product expectations change, such as requiring stronger consistency for exports or compliance workflows.

A practical review checklist looks like this:

  1. Inspect your most expensive paginated queries.
  2. Check whether the current sort order is stable and indexed.
  3. Review real client behavior: page jumps, infinite scroll, exports, sync jobs.
  4. List all endpoints that expose total counts and decide whether they are still worth the cost.
  5. Test how inserts and deletes affect page continuity between sequential requests.
  6. Decide whether the public contract should expose keys directly or switch to opaque cursors.
  7. Version the change carefully if existing clients depend on page numbers.

If you are implementing this in Node.js, remember that cursor secrets, signing keys, or token settings should not be scattered through the codebase. A clear configuration approach makes these changes safer over time; see Node.js Environment Variables Guide: Validation, Defaults, and Secrets.

The most durable recommendation is simple: start with the simplest pattern that fits your real access pattern, but choose a contract that leaves room to evolve. For many teams, that means using offset only where numbered pages are genuinely valuable, and using opaque cursors for endpoints likely to grow. Pagination is not just about dividing results into pages. It is about preserving a reliable path through data as systems and datasets change.

Related Topics

#api#pagination#backend#database#architecture
A

Alex Rowan

Senior SEO Editor

Senior editor and content strategist. Writing about technology, design, and the future of digital media. Follow along for deep dives into the industry's moving parts.

2026-06-13T06:18:45.663Z