On this page
What you need first
This lesson uses ideas from these earlier lessons. Read them first if any idea is new to you.
01Why interviewers ask this
In the API step (lesson 2), interviewers ask: "What does the client get back when something fails?" And: "A shop has 10 lakh (1,000,000) orders. How does the app get page 2?"
A weak answer returns every order in one call, with errors as free text. A strong answer returns small pages, and errors in one fixed shape that a program can read. In our own guide, SDE-1 can use offset paging. SDE-2 and above should say why a cursor is better.
02The idea in plain words
Think about a library desk. A good librarian says why a book is missing. For a long shelf, the librarian hands you 20 books and a bookmark.
Error shape. The status code (lesson 9) gives the family. The body gives the detail. Use the same body for every error, so the client needs one piece of code to read it. A public standard is RFC 9457, "problem details", sent as application/problem+json. It has these members:
type: the kind of problem, written as a URL. It never changes for that kind of problem.title: a short summary for people.status: the status code again.detail: the explanation for this one case.instance: a name for this one occurrence.
You may add members, such as request_id, which helps support find the call. Programs should check type, not title.
Paging. A page is a small part of a long list. Paging means one page per call, which protects the server and the phone. Cap the page size with a limit, for example 100. Both styles below put inputs after the ? in the URL. These are query parameters.
- Offset paging:
GET /orders?limit=20&offset=40means "skip 40 rows, then return 20". It is simple, and the client can jump to any page. It has two problems. The database must still work through the skipped rows. And if rows are added or removed between two calls, the pages shift: you see an item twice, or you miss one. - Cursor paging:
GET /orders?limit=20&cursor=abc. A cursor is a token that marks the last item the client saw. The server returns the 20 items after it, and anext_cursor. At the end of the list it isnull. Inside the token is usually the last row'sid, so the database can jump straight to it. This works because the ids are kept in sorted order. Sort by a unique value, such asid, or items with equal values can switch places between calls. The client must not read the token. Cursor paging costs you the jump to page 50, and a total count is extra work.
OpenAPI. An OpenAPI file is a text file (YAML or JSON) that describes your API: paths, methods, inputs, answers and error bodies. Tools turn it into a documentation page, make client code and check the server. You write it in the API step, before the code. Part of a file:
paths:
/orders:
get:
parameters:
- {name: limit, in: query, schema: {type: integer, maximum: 100}}
- {name: cursor, in: query, schema: {type: string}}
responses:
"200": {description: One page of orders}
"400": {description: Bad input (problem details)}Stuck on something? The AI tutor has read this lesson. Ask it with a free account
03Pause and predict
Pause. Guess first.
A list shows newest orders first. A user reads page 1, items 1 to 20. Then 5 new orders arrive. The app asks for
offset=20. Which old items does it get twice?
Write your guess before you read on. The answer is in the worked example.
04Worked example
Assumption: the shop has 1,000,000 orders. The page size is 20.
Work for the last page. Offset paging asks for offset=999980. The database works through 999,980 + 20 = 1,000,000 rows. Cursor paging works through 20 rows. That is 1,000,000 / 20 = 50,000 times more rows. This counts rows, not time.
Pages that shift. Page 1 held items 1 to 20. Every old item has moved down 5 places, so old items 16 to 20 are at positions 21 to 25. The call offset=20 returns positions 21 to 40, which are old items 16 to 35. So old items 16 to 20 appear twice: 5 repeats. This answers the predict box. A cursor "after old item 20" returns old items 21 to 40: no repeat.
An error. The app sends limit=500, but the maximum is 100. The server returns 400 and this body:
{
"type": "https://api.example.com/errors/limit-too-large",
"title": "limit is too large",
"status": 400,
"detail": "limit must be 100 or less. You sent 500.",
"request_id": "r-81f2"
}05Trade-offs
- Cursor paging solves repeated or missing items and slow deep pages. It costs page jumps and an easy total. I would use offset for a small, fixed list.
- One error shape solves guessing. It costs agreement across all teams. I would add fields only when needed.
06Common misconceptions
"Offset paging is fine, because databases are fast." The database still works through every skipped row, and shifting rows cause repeats.
"A cursor is just the last id, so the client may build it." The server may change what is inside the token. A client that builds its own cursor then breaks.
"The message text is enough for the client." Text changes and may be translated. A program reads type.
07How to say it in the interview
Say it like this
I would page the order list with a cursor and a limit of 100, so deep pages stay fast and no item repeats. Errors use one problem-details body with a fixed type, and I would write the API in an OpenAPI file.
08Check your understanding
- Name the two paging styles. Give one problem of each.
- A table has 200,000 rows and the page size is 50. For the last page, how many rows does each style work through? How many times more?
- Your app shows the same order at the end of one page and the start of the next. Why does it happen, and what do you change?
09Answers
Show the answers
- Offset: the database works through skipped rows, and pages shift. Cursor: no jump to a page number, and no easy total.
- Offset: 199,950 + 50 = 200,000 rows. Cursor: 50 rows. 200,000 / 50 = 4,000 times more.
- New rows pushed items down between the calls, and offset counts from the top. Use cursor paging with a unique sort order.
10Sources
- The row counts and order numbers are assumptions.
- Links checked 20 Oct 2026.
- Problem details: RFC 9457, https://www.rfc-editor.org/rfc/rfc9457.html (opens in a new tab).
- Skipped rows and unique order: PostgreSQL manual, "LIMIT and OFFSET", https://www.postgresql.org/docs/current/queries-limit.html (opens in a new tab).
- OpenAPI: https://spec.openapis.org/oas/latest.html (opens in a new tab).
Try it now, no sign-up
- Play Level 1: one server and 100 users. Free, no sign-up.
- Read today’s Drill: one real situation, answered in about 7 minutes.
More practice, in the app (free account)
Related lessons and practice
Ask the AI tutor about this lesson
The tutor has read this lesson. Ask in your own words, any time. Free account, 20 questions a day.
Reading never needs an account. The account is for the tutor, the quiz, your notes and your progress.