Q16 of 37 · API testing
How do you test pagination in an API?
Short answer
Short answer: Verify page boundaries (first, last, beyond-last), totals match, no records duplicated or skipped across pages, and pagination tokens or cursors are stable. Test with both small and large data sets — many bugs only show up at page 100+.
Detail
Pagination bugs are sneaky: small test datasets pass, prod fails on page 47.
Pagination styles to know:
- Offset / limit:
?offset=100&limit=20. Easy to implement, expensive on large tables (DB has to skip 100 rows). Susceptible to drift if data changes between calls. - Page / per-page:
?page=5&per_page=20. Same as offset/limit with different naming. - Cursor-based:
?cursor=abc123&limit=20. The cursor encodes "next position." Stable under inserts/deletes, harder to "jump to page N." - Keyset:
?after=2026-05-10T12:00:00Z. Sort key as cursor.
What to test:
1. Page contents are stable and don't overlap:
const page1 = await request.get('/items?page=1&per_page=10').then(r => r.json());
const page2 = await request.get('/items?page=2&per_page=10').then(r => r.json());
const ids1 = page1.items.map(i => i.id);
const ids2 = page2.items.map(i => i.id);
expect(new Set([...ids1, ...ids2]).size).toBe(20); // no duplicates
2. The total record count matches the sum of paginated results:
const all = [];
let page = 1;
while (true) {
const res = await request.get(`/items?page=${page}&per_page=10`).then(r => r.json());
all.push(...res.items);
if (res.items.length < 10) break;
page++;
}
expect(all.length).toBe(res.total);
3. Beyond-last page returns empty list, not error:
const res = await request.get('/items?page=99999&per_page=10');
expect(res.status()).toBe(200);
expect((await res.json()).items).toEqual([]);
4. Per-page boundaries: per_page=1, per_page=max, per_page=0 (likely 400), per_page=−1.
5. Stability under change (cursor-based): insert a new record between page calls; the cursor should still produce consistent results without duplicating or skipping.
6. Total field is correct: many APIs return total or total_pages. Compare against the actual count from the paginated walk.
7. Sorting consistency: pagination is meaningless without a stable sort. Test with explicit sort parameters; assert the same record orders across pages.
Common bugs:
- Last item of page N appears as first of page N+1 (off-by-one).
- "total" stops being correct after a delete.
- Cursor returns the same page repeatedly.
- per_page=0 hangs the server.
For very large datasets, write a test that walks 100+ pages and asserts no duplicates. Catches "race-with-inserts" bugs that smaller tests miss.
// EXAMPLE
// Walk every page and assert no duplicates / no skips
test('pagination covers all items without duplicates', async ({ request }) => {
const seen = new Set<string>();
let page = 1;
let total = 0;
while (true) {
const res = await request.get(`/items?page=${page}&per_page=50`);
const body = await res.json();
total = body.total;
for (const item of body.items) {
expect(seen.has(item.id)).toBe(false); // no duplicates
seen.add(item.id);
}
if (body.items.length < 50) break;
page++;
}
expect(seen.size).toBe(total); // all items returned exactly once
});// WHAT INTERVIEWERS LOOK FOR
// COMMON PITFALL
// Related questions
How do you validate a JSON response schema in your API tests?
API testing
Compare Postman, REST Assured, and Playwright's APIRequestContext for API testing.
API testing
How do you ensure API tests don't depend on each other (test isolation)?
API testing
Explain how you'd test pagination with REST Assured.
REST Assured