Markets and outcomes
How an odds line is structured — markets, outcomes, lines and prices.
An odds line answers one question about one proposition. A few fields locate it.
The shape
{
"market_id": "019f28da-10a5-744c-8281-6bc1f97f17d3",
"market_type": "match_winner",
"map_number": null,
"source": "eo_market",
"outcome_key": "away",
"label": "DONSTU",
"line": null,
"price": 13.39,
"captured_at": "2026-07-04T15:36:36.403882Z"
}market_id identifies the proposition — "who wins this match". Every outcome of the same
proposition shares it, so grouping a page of lines by market_id reconstructs whole markets.
market_type says which proposition it is: match_winner, map_winner, map_handicap or
total_maps. map_number names the map a per-map market covers, and is null on a
series-level market. Together they mean you never have to infer a market's identity from an opaque
id — which is what made everything except the match winner unrenderable before they were served.
outcome_key is the machine-readable side: home, away, over, under, and occasionally a
correct-score literal in a_b form — 2_1, 0_3 — with an underscore, not a hyphen.
label is the human name for the same thing (a team name). Switch on
outcome_key; display label.
When an outcome names a competitor, label is that team's or player's current name, resolved
when you fetch — so a rebrand reads consistently across a match's whole price history instead of
leaving old lines stuck on the old name.
line is the threshold that defines a market variant — 2.5 for a total, -1.5 for a
handicap. It is null on a moneyline, where there is no threshold. Two rows differing only by
line are genuinely different markets.
price is decimal odds, always. No American, no fractional, no implied-probability field.
Decimal odds
A decimal price is the total return per unit staked, so it converts to an implied probability by reciprocal:
implied probability = 1 / priceprice: 1.80 → 0.556. price: 13.39 → 0.075.
Because our published line is already de-vigged, the implied probabilities of a market's outcomes sum to almost exactly 1 — unlike a raw bookmaker price, where they sum to more. See the market line for what that means and why.
Guard the reciprocal
price is a float from real market data and can be large for a heavy underdog. It is never zero,
but defensive code should still avoid dividing without a check.
Participants
When an outcome is about a competitor, participant_type (team or player) and participant_id
point at it, so you can join to the entity without parsing label. Both are null on outcomes that
aren't about a competitor, such as a total.
Match markets and tournament markets
An odds line carries either match_id or tournament_id, never both. A tournament-scoped
line is an outright — "who wins the event" — rather than a fixture price.
Today tournament_id is null on every line we serve: outrights are not collected yet, so the
slot is in the contract and unused. Write your code to check which is populated — that is what will
keep working when they arrive — but don't wait for a non-null value.
What you won't find
There is no endpoint listing markets or market types. market_type and market_id on each line are
enough to identify and group them; a market catalogue isn't exposed.
And no per-book prices, ever — not as a field, not as a source, not as an attribution. Every line
you receive is one of exactly two derived sources. That's a product decision, not an omission; see
the market line.