What Is API Pagination? 7 Things Every Developer Should Know (With Examples)

November 3, 2026 · Web Development

Ask an API for every order a store ever took, and two things go wrong at once: the server has to assemble a giant response, and your client has to hold it, parse it, and render it. What is API pagination? It is the agreement that stops that from happening: the API returns the list in small, bounded pages, and the client walks from page to page until the API says there is nothing left. This guide explains the three pieces of every pagination design, the two main strategies, and the mistakes that lose or duplicate records.

Illustration of a long row of documents split into page groups, with a client window receiving only the first group
One list, many pages: the client receives a bounded chunk per request and asks for the next one when it is ready.

1. The problem pagination solves: one list is too big for one response

A list endpoint looks harmless with ten records and becomes a liability with a hundred thousand. The server reads, serializes, and sends all of them; the client downloads, parses, and stores all of them; one slow query or one flaky connection wastes the whole transfer. Pagination bounds that work: each request covers a fixed slice, so a failure costs one page, not the entire collection, and the client can show the first page while the rest is still loading.

That is also why pagination belongs on every collection endpoint of a REST API, even before the collection grows. Adding it later is a breaking change for clients that assumed one response held everything, while adding it early costs a few parameters. Design the contract first — page size, position, and completion signal — and the collection can grow without redesigning the endpoint.

2. The anatomy: page size, position, and a completion signal

Every pagination design, whatever its style, has the same three pieces. The page size says how many items one response carries, usually through a parameter like limit or per_page. The position says where this page starts, through a page number, an offset, or a cursor. And the completion signal says whether another page exists, so the client knows when to stop walking.

Position and size both travel as query string parameters on an ordinary GET request — for example, GET /orders?limit=20&page=3. The response then carries the items plus metadata describing the page: the current page, the page size, sometimes a total count, and a pointer to the next page. Read the metadata, not the item count, to decide whether to continue: a page that returns fewer items than the page size is not always the last page, and an empty page is a valid answer, not an error.

3. Offset and page-number pagination: simple, jumpable, and fragile on live data

Offset pagination is the easiest to explain: skip N items, take the next M. Page 3 at 20 items per page skips 40 and returns items 41 to 60. Clients can jump straight to any page, which makes it the natural choice for admin tables and search results where users click page numbers. GitHub's REST API works this way, with page and per_page parameters — and a documented cap: per_page defaults to 30 and never exceeds 100, even if you ask for more.

The fragility appears when the list changes between page fetches. Insert one record near the front and every later offset shifts by one: the item that was last on page 1 appears again first on page 2, and the item that should have been last on page 2 is never shown at all. Deep pages carry a second cost on the server side, because skipping a hundred thousand rows still means the database walks past them — the same index trade-offs that decide whether a query is fast at all. Offsets are fine for stable, browsable collections; they are the wrong tool for a feed that updates while you read it.

Diagram of a numbered list with a highlighted window of items that shifts when a new item is inserted at the top
Offset pagination counts from the start, so an insert near the front shifts every later page and repeats one item.

4. Cursor and keyset pagination: stable on live data, sequential by nature

Cursor pagination replaces counting with anchoring. Instead of "skip 40", the client says "give me what comes after the item I last saw", passing that item's identifier — the cursor — back to the API. Because the anchor is a record, not a position, inserts before it do not move it: the next page starts exactly where the last one ended, with no repeats and no gaps.

Stripe's list endpoints are the canonical example: they accept limit, starting_after, and ending_before, where the cursor is an existing object ID, and each response carries a has_more flag that says plainly whether another page exists. The limit ranges from 1 to 100 and defaults to 10. The trade-off is that cursors are sequential — you cannot ask for page 5 before page 4 has answered, because page 5's cursor only exists inside page 4's response — and they are usually opaque, so clients must pass them back unchanged rather than constructing or decoding them.

Diagram of a chain of items connected by arrows, with one item glowing as the cursor marker for the next group
Cursor pagination anchors to the last item seen, so the next page starts exactly where the previous one ended.

5. How clients find the next page: the Link header

Some APIs hand the client the next page's address directly, in a Link response header. GitHub does this: a paginated response carries links labelled first, prev, next, and last, each a ready-made URL with the right query parameters already filled in. The client follows rel="next" until that link disappears — and when a response fits on one page, the header is omitted entirely, which is itself the completion signal.

Following the header beats building URLs by hand, because the API owns its parameter names. A client that hard-codes ?page=2 breaks the day the API renames the parameter or switches to cursors; a client that follows the next link keeps working. The rule generalizes: treat the server's navigation metadata as the contract, and treat your own URL arithmetic as a shortcut that will eventually go stale.

6. A verified example: walking every page without losing a record

The difference between the two strategies is easier to trust once you can run it. This Python sketch implements both over the same ten-item list, then inserts a new item at the front between page fetches, exactly as a live list would change:

