> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sportrix.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# SportriX Passthrough API — Real-Time Sportsbook Odds

> The SportriX Passthrough API re-serves sportsbook odds as clean REST endpoints and a low-latency WebSocket stream. Base URL, endpoints, and market versioning explained.

The SportriX Passthrough API passes sportsbook odds straight through to you. SportriX uses fast websocket connection to deliver the lowest latency odds. To achieve the lowest possible latency the data is delivered straight to you.

Each sportsbook is passed through under its own path prefix.

## Base URL

```text theme={null}
https://passthrough.sportrix.ai/v1/pinnacle
```

Every path in this section is relative to that prefix. For example, `GET /sports` resolves to `GET https://passthrough.sportrix.ai/v1/pinnacle/sports`.

```bash theme={null}
curl https://passthrough.sportrix.ai/v1/pinnacle/sports \
  -H "X-API-Key: sk_your_api_key_here"
```

Requesting the base path itself returns a machine-readable route list, without a key — useful for confirming you have the right host and prefix before your credentials are sorted out.

## Endpoint quick reference

| Endpoint       | Auth         | Description                                                             |
| -------------- | ------------ | ----------------------------------------------------------------------- |
| `GET /sports`  | `odds` scope | Sports currently served                                                 |
| `GET /leagues` | `odds` scope | Leagues within a sport                                                  |
| `GET /matches` | `odds` scope | Match list for one sport (metadata only, no prices). Cached and gzipped |
| `GET /odds`    | `odds` scope | Every current selection for one match                                   |
| `WS /data`     | `odds` scope | Live stream, subscribed per match                                       |
| `GET /healthz` | none         | Liveness probe                                                          |

## How to use it

The REST catalog and the stream are meant to be used together:

<Steps>
  <Step title="Discover matches over REST">
    Call [`GET /sports`](/passthrough/sports) for the sport ids you hold, then [`GET /leagues?sport=<id>`](/passthrough/leagues), then [`GET /matches?sport=<id>`](/passthrough/matches) to find the match ids you care about. `sport` is required on `/matches`. These endpoints return metadata only — no prices. `/matches` also includes each game's in-play derivatives by default; pass `&include=specials` to fetch the whole catalog.
  </Step>

  <Step title="Open the WebSocket and subscribe">
    Connect to `WS /data` and send a `subscribe` message per match id. The server replies with a full snapshot of that match's selections, then pushes only what changes.
  </Step>

  <Step title="Apply updates by market version">
    Every selection carries a `version`, `cutoff_at`, and `limit`. Ignore any selection whose version is not newer than the one you hold for that `market_key`, and cap the risk stake at the current limit.
  </Step>
</Steps>

`GET /odds?match=<id>` returns the same selections as a WebSocket snapshot. Use it for one-off reads and for reconciling after a gap; do not poll it as a substitute for the stream.

## Prices and market versions

Odds are passed through as **selections** — one bettable outcome, already resolved to a human-readable market name and bet name, with American and decimal prices, an RFC3339 betting cutoff, and the market's maximum accepted risk stake in the account's currency. See the [Selection object](/passthrough/selection-object) for the full schema.

Limits are market-specific. They can differ across markets in the same match, so always read `limit` from the current selection instead of assuming one match-wide or sport-wide value.

Every selection carries the `version` of the market it came from. Versions are monotonic per market, so:

* **Dedup**: discard an incoming selection whose `version` is not greater than the version you already hold for that `market_key`.
* **Staleness**: a market whose version stops advancing while the feed is healthy is genuinely quiet, not lost.

Each live message also carries `ts` — the epoch-millisecond timestamp stamped the instant SportriX received the change from the sportsbook, not the moment it was sent to you. Compare it against your own clock to measure end-to-end latency.

## Response conventions

All responses are JSON. Unlike the SportriX API, list endpoints here return a **bare JSON array** — there is no pagination envelope, because the in-memory book is small enough to serve whole.

### Compression

Every REST response — `/sports`, `/leagues`, `/matches`, and `/odds` — is gzipped when the client sends `Accept-Encoding: gzip` and the response body is worth compressing (roughly 1 KiB or larger; small responses are sent uncompressed). Compressed responses carry `Content-Encoding: gzip` and `Vary: Accept-Encoding`. Most HTTP clients — `curl`, `requests`, `fetch`, Go's `net/http`, and so on — negotiate and decompress this automatically; you only need to opt in explicitly if you have disabled the default behavior.

Errors are a JSON object with a single `error` field:

```json theme={null}
{ "error": "unknown match" }
```

<Warning>
  The SportriX API returns errors as `{"detail": "..."}`. The Passthrough API uses `{"error": "..."}`. If you share error-handling code between the two, read both keys.
</Warning>

| Status | Meaning                                                                                                                                                                                 |
| ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `200`  | Success                                                                                                                                                                                 |
| `400`  | A required query parameter is missing or unparseable                                                                                                                                    |
| `401`  | Missing, unknown, or inactive API key                                                                                                                                                   |
| `403`  | Key is valid but lacks the `odds` scope, the sportsbook grant, or a grant for the sport referenced in the request. See [Per-sport grants](/passthrough/authentication#per-sport-grants) |
| `404`  | Unknown match, or a path that is not part of the API                                                                                                                                    |
| `503`  | The authentication backend is unavailable                                                                                                                                               |

<Tip>
  Parse responses leniently. Fields may be added to any response over time, so ignore keys you do not recognize rather than treating them as an error.
</Tip>

## Identifiers and time

Because this is a passthrough, match, league, and sport ids are **the sportsbook's own ids**, unchanged. They do not match the ids used by the [SportriX fixture catalog](/concepts/data-model) — the two products are not cross-referenced. This includes the sport ids used for [per-sport grants](/passthrough/authentication#per-sport-grants) on your API key.

Timestamps come in two forms:

* `ts`, `updated_at`, `last_msg_ms` — epoch **milliseconds** (integer).
* `start_time` — ISO 8601 in UTC, as issued by the sportsbook.

## Feed health

Sportrix maintains a separate upstream feed per sport, and any one of them can fail on its own. Every WebSocket connection receives a `heartbeat` frame every 10 seconds carrying a `feeds` object with per-sport `connected` and `last_msg_ms` values, scoped to the sports you are subscribed to, plus a `feed_healthy` summary of those same sports.

<Warning>
  When `feed_healthy` is `false`, at least one sport in your subscription has a disconnected upstream and its prices are no longer being refreshed. A client acting on these odds should **suspend and resync** the affected sport — the per-sport flags in `feeds` tell you which one — until it flips back. See the [live stream reference](/passthrough/stream#heartbeat).
</Warning>
