Prasoon AI
ServicesInsights
Let’s talk
GOOD IDEAS DESERVE GREAT ENGINEERING.Explore the possibilities.

WHAT I BUILD

AI systems

Agents, private knowledge, and production AI.

SaaS platforms

Scalable software, from first release to growth.

HOW WE WORK

Services

Engineering expertise for your next challenge.

Industries

Solutions grounded in your business context.

IDEAS & PERSPECTIVES

Insights

Practical thinking on AI and architecture.

About my approach

Bridging research and production.

Available for select projectsDiscuss your project
All insights/API Engineering

API Engineering / Practical engineering

Cursor Pagination Under Concurrent Writes: A Consistency Contract

Design stable ordering, signed cursors, matching indexes, and explicit snapshot semantics for APIs that paginate while records are being created and changed.

P.
Prasoon ThakurAI systems architect
September 20, 20264 min read
THE ENGINEERING SERIESAPI Engineering
IMMUTABLE SORT / TIME + IDA stable next page.
12:04:00 · record 204Returned
12:04:00 · record 203Boundary
Next cursor Continue below record 203 →
API EngineeringIdeas, connected to implementation.
In this article6 sectionsContents +
  1. 01Specify the traversal the client is buying
  2. 02Order by an immutable tuple
  3. 03Bind the cursor to the query
  4. 04Understand what concurrent writes can still change
  5. 05Give exports their own architecture
  6. 06Test boundaries that ordinary fixtures miss

Specify the traversal the client is buying

An activity feed, an administration table, and a financial export may all display fifty rows at a time. They do not need the same consistency contract. A feed can tolerate new items appearing above the current position. An export may need a complete, reproducible set with no duplicates or omissions.

Before choosing a cursor format, decide whether the client is browsing a changing collection, traversing records below a boundary, or reading a fixed snapshot. Write this into the API contract. Pagination bugs often begin when a live feed is implicitly treated as an audit export.

PostgreSQL requires a predictable ordering for meaningful limited result sets and notes that skipped offset rows still have to be computed. Large offsets can therefore become expensive even when the page size is small. PostgreSQL LIMIT and OFFSET

Order by an immutable tuple

For a descending activity feed, use a non-null creation timestamp plus a unique immutable ID as the tie-breaker. Timestamp alone is insufficient because many records can share it. The continuation query must use the same tuple and direction as the ordering.

System Source
SELECT id, created_at, event_type
FROM activity_events
WHERE workspace_id = $1
  AND (created_at, id) < ($2::timestamptz, $3::uuid)
ORDER BY created_at DESC, id DESC
LIMIT 51;

CREATE INDEX activity_feed_idx
ON activity_events (workspace_id, created_at DESC, id DESC);

This example fetches one extra row to determine whether a next page exists. The next cursor comes from the last row actually returned, not the extra row. Use a separate first-page query without the continuation predicate.

The index is a starting point, not a universal guarantee. Additional filters, skewed workspace sizes, and selective predicates can change the plan. Inspect representative execution plans and row counts before declaring the endpoint fast.

Bind the cursor to the query

A cursor should encode a format version, ordering values, direction, and a normalized query fingerprint. The fingerprint can include workspace, filters, sort order, and relevant snapshot identity. Reject reuse with a different query rather than returning a plausible but incorrect page.

Sign the cursor to detect tampering. Encryption is a separate decision if its contents reveal sensitive values; base64 is neither signing nor encryption. Bound decoded size, validate types, enforce supported versions, and avoid placing raw SQL fragments inside the token.

Permissions must be checked on every page. If access changes between requests, the API should remove unauthorized records even if that means an earlier estimate of total results is no longer accurate.

Understand what concurrent writes can still change

With immutable descending keys, new records above the current boundary do not shift the next page the way offsets do. But edits to sort keys can move records across the boundary. Deletes remove records. Updates to other fields change what the client sees.

PostgreSQL Read Committed uses a new snapshot for each statement. Repeatable Read can retain a transaction snapshot, but separate HTTP requests do not automatically share a transaction. Holding database transactions open while users browse introduces its own operational costs. PostgreSQL transaction isolation

An upper timestamp boundary is also not a complete snapshot. A late commit can insert a record with an earlier timestamp after the traversal began. Do not advertise fixed-snapshot semantics merely because the cursor carries a start time.

Give exports their own architecture

For reproducible exports, create a job with an authorized scope and a defined source snapshot. Materialize the result or its identifiers in a controlled transaction, then stream pages from that stable result. Recheck download authorization and expire the artifact.

Use casePractical contractAdditional mechanism
Live activity feedContinue below the last seen immutable keyKeyset cursor
Search sorted by changing relevanceBest-effort continuationSearch-engine snapshot or explicit caveat
Administrative exportFixed result membershipMaterialized export job
Compliance evidence packageReproducible content and provenanceVersioned artifact and source manifest

Materializing identifiers alone fixes membership, not field values. If the export must reproduce exact values, store those values or retain a source version that can reconstruct them.

Test boundaries that ordinary fixtures miss

Create many rows sharing one timestamp. Delete the final row of the previous page. Insert a row between page requests. Change a permitted filter while reusing the cursor. Attempt another workspace's cursor and a token with an unsupported version.

Verify that traversal matches the documented contract, query plans stay bounded at deep positions, and malformed cursors produce a controlled client error. Measure scanned rows as well as response time; a warm cache can disguise a query that will collapse at production scale.

Frequently asked questions

Does keyset pagination provide a consistent snapshot?

No. It provides an efficient continuation boundary over an ordering. Separate requests can still observe different database states unless the API implements an explicit snapshot or materialized export.

Can the cursor replace authorization checks?

No. Verify the current caller and permissions on every request. A valid cursor describes position and query context, not an enduring grant of access.

About the author

Prasoon Thakur

Prasoon is an AI systems architect focused on reliable agents, retrieval, LLM operations, and scalable SaaS platforms. His work connects model behavior to the controls production teams need: evaluation, observability, security, and cost discipline.

GitHubUpwork profile

Need a reliable production system?

Turn the patterns in this guide into a scoped system design, delivery plan, and measurable reliability target.

Start a strategy session

Related insights

Transaction Design

Inventory Reservations: Preventing Overselling Under Concurrency

4 min read
Database Engineering

Live PostgreSQL Migrations: Expand, Backfill, Verify, Contract

5 min read

Active Now • 24/7 Availability

Engaging with teams
from Silicon Valley to Singapore.

I operate as a high-availability resource. To maintain secure collaboration, all global engagements are managed via Upwork.

Discuss your project Direct collaboration through Upwork.

Global / Remote

24/7 Timezone Agnostic

Syncing with USA, Europe, UAE & Singapore

Secure Engagement

Top Rated Expert on Upwork

Prasoon AI

Thoughtful architecture.
Software built for the real world.

Based online. Working worldwide.

Explore

AI systemsSaaS platformsServicesIndustries

Discover

Engineering insightsMy approach

Connect

Upwork GitHub Open to project inquiries

© 2026 Prasoon Thakur

Independent thinking. Dependable engineering.Back to top ↑