items = [{"id": i, "name": f"item-{i}"} for i in range(1, 11)]

def offset_page(data, page, size):
    start = (page - 1) * size
    return data[start:start + size]

def cursor_page(data, after_id, size):
    rest = [x for x in data if x["id"] > after_id] if after_id else data
    return rest[:size]

Run locally, the behaviour matches the model. Before the insert, both strategies agree: page 1 is items 1 to 3, page 2 is items 4 to 6. After inserting item 0 at the front, offset page 2 returns items 3, 4, 5 — item 3 repeats, and item 6 quietly drops out of the walk. Cursor page 2 after item 3 returns items 4, 5, 6 unchanged. And a full cursor walk, advancing the cursor to each page's last id and stopping when a page comes back short, collects all ten items in order, with no repeats and no gaps.

Diagram of groups of items with arrows walking from group to group until a final group marked with a stop flag
The full walk: advance the cursor to each page's last item, and stop when the API's completion signal says there is nothing left.

7. When pagination helps — and when it hurts

Pagination helps when a collection is large, grows, or is consumed gradually — infinite scroll, exports, and sync jobs are the classic cases. It hurts when it is applied where it is not needed: a list that is always tiny, like a user's handful of notification settings, is simpler and cheaper as one response, and paginating it only adds round trips.

Two interactions deserve care. First, a paginated walk still sends many requests, so it must respect the API's rate limits and back off on a 429 response rather than hammering through them. Second, do not poll pages for fresh events when the API offers push delivery: a webhook that notifies you of each new event the moment it happens beats re-walking a paginated list on a timer, in latency, in wasted requests, and in missed updates between polls.

Common mistakes to avoid

  • Assuming one response holds everything. A collection that fits today will not fit forever; code that ignores pagination silently drops records once the API starts paging.
  • Stopping on a short page instead of the completion signal. A page can legitimately come back short; only the API's has_more flag, next link, or next token says the list is finished.
  • Building page URLs by hand. Follow the Link header or the returned cursor; hard-coded parameter names break when the API changes them.
  • Walking offsets over a changing list. Inserts and deletes shift every later offset; use cursors for anything that updates while you read it.
  • Decoding or inventing cursor values. Cursors are opaque; pass them back exactly as received, and never guess the next one.
  • Ignoring rate limits during a full walk. Thousands of pages mean thousands of requests; respect 429 responses and Retry-After headers.

Pagination is a small contract with a large effect: bounded pages, an honest position, and a clear signal for when to stop. Choose offsets for stable, browsable collections, cursors for feeds that change while you read them, and always let the API's own navigation metadata drive the walk. References: GitHub's Using pagination in the REST API and Stripe's Pagination reference.

Frequently asked questions

What is API pagination in simple terms?
API pagination is how an API returns a large list in smaller chunks called pages, instead of all at once. The client asks for one page at a time — for example, 20 items per request — and follows the API's instructions to get the next page, until there are no more pages left.
What is the difference between offset and cursor pagination?
Offset pagination counts from the start: page 2 with 20 items per page skips the first 20 and returns items 21 to 40. Cursor pagination anchors to the last item you saw: you pass that item's identifier back, and the API returns what comes after it. Offsets are simple and let you jump to any page; cursors stay stable when the list changes between requests.
How do I know when I have fetched the last page?
Use the API's own completion signal, never a guess. That signal may be a missing next link in the Link header, a has_more flag set to false, a next_page token that is absent, or a page that returns fewer items than the page size. Stop when the signal says stop, and treat an empty page as a valid result, not as proof the list is finished.
Why do APIs limit page size?
A page size limit protects both sides. A huge response costs the server memory and time, costs the client bandwidth and parsing time, and is more likely to time out. GitHub caps per_page at 100 and Stripe caps limit at 100 for the same reason: bounded pages keep every single request cheap and predictable.
Should I use page numbers or cursors for a public API?
Use cursors for feeds that change — timelines, activity logs, search results that update — because they survive inserts and deletes between page fetches. Use page numbers for stable, browsable collections where users jump around, like an admin table over data that rarely changes. If you are unsure, cursors are the safer default for anything a client walks end to end.
Is pagination the same as rate limiting?
No, though they interact. Pagination controls how much data one response carries; rate limiting controls how many requests a client may make in a time window. Fetching a large collection page by page still sends many requests, so a paginated walk must respect the API's rate limits and back off on a 429 response.
Can I fetch all pages in parallel to go faster?
Only with offset or page-number pagination, where every page address is known in advance — and even then, only within the API's rate limits. Cursor pagination is inherently sequential: each page's cursor comes from the previous response, so you cannot ask for page 5 before page 4 has answered.
When should I not paginate at all?
Do not paginate a list that is always tiny, like a user's handful of settings — one response is simpler and cheaper. And do not make clients poll pages for fresh events when the API offers push delivery instead: a webhook that notifies you of each new event beats re-walking pages on a timer.

Related articles