# GET /v1/persons/{person_id}/odds/history

```bash
curl "https://api.statshawk.ai/v1/persons/{person_id}/odds/history" -H "X-API-Key: sk_live_YOUR_KEY"
```

## Parameters

| Name | In | Required | Type | Description |
|---|---|---|---|---|
| `person_id` | path | yes | string | Minted person id (`per_…`) |
| `markets` | query | no | string | Required unless `measure` is set. Comma-separated markets |
| `measure` | query | no | string | Required unless `markets` is set. Qualified measure id or alias |
| `contest` | query | no | string | Minted contest id (`cst_…`). Without this, the window is `since` or the last 24h |
| `competition` | query | no | string | Competition slug or `comp_…` id |
| `books` | query | no | string | Comma-separated sportsbooks |
| `period` | query | no | string | Line period |
| `limit` | query | no | integer/int32 | Max price observations to return (default 50, max 100) |
| `before` | query | no | string | Exclusive ISO8601 upper bound on observed_at (window filter; not tie-safe) |
| `cursor` | query | no | string | Tie-safe older-page cursor from `next_cursor` (`raw\|{observed_at}\|{id}` or `bucket\|{start}`). Prefer this over `before` when paging. |
| `since` | query | no | string | Inclusive ISO8601 lower bound on observed_at |
| `interval` | query | no | string | `raw` \| `15m` \| `1h` \| `auto` (default auto) |

## Responses

- `200`: Newest-first page of pregame ticks for one market/measure on this person. Weight 10. Unscoped reads default to the last 24h. (schema: [ApiResponseOddsHistory](/docs/reference/data-model.md))
- `400`: Missing markets/measure, unknown filter, or invalid paging params (schema: [ErrorResponse](/docs/reference/data-model.md))
- `401`: Missing or invalid API key (`UNAUTHORIZED`). (schema: [ErrorResponse](/docs/reference/data-model.md))
- `404`: Person or contest not found (schema: [ErrorResponse](/docs/reference/data-model.md))
- `429`: Rate limit (`RATE_LIMIT_EXCEEDED`) or monthly quota (`QUOTA_EXCEEDED`). (schema: [ErrorResponse](/docs/reference/data-model.md))
- `500`: Internal failure (`INTERNAL_ERROR`, `CACHE_ERROR`). (schema: [ErrorResponse](/docs/reference/data-model.md))
- `501`: Capability not supported for this competition (`NOT_SUPPORTED`). (schema: [ErrorResponse](/docs/reference/data-model.md))
- `502`: Upstream data provider failed (`PROVIDER_ERROR`). (schema: [ErrorResponse](/docs/reference/data-model.md))
- `503`: Auth backend, DB, or providers unavailable (`AUTH_BACKEND_UNAVAILABLE`, `DB_UNAVAILABLE`). (schema: [ErrorResponse](/docs/reference/data-model.md))

Every response carries `X-Request-Id` and, when metered, `X-RateLimit-Limit` / `X-RateLimit-Remaining` / `X-RateLimit-Reset`. Errors use `{"error": {"code", "message"}}`. See [Errors](/docs/getting-started/errors.md).

Full spec: [/openapi.json](/openapi.json) · MCP equivalent: connect to `https://mcp.statshawk.ai/mcp`.
