# Pagination

> How limit, offset, and odds-history cursors work across the API.

## `limit` and `offset`
Person and team search paginate with `limit`/`offset`: `/v1/persons` and `/v1/teams`.
Both take two query params:

| Param    | Default | Max | Meaning                             |
| -------- | ------- | --- | ----------------------------------- |
| `limit`  | 50      | 200 | Rows per page                       |
| `offset` | 0       | —   | Rows to skip before the page starts |

```bash title="page_2.sh"
curl -H "X-API-Key: $STATSHAWK_KEY" \
  "https://api.statshawk.ai/v1/persons?q=tatum&limit=50&offset=50"
```

Requesting a `limit` above 200 doesn't error. It's clamped to 200 server-side.

Other list endpoints (editions, contests, rosters, standings) don't take these
params: they return the full set for the scope you asked for, so narrow them with
their own filters (`date`, `competition`, and so on) instead of paginating.
(`/v1/analysis/stat-board` accepts a `limit`, but it caps the board's row count;
there is no `offset`.) Odds history uses `limit` with a tie-safe `cursor` instead
of `offset` — see [Odds history pages](/docs/getting-started/quotas#odds-history-pages).

## Reading the response
Every list response's `data` carries `items` (the page) and `total` (the full match
count, not just this page):

```json title="response shape"
{
  "data": {
    "items": [ /* up to `limit` rows */ ],
    "total": 812
  },
  "meta": { "request_id": "…", "fetched_at": "…", "cache": "HIT", "version": "v1" }
}
```

Use `total` to know when you've reached the end: keep incrementing `offset` by `limit`
until `offset + items.length >= total`.

```python title="paginate.py"
import os, requests
KEY = os.environ["STATSHAWK_KEY"]
BASE = "https://api.statshawk.ai/v1"

all_items, offset, limit = [], 0, 200
while True:
    page = requests.get(
        f"{BASE}/persons",
        headers={"X-API-Key": KEY},
        params={"limit": limit, "offset": offset},
        timeout=10,
    ).json()["data"]
    all_items.extend(page["items"])
    offset += limit
    if offset >= page["total"]:
        break

print(f"{len(all_items)} persons")
```

[Try /v1/persons in the playground →](/docs/api)

## Odds history pages
`GET /v1/contests/{id}/odds/history` and `GET /v1/persons/{id}/odds/history` page with
`limit` and `cursor`, not `offset`. Paging fields are on `data` (`truncated`,
`next_cursor`, `next_before`) — not on `meta`. Full contract: filters, `interval`,
`before` as a window, and unit cost live on
[Quotas · Odds history pages](/docs/getting-started/quotas#odds-history-pages).
