> ## 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.

# Selection Object — Passthrough API Schema Reference

> The selection object is one bettable outcome: market key and name, bet name, line, American and decimal price, betting cutoff, stake limit, lock status, and the market version used for deduplication.

A **selection** is one bettable outcome — one row on a betting board. It is the unit the Passthrough API returns everywhere prices appear: in [`GET /odds`](/passthrough/market-odds), and in `snapshot` and `update` frames on the [live stream](/passthrough/stream).

The sportsbook's own format nests prices inside markets inside matchups, with outcomes referenced by id. The passthrough flattens all of that into a list of selections with names already resolved, so you do not have to reimplement the sportsbook's labelling rules.

```json theme={null}
{
  "market_key": "s;1;ou;2.5",
  "market_name": "Map 1 - Total",
  "period": 1,
  "bet_name": "Over 2.5",
  "line": 2.5,
  "price": -101,
  "decimal": 1.99,
  "status": "unlocked",
  "version": 214,
  "cutoff_at": "2026-07-28T13:40:13Z",
  "limit": 225
}
```

## Fields

| Field         | Type    | Always present | Description                                                                                     |
| ------------- | ------- | -------------- | ----------------------------------------------------------------------------------------------- |
| `market_key`  | string  | Yes            | Stable identifier for the market this outcome belongs to. Use it as your state key              |
| `market_name` | string  | Yes            | Human-readable market name, for example `Map 2 Round 13 - Money Line`                           |
| `period`      | integer | Yes            | The sportsbook's period number. `0` is the whole match                                          |
| `bet_name`    | string  | Yes            | Human-readable outcome name, for example `T1`, `Over 2.5`, `FURIA +2.5`                         |
| `line`        | number  | No             | Handicap or total line. Absent on money line markets                                            |
| `price`       | integer | Yes            | American odds                                                                                   |
| `decimal`     | number  | Yes            | Decimal odds, rounded to 3 decimal places                                                       |
| `status`      | string  | Yes            | `unlocked` if bettable, `locked` if the market is closed, in-play suspended, or past its cutoff |
| `version`     | integer | Yes            | Monotonic version of the market this selection came from                                        |
| `cutoff_at`   | string  | Yes            | RFC3339 UTC time betting closes on this market, with a `Z` suffix                               |
| `limit`       | integer | Yes            | Maximum risk stake the sportsbook accepts on this market, in the account's currency             |

## `market_key`

The key is the sportsbook's own market identifier, passed through as an opaque string. It encodes the period and market type, and the line where one applies:

| Example      | Market                 |
| ------------ | ---------------------- |
| `s;0;m`      | Match money line       |
| `s;1;m`      | Map 1 money line       |
| `s;0;s;-2.5` | Match handicap at −2.5 |
| `s;1;ou;2.5` | Map 1 total at 2.5     |

<Warning>
  Treat `market_key` as **opaque**. Use it for equality and as a map key — do not parse it. The format is the sportsbook's, and because this is a passthrough it can change without notice on their side.
</Warning>

All selections sharing a `market_key` share a `version`, `status`, `cutoff_at`, and `limit`, because those are properties of the market rather than the individual outcome.

### An alternate line is its own market

Because the line is part of the key, `s;0;s;-2.5` and `s;0;s;-3.5` are two separate markets with independent versions. A line move arrives as a new market key, not as a changed `line` on an existing one — and the old key arrives in `removed_market_keys` when the sportsbook withdraws it.

## `market_name` and `period`

`market_name` is composed as `<period label> - <market label>`, both taken from the sportsbook's own label table rather than generated:

```text theme={null}
Match - Money Line
Map 1 - Total
Map 2 1st Half - Handicap
Map 2 Round 13 - Money Line
```

Period numbering is sport-specific and is **not** a simple counter — in esports, period `20` is `Map 1 Round 3` and period `66` is `Map 2 Round 13`. Use `market_name` for display and `period` only as an identifier; do not derive one from the other.

On a **special**, `market_name` is the special's own description instead — `Correct Score`, `(Map 2) Winning Margin` — since that description already carries the full context.

<Note>
  The label table loads at startup. In the first moments of a cold start, before it is available, names fall back to a generic form such as `Period 3 - Money Line`. Names never invent a round number they cannot look up.
</Note>

## `bet_name`

How the outcome is named depends on the market type:

