← Back to blogs
Digital Engineering

API Pagination in 2026 — Offset Pagination vs Cursor Pagination and When to Use Each

JUN 22, 2026•6 MIN READ•Project Supply Team
API Pagination in 2026 — Offset Pagination vs Cursor Pagination and When to Use Each

In the landscape of modern API development, pagination is not merely a technical implementation detail; it is a fundamental architectural decision that directly influences system performance, user experience, and scalability. As we navigate the requirements of 2026, where data volumes are larger and user expectations for "instant" interfaces are higher, selecting the right pagination strategy—Offset vs. Cursor—is more critical than ever.

1. Defining the Core Concepts

Offset-Based Pagination

Offset-based pagination is the classic "page-number" approach. It relies on the ability of the database to skip a specific number of rows before returning the requested subset.

  • How it works: The client requests a specific page (e.g., page=3) and a size (e.g., limit=20). The backend calculates the offset as (page - 1) * limit and executes a query similar to SELECT * FROM items LIMIT 20 OFFSET 40.
  • The Intuition: It provides a direct, integer-based mapping to the dataset.

Cursor-Based (Keyset) Pagination

Cursor-based pagination (often called Keyset pagination) uses a pointer to a specific record in the dataset to fetch the next "page."

  • How it works: The client provides a cursor (a token, timestamp, or unique ID) and a limit. The query uses this cursor in the WHERE clause: SELECT * FROM items WHERE created_at < '2026-07-07T06:00:00' ORDER BY created_at DESC LIMIT 20.
  • The Intuition: It treats the dataset as a continuous stream or chain, where each result contains the information needed to request the next segment.

2. Technical Comparison Table

FeatureOffset PaginationCursor Pagination
PerformanceDegrades linearly with offset sizeConsistent/Stable performance
Random AccessYes (can jump to any page)No (sequential access only)
Data ConsistencyProne to duplicates/gaps if data changesHighly stable under data churn
ComplexityLow (simple to implement)Medium (requires indexed cursor field)
CachingExcellent (page URLs are predictable)Harder (cursor tokens are dynamic)
Ideal ForAdmin tables, static reportsSocial feeds, logs, infinite scrolls

3. Deep Dive: Why Offset Struggles at Scale

The primary Achilles' heel of offset-based pagination is the underlying database behavior. When you execute LIMIT 20 OFFSET 1000000, the database engine is forced to perform a full scan (or index scan) to count and discard one million rows before retrieving the twenty records you actually need.

In 2026, with datasets often containing millions or billions of rows, this overhead creates:

  1. High Latency: Response times become sluggish as the user traverses deeper into the dataset.
  2. Resource Exhaustion: Each request consumes significant CPU and I/O cycles on your database, potentially leading to cascading performance issues.

Furthermore, offset pagination suffers from the "Phantom Read" problem. Imagine a user is on page 1 of a blog feed. While they are reading, a new post is inserted at the top of the database. When the user clicks "Page 2," the post that was previously at the bottom of Page 1 is now at the top of Page 2, causing the user to see it twice. Conversely, if a row is deleted, they might skip an item entirely.

4. Deep Dive: Why Cursor Wins in Dynamic Systems

Cursor pagination solves the consistency problem by anchoring the query to a specific record rather than a row count. Because the WHERE clause can utilize an index (e.g., on created_at or id), the database engine can jump directly to the starting point of the requested data.

The Anatomy of an Efficient Cursor

To implement cursor pagination effectively, the "cursor" value should be:

  • Indexed: Must be a column (or tuple) that has an efficient database index.
  • Unique/Deterministic: If using a timestamp, you must include a unique secondary identifier (like id) in the sort order to break ties.

Recommended Query Pattern:

Instead of WHERE created_at < cursor, use a tuple comparison:

SQL

SELECT * FROM posts
WHERE (created_at, id) < (cursor_timestamp, cursor_id)
ORDER BY created_at DESC, id DESC
LIMIT 20;

This ensures that even if thousands of items share the exact same timestamp, the pagination remains precise and consistent.

5. When to Choose Which

Choosing Offset Pagination

You should choose offset-based pagination when the requirements necessitate random access or are constrained by simple URL structures.

  • Small, Static Datasets: If your table will never grow beyond a few thousand rows, the performance penalty of offset is negligible.
  • Admin Dashboards: When users explicitly need "Go to page 50" functionality.
  • SEO/Deep Linking: If you need to generate crawlable URLs for search engines (e.g., ?page=1, ?page=2), offset provides a predictable structure.
  • Aggregated Data: In scenarios where you need a Total Count of all items to display to the user (e.g., "Showing 1-20 of 5,432 items"), offset is the most straightforward path.

Choosing Cursor Pagination

Cursor-based pagination is the industry standard for modern, high-traffic, dynamic applications.

  • Infinite Scroll/Feeds: If you are building a feed (like Twitter/X, Instagram, or a log viewer), cursor is the only way to ensure the user never sees duplicates or skips items.
  • High-Write Concurrency: If your data is frequently being inserted, deleted, or updated, cursor ensures a stable user experience.
  • High Performance/Large Datasets: If you cannot afford the performance degradation of OFFSET at large numbers, cursor is non-negotiable.

