API pagination is the technique of splitting large datasets into smaller, manageable chunks called pages, returned sequentially across multiple API requests. It prevents servers from being overwhelmed and keeps client applications fast and responsive.
When an API endpoint could return thousands or millions of records, returning all of them in a single response is impractical and dangerous. Pagination breaks the result set into discrete pages, each containing a fixed number of items. The client requests one page at a time, optionally moving forward or backward through the full dataset. This mirrors how traditional search engines or e-commerce sites display results across multiple numbered pages.
Without pagination, a single API call could transmit gigabytes of data, exhaust server memory, and time out the client connection. Limiting response size reduces latency, lowers bandwidth costs, and protects backend databases from runaway queries. It also enables the server to apply query optimizations like index-range scans rather than full-table reads. For clients, smaller payloads mean faster rendering and a better user experience.
Offset-based pagination uses a numeric skip value and a limit — for example, `?offset=40&limit=20` — and is the simplest approach but degrades in performance on large offsets. Cursor-based (keyset) pagination passes an opaque pointer to the last-seen record, such as `?after=eyJpZCI6MTAwfQ`, making each query equally fast regardless of depth. Page-number pagination (`?page=3&per_page=25`) is a user-friendly variant of offset pagination common in REST APIs. Time-based pagination uses a timestamp anchor and is popular in event streams and social-media feeds.
The server encodes the last record's unique, indexed field — typically an ID or timestamp — into a cursor, often Base64-encoded. On each subsequent request the client sends this cursor, and the server executes a `WHERE id > :cursor ORDER BY id LIMIT n` query, which hits an index efficiently. The response includes both the data page and a `next_cursor` value for the following request. When `next_cursor` is null or absent, the client knows it has reached the end of the dataset.
Offset pagination has a well-known consistency problem: if records are inserted or deleted between requests, items can be duplicated or skipped across pages. For example, deleting a row while a client is on page 2 shifts all subsequent rows, causing the client to miss one record on page 3. This makes offset pagination unsuitable for frequently mutating datasets. Cursor-based pagination largely avoids this issue because it anchors to a stable, indexed value rather than a positional offset.
Always enforce a maximum page size server-side so clients cannot request unlimited records by passing a very large limit. Include metadata such as `total_count`, `has_next_page`, and cursor values in every response to help clients navigate without extra requests. Use cursor-based pagination for any dataset that grows beyond tens of thousands of rows or that mutates frequently. Document your pagination contract clearly in your API spec, including what happens when a cursor expires or a requested page is out of range.
© RM Full Stack & AI Engineer · All guides · Roadmaps · Open the app