Odds

Odds lines (market + model)

Time-series odds lines, newest first — the derived market line (`source: "eo_market"`) and the proprietary model line (`source: "eo_model"`; ADR 0009). With `?match={id}` the response is that match's line history, and `?cursor=` pages through it. **Without `?match=` the default is a bulk snapshot** — the latest line per source per open market across the game, the product's headline endpoint for pulling a whole slate in one call; the snapshot is a single page, so it **ignores `?cursor=`** (its `next_cursor` is always null). The movement fields (`open_price`, `delta_since_open`) are computed per response from the line's own series. Market rows appear once the multi-book scrape runs (`EXPOSE_MARKET_ODDS`; at least 2 contributing books per line); model rows only after the model's validation gate clears (`EXPOSE_MODEL_ODDS`) — before then model rows are simply absent, never faked. Raw per-book bookmaker rows are never returned (see the top-level description). **Markets & the `line` field.** Beyond match winner, the endpoint serves the derived line for additional markets as multi-book coverage arrives: `total_maps` (`outcome_key` `over`/`under`, `line` = the games threshold, e.g. `2.5`), `map_handicap` (`outcome_key` `home`/`away`, `line` = the **home team's signed handicap**, e.g. `-1.5`, carried by both complementary legs), and per-map `map_winner`. Because markets are distinguished by `line` as well as market and outcome, the **bulk snapshot may return more than one row per `market_id`** — one per distinct `line`. Each row is still the latest per `(market, outcome, line, source)`.

GET
/v1/{game}/odds

Time-series odds lines, newest first — the derived market line (source: "eo_market") and the proprietary model line (source: "eo_model"; ADR 0009). With ?match={id} the response is that match's line history, and ?cursor= pages through it. Without ?match= the default is a bulk snapshot — the latest line per source per open market across the game, the product's headline endpoint for pulling a whole slate in one call; the snapshot is a single page, so it ignores ?cursor= (its next_cursor is always null). The movement fields (open_price, delta_since_open) are computed per response from the line's own series. Market rows appear once the multi-book scrape runs (EXPOSE_MARKET_ODDS; at least 2 contributing books per line); model rows only after the model's validation gate clears (EXPOSE_MODEL_ODDS) — before then model rows are simply absent, never faked. Raw per-book bookmaker rows are never returned (see the top-level description).

Markets & the line field. Beyond match winner, the endpoint serves the derived line for additional markets as multi-book coverage arrives: total_maps (outcome_key over/under, line = the games threshold, e.g. 2.5), map_handicap (outcome_key home/away, line = the home team's signed handicap, e.g. -1.5, carried by both complementary legs), and per-map map_winner. Because markets are distinguished by line as well as market and outcome, the bulk snapshot may return more than one row per market_id — one per distinct line. Each row is still the latest per (market, outcome, line, source).

Authorization

AuthorizationBearer <token>

Primary auth (live). Authorization: Bearer <api-key> — the header takes precedence over the query fallback. The raw key is hashed (SHA-256, hex) and looked up in api.keys.key_hash; the raw value is never stored.

In: header

Path Parameters

game*string

Game slug. cs2 is the only populated title today; an un-onboarded game 404s cleanly.

Query Parameters

match?string

Scope to one match's full line history, newest first and cursor-paginated. Omit it for a different mode entirely: the bulk snapshot, which returns the latest line per outcome across every open market and is not paginated.

Formatuuid
source?string

Filter to one served source. Omit for both.

Value in

  • "eo_market"
  • "eo_model"
latest?"1"

With match, return that match's CURRENT board — the latest line per market, outcome and handicap/total line — instead of its paginated tick history. Use it whenever you only need present prices: it is dramatically cheaper on both sides, since the response's open_price lateral then runs once per returned line rather than once per historical tick. Not paginated (its size is bounded by the match's market surface). Ignored without match, because the bulk snapshot already returns latest-per-market.

Value in

  • "1"
market?string

Filter one match's history to a single market type, e.g. match_winner, map_winner or total_maps (the market_type on each returned line). Only applies together with match — the bulk snapshot already returns just the latest line per market, so it has nothing to narrow. Use it when you are plotting one series: limit is shared across every market on the match, so without it a busy match spends most of its page on markets you are not drawing. An unrecognised but well-formed key returns no rows rather than an error.

Match^[a-z0-9_]{1,40}$
limit?integer

Page size — this endpoint's own default is 200 and its maximum 1000, higher than the API-wide 100/500, because an odds page is one row per outcome per source rather than one row per entity.

In history mode (?match=) this is a page size and cursor walks the rest. In snapshot mode (no match) it is a hard truncation: the snapshot is not paginated, meta.next_cursor is always null, and any lines past the limit are silently dropped. If meta.count equals your limit in snapshot mode, assume there were more.

Range1 <= value <= 1000
Default200
cursor?string

Opaque forward pagination cursor — pass the previous page's meta.next_cursor verbatim. Omit for the first page. Never construct one by hand.

Response Body

application/json

application/json

application/json

application/json

application/json

curl -X GET "https://example.com/v1/cs2/odds"
{  "data": [    {      "id": "019f2dc6-4539-7e15-bd2c-347d1562fd38",      "match_id": "019f28d9-f78f-790f-af0e-6d350172bc3e",      "tournament_id": null,      "market_id": "019f28da-10a5-744c-8281-6bc1f97f17d3",      "source": "eo_market",      "model_version": null,      "book_count": 2,      "outcome_key": "away",      "label": "DONSTU",      "participant_type": "team",      "participant_id": "019f23d2-5a34-7a73-b711-bd02601f9890",      "line": null,      "price": 13.39966832504146,      "is_winner": null,      "is_closing": false,      "open_price": 1.8144053351713343,      "delta_since_open": 11.585262989870126,      "captured_at": "2026-07-04T15:36:36.403882Z"    },    {      "id": "019f2dc6-4539-7958-ba0b-934cb420cb13",      "match_id": "019f28d9-f78f-790f-af0e-6d350172bc3e",      "tournament_id": null,      "market_id": "019f28da-10a5-744c-8281-6bc1f97f17d3",      "source": "eo_market",      "model_version": null,      "book_count": 2,      "outcome_key": "home",      "label": "SAW",      "participant_type": "team",      "participant_id": "019f23d2-5a33-70ad-a99f-3799b5cba292",      "line": null,      "price": 1.080647318443226,      "is_winner": null,      "is_closing": false,      "open_price": 2.22788979493807,      "delta_since_open": -1.147242476494844,      "captured_at": "2026-07-04T15:36:36.403882Z"    }  ],  "meta": {    "count": 2,    "next_cursor": null  }}
{  "error": {    "code": "not_found",    "message": "not found",    "request_id": "019f3d18-c15f-7319-81a7-343e8a80a578"  }}
{  "error": {    "code": "not_found",    "message": "not found",    "request_id": "019f3d18-c15f-7319-81a7-343e8a80a578"  }}
{  "error": {    "code": "not_found",    "message": "not found",    "request_id": "019f3d18-c15f-7319-81a7-343e8a80a578"  }}

{  "error": {    "code": "rate_limited",    "message": "rate limit exceeded",    "request_id": "019f3d18-c15f-7319-81a7-343e8a80a578"  }}