# Pagination & response size

> page and perPage are top-level fields on the criminal endpoint, perPage is clamped to 200, and counts.records is the true total before the page slice is cut.

- **HTML:** https://offendersearch.app/docs/criminal/pagination
- **Base URL:** https://api.offendersearch.app
- **Authentication:** `X-API-Key` request header
- **OpenAPI:** https://offendersearch.app/openapi.json · https://offendersearch.app/openapi.yaml
- **Criminal reference as markdown:** https://offendersearch.app/docs/criminal.md

## page and perPage are top-level

`page` and `perPage` are **top-level request fields**, not inside `query` — a deliberate difference from the Sex-Offender API. Sending them inside `query` silently gives you the defaults (`page: 1`, `perPage: 50`). They are echoed back at the top level exactly as resolved.

| Parameter | Where | Default | Constraint |
| --- | --- | --- | --- |
| `page` | request top level | `1` | `>= 1` |
| `perPage` | request top level | `50` | clamped to **1–200** |

## Behaviour

- **`counts.records` is the true total** and does not change as you page. Walk `page = 1 … totalPages` to retrieve the whole result set; each record appears exactly once.
- **`totalPages`** is `ceil(records / perPage)`, minimum `1`.
- **A page past the end returns an empty `records` array** — `page` echoes the number you asked for, `counts.records` and `totalPages` are unchanged, and `recordsReturned` is `0`. So `while (records.length) page++` terminates.

```json
// perPage 300 is clamped to 200; a 512-record result therefore spans 3 pages
{ "page": 1, "perPage": 200, "totalPages": 3, "counts": { "records": 512, "recordsReturned": 200 } }

// page 99 of that same result — past the end
{ "page": 99, "perPage": 200, "totalPages": 3, "counts": { "records": 512, "recordsReturned": 0 }, "records": [] }
```

**A cap is not a page.** One response carries at most 200 records, and `perPage` slices an answer you can walk in full. Read `counts.records` against `counts.recordsReturned`: if they differ, there is more to fetch.

---

## Related

- Previous: [Result completeness & source status](https://offendersearch.app/docs/criminal/result-completeness.md)
- Next: [Coverage today & the sources endpoint](https://offendersearch.app/docs/criminal/coverage.md)
- Index: [Criminal Search API reference](https://offendersearch.app/docs/criminal.md)