6. Strategic Implementation for 2026

As you design your API for 2026, keep these design principles in mind:

Always Include Metadata

Regardless of the pagination type, never just return a raw array. Wrap your responses in a structure that provides context:

JSON

{
"data": [...],
"meta": {
"next_cursor": "eyJjcmVhdGVkX2F0IjoiMjAyNi0wNy0wNyIsImlkIjoxMDUwfQ==",
"has_more": true
}
}

For offset, include total_pages and current_page. For cursor, include next_cursor and a has_more boolean. This prevents the client from making unnecessary requests.

Handling Caching

One of the trade-offs of cursor pagination is that cursor tokens are often unique and "opaque," making them difficult to cache at the CDN level.

  • Offset: Highly cacheable. A URL products?page=2 is consistent.
  • Cursor: Requires a client-side approach or complex cache key management. If you must use cursors but need caching, consider "time-windowed" cursors that remain valid for a few minutes.

The Hybrid Approach

Sophisticated systems often employ a hybrid strategy. For example, a "Top 100" list might be cached and served via offset for SEO, while the "Load More" functionality below that list switches to a cursor-based endpoint to handle the long-tail of data efficiently.

7. The Future of Pagination (2026 and Beyond)

We are seeing a move away from traditional REST-based pagination toward GraphQL-style "Connections." The Relay Cursor Connections Specification is becoming a blueprint for many non-GraphQL APIs. This spec defines a standard way to represent cursors, edge data, and page information, promoting interoperability between different services and frontend frameworks.

Furthermore, as browser-side state management (like TanStack Query, SWR, or Apollo) becomes more advanced, the "client-side illusion" of pagination is improving. Modern frontend state managers can take a cursor-based stream and present it to the user as a set of numbered pages, bridging the gap between the server’s need for efficient cursor queries and the user’s desire for familiar numbered navigation.

Final Recommendation for Architects

  1. Default to Cursor: If you are unsure or if the resource is likely to grow, start with cursor-based pagination. It is significantly harder to refactor from offset to cursor once a system is in production than it is to build cursor-based from the start.
  2. Explicitly Request Offset: Only opt for offset pagination if you have a concrete requirement for "jump-to-page" or SEO indexing of specific pages.
  3. Optimize the Database: Ensure that whichever strategy you choose, the sorting column (ORDER BY) and the filtering column (the cursor field) are covered by a composite database index. This is the single most effective way to guarantee API performance in 2026.

Pagination is a tool to manage complexity. By choosing the right strategy early, you decouple your API’s scalability from your database’s growth, ensuring a performant, reliable experience for your users today and for years to come.

FAQs

Most teams launch with a basic admin/user system because it is easy, but this simple structure collapses under the weight of enterprise requirements like custom roles, row-level access, and team-based permissions. Because the authorization logic is often hardcoded directly into the application, adding these enterprise features requires a complete rewrite of the system rather than just updating a policy definition. Building an extensible architecture from day one, which separates policy from application logic, is the only way to avoid this inevitable "rebuild" phase.

RBAC (Role-Based Access Control) assigns permissions to roles, and then assigns users to those roles, which is great for general access but lacks the granularity needed for complex scenarios. ABAC (Attribute-Based Access Control) adds conditions to those permissions based on attributes of the user, the resource, or the environment—like "only edit between 9 AM and 5 PM" or "only edit documents in your own department." Most mature SaaS platforms eventually adopt a hybrid model that uses roles for standard access and attributes for granular, dynamic conditions.

Implementing Row-Level Security often involves adding scope-filtering logic to every database query, which can become slow if your database is large or your relationships are complex. The key is to enforce these filters at the data-access layer by automatically injecting the required scope into your queries—for example, by adding a "where organization_id = X" clause to every request. Using database-level features like PostgreSQL RLS or simply maintaining high-performance indexing on your scoping columns (like organization_id) is essential to keep your system fast as your data grows.

For a small or medium-sized product, building your own RBAC system is usually better because it keeps your infrastructure simple and prevents you from being locked into a third-party service. As your product becomes highly complex, or as enterprise requirements for compliance and audit-logging become overwhelming, external services (like Auth0 or specialized permission engines) can become valuable. Start with a clean, policy-based internal design, and only consider outsourcing if your authorization logic itself becomes a major, specialized engineering challenge.

Your audit log must capture who performed an action, what they did, when they did it, and—crucially—whether the action was successful or denied. This data should be stored in a write-only, tamper-proof system, as enterprise clients will want to export this for their own compliance reviews. Storing this in a dedicated service that tracks authorization decisions at the engine level allows you to provide this data to clients as a premium feature, adding significant value during the enterprise sales cycle.

Store roles as collections of permissions in your database, rather than hardcoding them into your application logic. This allows you to build an interface where customers can group permissions into their own custom roles, which your authorization engine then checks dynamically. By keeping the role-to-permission mapping in a clean, queryable database table, you avoid the need for complex logic and keep your code base stable regardless of how many roles your customers define.

Tags
Digital EngineeringArtificial Intelligence
LET'S WORK TOGETHER

Have a project in mind?Let's make it real.

Tell us what you're building. We'll bring the design, technology, and thinking to make it happen.

Fill up the following form to start a conversation with our team