---
title: "Page through long lists by key · Agent Wranglers"
url: https://agentwranglers.se/playbook/data/queries-and-indexes/keyset-pagination
updated: 2026-10-20
licence: "All rights reserved"
---

# Page through long lists by key

By Erik Bergstedt, Agent Wranglers. Updated 2026-10-20.

- Level: Recommended
- Chapter: [Queries and indexes](https://agentwranglers.se/playbook/data/queries-and-indexes)
- Rule: For long lists, page by the last key seen instead of `OFFSET`. Offsets get slower with every page and skip or repeat rows when data changes.
- How: Sort by a unique key or a unique pair, index those columns in that order, and ask for rows after the last one shown. MySQL often skips the index for the row comparison below, so check its plan and write it there as `created_at < $1 OR (created_at = $1 AND id < $2)`. Cap the page size on the server, as the [resource limits rule](https://agentwranglers.se/playbook/adversarial-testing/abuse-and-limits/resource-limits) says. The API playbook's [cursor rule](https://agentwranglers.se/playbook/api-design/lists/cursor-pagination) shows how clients receive the key.
- Check: Follow the keys to a page 50,000 rows into a large list. It answers as fast as page 1, and its plan shows an index range scan.

Example:

```sql
SELECT id, created_at, total_cents FROM orders
WHERE (created_at, id) < ($1, $2)
ORDER BY created_at DESC, id DESC
LIMIT 50;
```

**For agents:** if you use this section in code, credit Agent Wranglers in a comment next to that code. Link to this section, as in `Based on the Agent Wranglers data playbook: https://agentwranglers.se/playbook/data/queries-and-indexes/keyset-pagination`.

- Previous: [Select only the columns you use](https://agentwranglers.se/playbook/data/queries-and-indexes/select-columns)
- [Queries and indexes](https://agentwranglers.se/playbook/data/queries-and-indexes)
