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)`.
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).
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 slug. cs2 is the only populated title today; an un-onboarded game 404s cleanly.
Query Parameters
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.
uuidFilter to one served source. Omit for both.
Value in
- "eo_market"
- "eo_model"
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.
1 <= value <= 1000200Opaque 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": "not_found", "message": "not found", "request_id": "019f3d18-c15f-7319-81a7-343e8a80a578" }}Service health (incl. DB reachability) GET
Unauthenticated liveness/readiness probe. 200 when the DB is reachable, 503 otherwise.
A player's all-time career aggregate GET
Derived career totals + averages over the player's whole-match stat rows (maps are not double-counted). Averages are null for a player with no rated maps. 404 for an unknown player. Enveloped `{"data": PlayerCareerStats}`.