Mobile API Best Practices · Designing APIs for Mobile Clients

Lists, Pagination & Infinite Scroll

Cursor pagination, stable ordering and refreshing lists without duplicates.

Infinite-scroll feeds need cursor (keyset) pagination: offsets skip or duplicate items when new rows arrive at the top. Return an opaque next_cursor, a stable sort (e.g. created_at DESC, id DESC) and a page size cap. For pull-to-refresh, request items newer than the first item's cursor (before/after cursors). Include has_more so clients stop requesting.

Pagination for mobile lists

Criterion Offset/limit Cursor (keyset) Page token (opaque)
New items inserted at top Duplicates/skips Stable Stable
Jump to page N Yes No No
DB cost at depth Grows (OFFSET scans) Constant (index seek) Constant
Bidirectional (refresh) Awkward before/after cursors prev/next tokens
Best for Admin tables Feeds, timelines Public APIs (hides implementation)
{
  "data": [{"id": "p_981", "title": "…"}, {"id": "p_980", "title": "…"}],
  "page": {
    "next_cursor": "eyJjIjoiMjAyNi0xMC0xMVQwODo0MjoxMFoiLCJpIjoicF85ODAifQ",
    "prev_cursor": "eyJjIjoiMjAyNi0xMC0xMVQwOToxMjowMVoiLCJpIjoicF85ODEifQ",
    "has_more": true
  }
}

Common pitfalls

  • Using offsets for a live feed.
  • Non-unique sort keys (ties cause duplicates).
  • Unbounded page sizes from the client.

Key points

  • Keyset cursors for feeds
  • Unique, stable sort order
  • before/after cursors for refresh

Further reading: Use The Index, Luke: No Offset

Practice questions

Why does offset pagination produce duplicates in a live feed?

Answer

When new items are inserted at the top, every existing item shifts down; requesting the next offset returns items the user has already seen.

How do you implement pull-to-refresh with cursors?

Answer

Ask for items after the newest item the client has (an 'after/prev' cursor) and prepend them; if too many arrived, reset the list.

Open in the app to mark lessons complete, take notes and track your progress.