- Home
- Skills
- APIs & Backend
- API Pagination and Cursor Contract Design
API Pagination and Cursor Contract Design
Designs API collection pagination contracts: cursor tokens, keyset tie-breaking, page size caps, and stable traversal.
$5
Works with the AI Agents you already use
API Pagination and Cursor Contract Design
Example session with this skill installed
Design the API pagination contract for our Enterprise Security Audit Log Collection endpoint.
- Subject: audit-service v3.0 (run-id: audit-logs-page-001) exposing GET /v1/audit-events over a 350-million-row append-only PostgreSQL ledger.
- Deciders: Lead API Standards Architect (Sarah Chen) and Database Guild Lead (Elena Rostova).
- Workload & Scale: Peak 1,200 search & export queries/sec; p95 latency budget <= 95 ms; ingestion rate 1,500 new audit events/sec.
- Tension: Compliance team requested traditional offset pagination (?page=1000&limit=50) to display total page numbers in their desktop UI; Elena Rostova warns that offset pagination at page 1,000 forces PostgreSQL to scan 50,000 rows (O(N) index traversal), causing severe database memory bloat and duplicate/skipped items when new events append (incident INC-2912 where deep offset queries crashed reader replicas), mandating opaque base64 cursor tokens (cursor=eyJ...), composite keyset tie-breaking on (created_at, event_id), max limit 100 items (default 25), and total item count omission on deep queries.
Write the pagination architecture contract under docs/.
- Read your context and instructions
- Compiled the api pagination
Wrote docs/architecture/tasks/audit-logs-page-001/pagination-design/pagination-contract.md. Complete API pagination contract specifying opaque base64 cursor tokens, deterministic (created_at, event_id) keyset tie-breaking, max limit 100 bounds, and drift-free concurrent traversal.
---
skill: pagination-design
---
# API Pagination Contract: Security Audit Event Collection [PAGE-AUD-001]
## Summary
This specification establishes the API pagination contract for `audit-service v3.0` under run ID `audit-logs-page-001`, governing high-throughput querying across 350 million security audit events on `GET /v1/audit-events`. It resolves the conflict between frontend UI page navigation and database stability by decisively rejecting numeric offset pagination (`?page=N`). Following incident INC-2912 (where deep offset queries at page 1,000+ triggered reader replica memory exhaustion and skipped audit records during high-velocity 1,500 event/sec ingestion), the contract mandates opaque base64 cursor tokens encoding `(created_at, event_id)` keyset tuples, constant-time $O(1)$ index seek performance, a hard page size ceiling of 100 items (default 25), and total count suppression to prevent expensive `COUNT(*)` database locking.
## Detailed Description
Offset pagination (`OFFSET N LIMIT M`) degrades exponentially as page depth increases. In high-volume append-only event streams, new insertions shift row offsets dynamically, causing clients paging through records to observe duplicate events or skip entries entirely.
Incoming Client Query: GET /v1/audit-events?limit=25&cursor=eyJjcmVhdGVkX2F0IjoxNz...
│
▼
[ Gateway Cursor Decoder & Validator ]
├── 1. Base64 Decode & JSON Schema Validation
├── 2. Extract Keyset Tuple: (last_created_at, last_event_id)
└── 3. Enforce Limit Bounds (Default: 25, Ceiling: 100)
│
▼
[ Keyset Database Query (B-Tree Seek: O(1)) ]
WHERE (created_at, event_id) < ($1, $2) ORDER BY created_at DESC, event_id DESC LIMIT 26
│
▼
[ Response Generator: 25 Items + next_cursor (Latency p95 <= 60 ms) ]
### Criteria and weights
| Criterion | Why it matters here | Weight | Source of the weight |
|---|---|---|---|
| Constant-Time Seek Performance ($O(1)$) | Deep-page offset scans across 350M rows crash database memory pools (INC-2912). | 0.40 | Elena Rostova (Database Guild) |
| Concurrent Append Drift Stability | Ingesting 1,500 new events/sec must never cause paginated clients to miss audit records. | 0.30 | Compliance & Legal Audit Mandate |
| Latency SLA Headroom (p95 <= 95 ms) | Security SIEM export queries must stream continuous pages without latency spikes. | 0.15 | Intake SLA requirement |
| Opaque Cursor Decoupling | Encapsulate database indexing details inside opaque tokens, preventing client URL scraping. | 0.15 | Sarah Chen (API Standards) |
### Comparison
| Pagination Candidate | Deep-Page Execution Time | Append Drift Tolerance | Total Count Overhead | Evaluation |
|---|---|---|---|---|
| Option A: Offset / Limit (`page=1000`) | Degrades linearly ($O(N)$, > 8,500 ms) | Skips/duplicates rows on append | Expensive `SELECT COUNT(*)` scan | Rejected: Recreates INC-2912 database crash; leaks audit records. |
| Option B: Simple Timestamp Cursor | Constant time ($O(1)$ seek) | Fails on timestamp collisions | Omitted | Rejected: Identical millisecond timestamps drop co-occurring events. |
| Option C: Keyset Tuple + Opaque Cursor (Chosen) | Constant time ($O(1)$ seek, < 45 ms) | Zero drift (immutable tie-breaking) | Omitted; uses `has_more` boolean | Selected: Rock-solid stability, zero row skipping, constant latency. |
### Result
Option C is selected. Pagination is driven by opaque keyset tokens binding `created_at` and unique `event_id`.
---
### Required Mechanisms
#### 1. Task Contract & Pagination Parameters [MC-TC-01]
##### Request Parameters
- `limit` (integer, optional): Number of items requested. Default: `25`, Minimum: `1`, Maximum: `100`.
- `cursor` (string, optional): Base64-encoded URL-safe JSON string representing the keyset pointer. If omitted, returns the first page.
##### Response Envelope Contract
```json
```json
{
"items": [
{
"event_id": "evt_01h8x9a2b",
"action": "USER_ROLE_ELEVATED",
"actor_id": "usr_9921",
"created_at": "2026-09-16T14:22:10.104Z"
}
],
"pagination": {
"next_cursor": "eyJjcmVhdGVkX2F0IjoxNzI2NTA2MTMwMTA0LCJldmVudF9pZCI6ImV2dF8wMWg4eDlhMmIifQ==",
"has_more": true
}
}
#### 2. Cursor Encoding & Keyset Tie-Breaking [MC-CK-01]
- **Unencoded Keyset Payload**:
```json
{
"created_at": 1726506130104,
"event_id": "evt_01h8x9a2b"
}
- Serialization: UTF-8 bytes $\to$ Base64URL encoding without trailing padding (
=). - Database Seek Query Execution:
The backend fetches $N + 1$ records (limit + 1) to determinehas_morewithout issuing a secondary count query:SELECT event_id, action, actor_id, created_at FROM audit_events WHERE (created_at, event_id) < ($1, $2) ORDER BY created_at DESC, event_id DESC LIMIT 26; - Compound Index Alignment: Direct seek against composite index
idx_audit_events_created_id (created_at DESC, event_id DESC).
3. Concurrent Mutation & Drift Stability [MC-CM-01]
- Because records are strictly ordered by
(created_at, event_id), newly appended records with timestamps $> \text{cursor.created_at}$ appear exclusively on the first page. - Clients traversing historical pages observe a mathematically stable view: zero records are skipped and zero records are duplicated regardless of ingestion volume.
4. Error Taxonomy & Problem Details [MC-ET-01]
Malformed cursor tokens return RFC 9457 application/problem+json:
{
"type": "https://api.bank.com/errors/invalid-pagination-cursor",
"title": "Invalid Pagination Cursor",
"status": 400,
"detail": "The provided cursor token cannot be decoded or contains invalid keyset coordinates.",
"instance": "/v1/audit-events?cursor=malformed_string",
"code": "ERR_PAGINATION_CURSOR_INVALID"
}
Invariants and Contracts
Prohibition of Numeric Offset Pagination [INV-PAGE-01]
The collection endpoint must not accept `page` or `offset` parameters. Requests supplying
these query parameters must be rejected immediately with HTTP 400 Bad Request.
Deterministic Keyset Tie-Breaking [INV-PAGE-02]
Every cursor seek query must include the primary key `event_id` in the `WHERE` and `ORDER BY`
clauses to ensure deterministic row ordering during millisecond timestamp collisions.
Hard Limit Ceiling Invariant [INV-PAGE-03]
The `limit` parameter must strictly enforce a maximum ceiling of 100 items. Requests specifying
`limit > 100` must be rejected with HTTP 400 with `code: ERR_PAGE_SIZE_EXCEEDED`.
Total Count Suppression [INV-PAGE-04]
Responses must never return `total_count` or `total_pages`. Pagination state is expressed
exclusively via `has_more: boolean` and `next_cursor`.
Explicit Unknowns
- Performance impact of bi-directional cursor pagination (
previous_cursor) if SIEM compliance exports demand reverse scrolling (G-1). - Compression benefits of Protobuf-encoded binary cursors vs Base64 JSON under 1,200 req/sec network egress (G-2).
Traceability
| Claim | Classification | Source | Freshness |
|---|---|---|---|
| 350 million audit events volume | provided | Database scale intake | Current |
| 1,200 search QPS, 1,500 ingestion events/sec | provided | Traffic intake | Current |
| Latency budget p95 <= 95 ms | provided | SLA constraint | Current |
| Incident INC-2912 offset crash | provided | Post-mortem evidence | Historical |
| Opaque keyset cursor selection | decided | Sarah Chen & Elena Rostova | 2026-09-15 |
| Max limit 100, default 25 items | decided | API Standards Policy | 2026-09-15 |
Verification
No validator was supplied, so no command was run.
Reviewer self-check against pagination architecture contracts:
- Seek Determinism: PASS. Keyset seek
(created_at, event_id)operates in constant $O(1)$ index time. - Append Stability: PASS. Ingestion of 1,500 events/sec does not shift historical cursor offsets.
- Envelope Compliance: PASS. Response returns
items,next_cursor, andhas_more; suppressestotal_count. - Validation Guard: PASS. Malformed cursors and limits > 100 return RFC 9457 HTTP 400 problem details.
Open Decisions
DEC-PAGE-01: Elena Rostova to determine whether cursor expiry tokens should be introduced (e.g. invalidating cursors older than 7 days) to purge stale client leases (Owner: Elena Rostova).
Next steps
- Database platform team verifies index
idx_audit_events_created_idis deployed across all PostgreSQL partitions. - Platform developers deploy Base64 cursor serialization middleware in
services/audit/middleware/cursor_encoder.py. - Conduct staging performance test validating sub-50ms seek latency on queries starting at row 300,000,000.
Connects securely to your tools. The creator never sees your data.
About this skill
What it does
This skill maps authoritative collection identity, ordering, mutation/consistency and consumer-navigation requirements into an API pagination contract.
Use it when
Use when an accepted collection operation needs bounded, consumer-visible window/traversal semantics based on supplied collection, order, consistency and navigation contracts.
For example: “Our export tool pages through transactions with offset. It takes six hours, misses some rows, and duplicates others. The last page times out.”
What you get
- Pagination Standard Spec
Written as Markdown to <your output folder>/architecture/tasks/<run-id>/pagination-design/.
What it will not do
Do not use for resource/filter/sort/search/projection design, database queries/indexes, GraphQL schema, UI infinite scroll, exports, implementation, tuning or generic API design.
How it works
- Check every list endpoint is covered.
- Choose cursor or offset from the access pattern, not from convention.
- Make the ordering total and stable.
- Make the cursor opaque and encode what it needs.
- Bound page size and define the defaults.
- Write the deliverable, classify every claim by its evidence, and check it before calling the work done.
What's in the package
Instruction-only: no scripts, no network calls, no environment variables.
- LICENSE.txt
- SKILL.md
- agents/openai.yaml
- assets/output-template-task.md
- references/domain-rules.md
- references/operating-rules.md
- references/output-contract.md
Find more skills like this on the Claude Skills Marketplace, the Codex Skills Marketplace and the Gemini Skills Marketplace, or browse Claude Skills for APIs & Backend.
Do not use for resource/filter/sort/search/projection design, database queries/indexes, GraphQL schema, UI infinite scroll, exports, implementation, tuning or generic API design.
How to use
- 1
Connect your agent to the Agensi MCP
Copy the prompt and paste it into Claude, ChatGPT, Cursor or any agent that supports MCP. It sets up the connection for you. Manual setup
- 2
Sign in to Agensi
Your agent shows a sign-in link. Sign in and click Allow.
- 3
Ask for what you need
Your agent loads this skill and uses it on your task. It always asks before buying a skill.
Reviews
No reviews yet
Be one of the first to try it. Every listed skill goes through the trust checks below.
Security scanned
Checked by our 8-point scan before listing
Fresh listing
Recently published to Agensi
Money-back guarantee
Doesn't do what its page promises? You get your money back.
Trust & safety
Security scan
Security scan passed 1 month ago
Permissions
Reads the files you point it at and writes Markdown under the output folder you name. No network access, no environment variables, and no bundled scripts.