Pagination sounds like performance work.
Page size. Cursor token. Offset. Sort order. Total count. Next page. Done.
That framing is too small. In many products, pagination is where data exposure gets operational. It decides how much data an actor can enumerate, how quickly they can move through it, whether hidden records leak through counts, whether deleted records stay discoverable, and whether an integration can quietly turn a list API into an export pipeline.
Nobody calls it exfiltration when an approved API client asks for page after page. That is exactly why the design deserves more attention.
The list endpoint is not neutral
A single record endpoint usually gets more scrutiny. Can this user read this object? Does the tenant match? Is the role allowed? Is the record in the right state?
List endpoints are sneakier. They look less dangerous because each response is partial. Ten records. Fifty records. Maybe a hundred. The risk hides in repetition.
A user who cannot download a full dataset from the UI may still be able to walk the whole dataset through the API. A partner integration may only need active cases, but receive archived cases because the filter is optional. A support role may need records assigned to their queue, but get a paginated view of every customer in the tenant. A reporting token may need summary data, but receive row level detail because the list endpoint was easier to reuse.
The mistake is treating pagination as a delivery mechanism after access has already been solved. Pagination is part of the access decision.
What people usually get wrong
The first failure is authorizing the collection instead of each item.
The system checks that the user can access the endpoint, then returns whatever the query produces. That works only if the query is perfectly scoped every time. It usually is not. Filters change. Admin views get reused. Background jobs call the same route. New states get added. Someone adds an include archived option because a customer asked for it.
This is the same old API problem with a larger blast radius. A hidden button is not an authorization boundary, and neither is a UI page that only shows the first fifty records. The backend decision still has to be real. ZDS covered that pattern in API Authorization Fails When the Frontend Becomes the Control.
The second failure is trusting page size as the control.
A maximum page size of one hundred sounds sensible. It prevents one enormous response. It does not prevent ten thousand requests. It does not answer whether the caller should be able to enumerate the collection at all. It does not protect sensitive fields inside each row. It does not stop a cursor from becoming a harvesting tool.
The third failure is leaking information through metadata.
Total counts, empty pages, predictable sort orders, error messages, and filter behavior can disclose more than teams expect. A user may not see the records, but they may learn that records exist. In some systems, existence is sensitive: investigations, employees, invoices, claims, legal matters, security alerts, medical workflows, executive accounts, terminated users.
If the count is sensitive, do not ship the count casually.
Cursor tokens need design, not decoration
Cursor pagination is often better than offset pagination for consistency and performance. Fine. But cursor tokens can still become security objects.
A cursor should not be a readable bundle of query state that exposes internal IDs, tenant IDs, timestamps, or sorting assumptions. It should not be portable across users, roles, tenants, or materially different filters. It should not keep working after the authority behind the original query changes.
The question is not whether the cursor is clever. The question is what authority it carries.
If a user starts a paginated query while they have access, then loses access, what happens to the next cursor request? If a role changes between page one and page two, does the system recheck permissions? If the underlying dataset changes, does the cursor expose records that no longer belong in the result? If the cursor is copied into another session, does it still work?
Those are design choices. Leaving them accidental is not engineering speed. It is hidden policy.
Rate limits help, but they are not the policy
Rate limits matter here. A list endpoint that allows aggressive pagination can create scraping risk, privacy risk, and operational load. But throttling is not the same as authorization.
A rate limit can slow a bad outcome. It cannot decide whether the outcome is allowed. It cannot tell the difference between a legitimate tenant admin exporting records for a lawful purpose and a broad token walking customer data because no one scoped it properly.
That is why rate limits need to reflect business rules, not just infrastructure tolerance. ZDS covered that in Rate Limits Are Business Rules in Disguise.
For pagination, the useful question is: what is the maximum amount of data this actor should be able to retrieve for this purpose, through this path, in this time window?
That answer may vary by role, tenant, plan, customer agreement, jurisdiction, integration type, and data class. Annoying, yes. Also closer to reality.
When pagination becomes export
There is a point where repeated list calls become functionally identical to download.
Product teams often treat those as separate features. The UI export button gets reviewed. The API list endpoint gets documented. The integration team gets a token. Everyone moves on.
But from a data movement perspective, the difference may be cosmetic. If an actor can retrieve the full dataset through pagination, the API is an export path. It needs purpose, ownership, limits, logging, and evidence. The same logic applies from The Download Button Is a Data Transfer Control: familiar mechanics do not make data movement harmless.
That does not mean every list endpoint needs heavyweight governance. It means teams should name when a list endpoint crosses into bulk access.
Small lists are normal. Operational queues are normal. Search results are normal. Partner integrations are normal. The control question is whether the design matches the sensitivity and volume of the data being exposed.
What to ask in the review
A decent pagination review does not need theater. It needs specific answers.
Who is allowed to enumerate this collection, not just read one record?
Is authorization applied before pagination, after pagination, or both? If rows are filtered after the query, can page behavior reveal excluded records?
Are tenant, role, ownership, record state, and data classification enforced server side on every page request?
Can the caller change filters, sort fields, include archived data, request related objects, or expand sensitive fields?
Does the response include total counts or metadata that may reveal sensitive existence?
Are cursor tokens opaque, scoped, time bound where needed, and revalidated against current authority?
At what point does repeated pagination become bulk export, and who owns that decision?
What evidence exists that the control works? Not a diagram. Tests, logs, sample decisions, denied requests, and reviewable configuration.
If those answers are unclear, the risk is not theoretical. The product has an undocumented data access model.
The tradeoff worth naming
Teams want reusable APIs. They should. Reuse reduces duplicate logic and makes products easier to operate.
Security wants scoped access. It should. Sensitive data should not become broadly enumerable because the cleanest endpoint was the most convenient one to reuse.
The compromise is not to make every list endpoint painful. The compromise is to classify list endpoints by what they can expose: operational view, search result, admin collection, integration feed, bulk export, regulated dataset, cross tenant sensitive path.
Then design pagination rules that match the class.
Some endpoints can have generous page sizes. Some need field minimization. Some need no total count. Some need stricter rate limits. Some need separate export approval. Some should not exist as broad list endpoints at all.
That is not drama. That is architecture doing its job.
If your team is trying to turn these decisions into a clearer operating model, Zero Drama Security services can help with pragmatic security architecture and governance design.
The simple standard
Treat pagination as part of the data access contract.
If an API can walk a dataset, the design must say who may walk it, how far they may go, which fields they may see, what changes when authority changes, and what evidence proves the boundary held.
Anything less is just a polished way to leak data one page at a time.
