What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Choose pagination when you design a collection endpoint, not after it ships. Adding it later can change behavior incompatibly, even when the new fields are technically additive. For most changing or large datasets, use an opaque continuation cursor with a stable sort; use offset or skip when clients genuinely need positional jumps and the backend can support deep offsets; use response links when discoverability and a uniform client experience matter most.
What API pagination solves
A collection response must have a bounded size. Pagination divides that collection into pages so clients can control transfer size, latency, memory use, and retry scope. It also gives a service room to evolve its storage without exposing every record at once.
Google’s AIP-158 says collection-returning RPCs should provide pagination from the outset because adding it later is a backwards-incompatible behavioral change. Apply the same principle to REST and other HTTP APIs: define pagination fields, ordering, limits, and end-of-results semantics in the first public contract.
Define the contract before choosing a pattern
Page-size rules
- Make
page_sizeorlimitoptional. A missing or zero value selects a documented default. - Clamp values above the service maximum to that maximum rather than failing, if that is your documented behavior.
- Reject negative values.
- Document that a response may contain fewer records than requested without proving that the collection has ended; filtering, server limits, or concurrent changes can all produce a short page.
Record the default and maximum beside the endpoint definition. For example: “limit defaults to 50, is capped at 200, and negative values return 400.” Do not silently invent a universal limit across APIs.
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Repair Windows errors before they cause bigger problemsFix Now →#1 Best Overall
Continuation and termination
Choose one unambiguous terminal signal. Under AIP-158, an empty next_page_token means there are no more results. In SCIM cursor pagination, RFC 9865 requires nextCursor to be omitted only on the final page. Document the exact rule in your schema and examples.
Ordering and consistency
Every page needs a deterministic order. Prefer a unique, indexed key as a tie-breaker (for example, created_at plus id). State whether the traversal is a snapshot, a best-effort view of changing data, or a cursor that encodes a read position. Without this, inserts and deletes can cause duplicates or omissions.
Opaque, context-bound tokens
AIP-158 requires page tokens to be opaque, URL-safe, and not user-parseable. A token should identify where to continue, not grant authorization. Recheck authorization on every request. Bind the token to the original filter, sort, tenant, and other relevant query context; reject or safely invalidate it if those inputs change. RFC 9865 likewise requires subsequent SCIM requests to preserve the original query parameters other than the cursor.
Token storage and expiry are API-specific. AIP-158 offers three days as a rule of thumb for internally stored tokens, not a universal lifetime. Return a documented error when a token expires and tell clients whether they should restart from the first page.
Rank #2
Compare the three common patterns
| Pattern | Request example | Strengths | Trade-offs |
|---|---|---|---|
| Offset/skip | ?limit=50&offset=100 |
Simple; supports jumping to an approximate position and familiar page-number interfaces. | Deep offsets may require more backend work, and inserts or deletes before the offset can shift records between pages. Actual cost depends on the storage engine and query plan. |
| Cursor/keyset | ?limit=50&cursor=opaque-token |
Good for sequential traversal of changing or large collections; continuation state can encode a stable key and snapshot. | Usually no random page-number jumps; requires a stable ordering, token lifecycle, and context validation. |
| Link-based | Link: <https://api.example/items?page=3>; rel="next" |
The server supplies complete continuation URLs, making clients discoverable and reducing parameter assumptions. | Links are endpoint-specific and must be parsed correctly; clients still need clear terminal behavior. |
Zalando’s REST pagination guideline recommends preferring cursors over offsets in many designs, but neither that guidance nor AIP-158 establishes a performance result for every database or workload. Choose from your access requirements and measured query plans, not slogans.
Offset pagination: implementation details
Request and response
GET /v1/orders?limit=25&offset=50
{
"items": [/* 25 orders */],
"limit": 25,
"offset": 50,
"has_more": true
}
Use offset when a user must jump to a position, when the collection is small or relatively static, or when the data store has an efficient strategy for the expected depth. Put a hard maximum on offset or offer an alternative for unbounded histories.
Handling changing rows
If records can be inserted or deleted while a client walks pages, offset is vulnerable to duplicates and gaps. A stable snapshot, a consistent transaction, or a documented “results may shift” policy is necessary. If correctness across a long traversal matters, keyset or cursor pagination is usually a better fit.
Cursor and keyset pagination
Design the cursor
A cursor can reference the last sort key, a server-side snapshot, or both. Serialize and authenticate it on the server, then encode it as an opaque URL-safe value. Do not expose a base64-encoded object that clients are expected to edit. Include enough context to detect a changed filter, tenant, direction, or sort.
Rank #3
GET /v1/events?limit=100&cursor=eyJ...opaque...
{
"items": [/* up to 100 events */],
"next_cursor": "eyJ...opaque..."
}
Return an empty token or omit the field according to the convention you publish. The client must use the server-provided value exactly; it should never calculate a cursor from an undocumented ID.
Forward and backward traversal
For forward keyset pagination, query rows greater than the cursor’s last key, using the same ordering and tie-breaker. For reverse traversal, define whether the API supports a separate previous cursor, an ending_before-style parameter, or only forward iteration. Stripe list methods are a vendor-specific example: they use object IDs with starting_after or ending_before and provide auto-pagination helpers in client libraries. Its documented list default is 10; its search API documents a limit from 1 through 100 with default 10, values that should be checked against the current Stripe reference.
Link-based pagination
GitHub’s REST API places navigation URLs in the HTTP Link response header. A client should parse relations such as rel="next" and rel="last" rather than reconstructing undocumented query parameters. The official GitHub guidance shows this approach, including endpoints where a response page is 30 items while many more issues remain.
Link: <https://api.example.com/items?page=2>; rel="next", <https://api.example.com/items?page=10>; rel="last"
Link headers work well when the server can express the complete continuation URL. Also include a machine-readable body field if your client ecosystem does not reliably expose headers, and state which source wins if they disagree.
Free tools Windows power users keep installed
One-click scans. No signup required.
Client traversal that does not lose data
- Send the first request with an explicit page size that is within the documented maximum.
- Process items idempotently. Store a durable checkpoint if a traversal may be interrupted.
- Read the server’s next token, cursor, or link. Preserve all original filters, sorting, authorization, and tenant context.
- Stop only on the documented terminal signal: an empty AIP-158 token, an omitted RFC 9865
nextCursor, or norel="next"link. - Retry transient failures with bounded exponential backoff. Reuse the same continuation value; do not advance it until the page has been committed successfully.
- Handle token expiry or invalidation by restarting according to the API’s documented recovery policy, and deduplicate by a stable resource ID if the collection can change.
Python example
import requests
url = "https://api.example.com/v1/items"
params = {"limit": 100}
while True:
response = requests.get(url, params=params, timeout=30)
response.raise_for_status()
page = response.json()
for item in page["items"]:
consume(item)
token = page.get("next_page_token")
if not token:
break
params["page_token"] = token
Node.js example
let pageToken;
do {
const query = new URLSearchParams({ limit: "100" });
if (pageToken) query.set("page_token", pageToken);
const res = await fetch(`https://api.example.com/v1/items?${query}`);
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
const page = await res.json();
for (const item of page.items) consume(item);
pageToken = page.next_page_token || "";
} while (pageToken);
cURL inspection
curl -i -G "https://api.example.com/v1/items"
-d limit=100
-H "Authorization: Bearer $TOKEN"
Performance, reliability, and cost decisions
- Index the order: a cursor on
created_at,idneeds an index that matches the filter and order. - Bound page sizes: larger pages reduce request overhead but increase latency, memory, and retry cost.
- Measure deep access: offset behavior varies by database, indexes, and query shape; benchmark your workload instead of claiming one pattern is universally faster.
- Protect the service: apply per-client rate limits, maximum traversal duration, and cancellation support.
- Make retries safe: GET traversal is normally idempotent, but downstream processing may not be. Use resource IDs or an idempotency store.
- Monitor: track page size, depth, token-invalid errors, empty-page frequency, latency percentiles, and abandoned traversals.
Common failures and fixes
Repeated or missing records
Cause: offset against a changing collection, or an unstable sort with ties. Fix: use a snapshot or cursor tied to a unique deterministic order, and deduplicate by ID when the contract permits changes.
400 for a page request
Cause: negative size, malformed token, changed filter, or unsupported parameter. Fix: validate against the published schema, preserve the original query, and restart only under the documented invalid-token policy.
Short page mistaken for completion
Cause: the client assumes fewer records than requested means “last page.” Fix: follow the explicit token, cursor, or link termination rule.
401 or 403 on a later page
Cause: authorization changed or a client treated a continuation token as permission. Fix: refresh credentials if appropriate and enforce authorization independently on every request.
Best Value
Expired cursor
Cause: server-side state or a signed token passed its lifetime. Fix: surface the API’s expiry error, restart from the beginning or a supported checkpoint, and avoid promising a lifetime the service does not guarantee.
Pagination is not search-engine page navigation
API continuation tokens are for clients traversing data. Web-page SEO has a separate requirement: Google Search Central explains that crawlers generally discover pages through URLs in anchor href attributes and generally do not click buttons or trigger user actions that load more content. For crawlable HTML, provide sequential, valid links and correct URL handling as described in Google’s pagination guidance. Do not assume an API cursor alone makes a “load more” interface indexable.
Or skip the browser setup
For developers who need screenshots of paginated pages rather than API records, ScreenshotNeo returns PNG, JPEG, WebP, or PDF from one request. It accepts the cookie or consent banner as a visitor, then removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each cleanup step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing result. Its MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients.
Example (see the ScreenshotNeo API documentation):
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
There is a free allowance of 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 shots, and every feature is available on every plan. Sign up for the free ScreenshotNeo plan.
PC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteFrequently Asked Questions
Should an API return total page counts?
Only when the count is useful and affordable to compute. A count can become stale while pages are being read; a continuation signal is the reliable control for traversal.
Can I let clients decode a cursor for debugging?
No. Keep production cursors opaque as required by AIP-158. Log server-side correlation data or provide a separate diagnostic facility instead.
Is a 204 response the right last page?
Usually not for a paginated collection. Keep the collection response shape stable and use the documented empty-token, omitted-cursor, or missing-link signal.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.
Do these 3 things before closing this tab:
1Fix the driver behind crashes, sound loss and screen glitches2Clear out junk files and repair common Windows errors3Scan for outdated or missing drivers - takes under a minute