| Market type | `bet_name`                                                     | Example               |
| ----------- | -------------------------------------------------------------- | --------------------- |
| Money line  | Team name, or `Draw`                                           | `T1`                  |
| Handicap    | Team name and signed line                                      | `FURIA +2.5`          |
| Total       | `Over` or `Under` and the line                                 | `Over 2.5`            |
| Team total  | `Over` or `Under` and the line, with the side in `market_name` | `Under 13.5`          |
| Special     | The outcome text                                               | `Leviatan 0, FURIA 2` |

Outcomes the sportsbook keys by participant id — which includes every special, and some money lines — are resolved to the participant's name. If a name genuinely cannot be resolved, the fallback is the raw designation rather than an empty string.

## `price` and `decimal`

`price` is **American odds**, exactly as the sportsbook publishes them. `decimal` is the same price converted, rounded to three decimal places, provided so you do not have to convert it yourself:

| American | Decimal |
| -------- | ------- |
| `+150`   | `2.5`   |
| `+100`   | `2.0`   |
| `-110`   | `1.909` |
| `-120`   | `1.833` |

`decimal` is a convenience derived from `price`. Where the two could ever disagree by rounding, `price` is authoritative.

## `status`

| Value      | Meaning                                                                 |
| ---------- | ----------------------------------------------------------------------- |
| `unlocked` | The market is open and the price is bettable                            |
| `locked`   | The resolved market status is not `open`, **or** `cutoff_at` has passed |

A locked selection keeps its last known price. That is intentional — it lets you keep rendering the board through an in-play suspension instead of blanking it.

### Two things lock a market

The upstream sportsbook only ever publishes the first of these, so the passthrough resolves both for you:

* The market's resolved status is not `open` — either the market's own status, or the matchup's period status when the market does not carry one.
* `cutoff_at` has passed relative to the current server time.

The cutoff check exists because upstream **does not retract a prematch market when a fixture goes in-play**. Instead it mints a brand-new live matchup with a fresh future cutoff and abandons the prematch one, frozen at `status: open` forever with a cutoff in the past. Without the cutoff check those stale prematch quotes would look bettable indefinitely; with it, any client that trusts `status` alone is safe. You do not have to police stale prematch quotes yourself.

Every [`GET /odds`](/passthrough/market-odds) response and every [stream](/passthrough/stream) frame also carries a top-level `ts` (epoch milliseconds) — the moment SportriX last applied a change to that match. If `ts` is not advancing, no new information has arrived for that match, and staleness is visible without any extra bookkeeping.

<Warning>
  `locked` and *removed* are different states. A locked market is still in the book and may reopen at a new price. A market listed in `removed_market_keys` is gone and should be deleted from your state.
</Warning>

## `cutoff_at`

RFC3339 UTC timestamp for when betting closes on this market, always normalised to a `Z` suffix (upstream spells the same instant as `+00:00` on markets and `Z` on periods; both come out as `Z` here). Present on every selection, straight and special alike. If the market itself does not carry its own cutoff, the matchup's period-level cutoff is used as a fallback so the field is always populated.

Use `cutoff_at` to:

* **Detect stale prematch markets.** A cutoff in the past is the passthrough's signal that this market has been abandoned in favour of a live twin — the same signal that flips `status` to `locked`.
* **Show a countdown.** Render the remaining time until the market closes.
* **Sort or filter markets by close time**, for example to surface markets closing soonest.

## `limit`

The maximum risk stake the sportsbook will accept on a single bet on this market, expressed as an integer in the account's currency. Present on every selection.

Limits vary widely — even within one match. For example, a response can carry a `limit` of `100` on first-half markets, `250` on team totals, `500` on match totals, and `1000` on match handicaps and 1X2. These values are illustrative, not fixed bands; always use the value returned for the current market.

A client sizing a bet **must** read `limit` per market rather than assuming a match-wide, sport-wide, or account-wide constant. Use it to cap the stake you offer a user, gray out a stake input that exceeds it, or decide whether a market is deep enough to be worth surfacing at all.

## `version`

The monotonic version of the market this selection came from. It is the mechanism for correct state updates:

* **Deduplicate.** Ignore a selection whose `version` is not greater than the version you already hold for that `market_key`.
* **Detect staleness.** A version that stops advancing while the feed is healthy means a quiet market, not a lost one.

Versions are only comparable **within a single `market_key`**. Comparing versions across markets is meaningless.

<Note>
  Match objects also carry a `version`. That is the match-level version and moves independently of any market's. Do not compare the two.
</Note>
