# Coverage model

This describes today's public odds catalog behaviour: what an authenticated account can query, how
its coverage grant constrains results, and which query filters you control.

## What you can query

Public catalog (documented in OpenAPI / Swagger at [/docs](/docs/)):

- `GET /authenticate`
- `GET /rest` (API version)
- `GET /rest/account` (your own account: licence, coverage grant, hourly limit)
- `GET /rest/odds` and `GET /rest/odds/{events,markets,providers,sports,competitions}`

Internal and administrative routes exist on the same host but are **out of scope** for the public
catalog and SDKs.

Entity matching (`/rest/match/*`) resolves provider-supplied names to canonical entities. Like the
rest of `/rest`, it is available to any authenticated account — it is a separate surface from this
odds catalog. See the [matching guide](./matching.md).

## Coverage grant

Every account carries a **coverage grant**: a list of `sport` × `bookmaker` (provider) pairs, where
`*` on either side means "any". `GET /rest/account` returns the grant for the calling account.

- A request inside the grant is served, scoped to the granted pairs.
- A request outside the grant — for a sport or provider it does not cover — is rejected with **403**
  `{"error":"coverage_not_entitled"}`, and nothing is charged.
- The grant is enforced on `GET /rest/odds` and `GET /rest/odds/events` (the odds list and its
  distinct-events view).
- The remaining catalog list endpoints (`GET /rest/odds/{markets,providers,sports,competitions}`) are
  discovery endpoints and are **not** constrained by the grant.
- `/rest/match/*` is exempt as well: you may resolve names for a provider you do not receive odds
  for.
- An account with an empty grant is denied every grant-enforced request, so if a call 403s with
  `coverage_not_entitled`, check the grant `GET /rest/account` reports.

## Rate limits

Accounts may also be capped: `GET /rest/account` reports the hourly data-point limit under
`throttle`. Once the current hour's usage is over the limit, further requests on the metered routes
are rejected with **429** until the hour rolls over, and the body carries the count, the limit, the
reset time and `retry_after_seconds`. See the [error guide](./errors.md).

Successful responses on the metered routes (`GET /rest/odds` and `/rest/match/*`) carry the caller's
current usage in response headers: `X-Data-Points-This-Hour`, `X-Data-Points-Limit` and
`X-Hour-Resets-At`.

## Query filters (caller-controlled)

Typical optional query params on odds endpoints:

- `sport`, `provider`, `competition` / `competitionName`, `market`
- `fromNow` (default true — upcoming events only)
- `eventName` with `eventTime` — the supported single-event filter
- `limit` / `skip` / sort fields

See the OpenAPI operation docs for the full parameter set.

`eventId` is an **internal** key: it identifies both the event and the data source behind a row, so
it is not a meaningful or stable filter for consumers and it is **not** part of the documented query
surface. To pin one event, pass its `eventName` together with `eventTime`.

## Related

- [Quickstart](./quickstart.md)
- [Error handling](./errors.md)
- [Entity matching](./matching.md)
