Changelog
Notable changes to the EsportsOdds API and documentation.
Notable, developer-facing changes to the API. Breaking changes ship under a new version prefix;
within v1, changes are additive.
2026-07-26
Action required: the error body shape changed
Error responses moved from {"error": "message"} to a coded envelope. If you match on the old
flat string, update before this reaches you — everything else in this release is additive.
-
Coded error envelope. Every non-2xx response now returns:
{ "error": { "code": "invalid_cursor", "message": "invalid cursor", "request_id": "019f…" } }codeis a stable identifier you can switch on;messageis prose and explicitly not a contract. This exists because the flat shape gave you nothing machine-readable beyond the HTTP status, which collapses a dozen distinct 400s into one bucket — so clients were left string-matching text we were free to change. The codes that matter most: a429is now distinguishablyrate_limited(retry after a moment) orquota_exceeded(retrying cannot help until the window resets). See Errors for the full list and a worked handler. -
X-Request-Idon every response, success or failure, and repeated aserror.request_id. Log it — quoting it turns a support question into a single log lookup. A panic now also returns the envelope rather than an empty body. -
Slug addressing. A path
{id}accepts either a UUID or the resource's slug on teams, players and tournaments:/v1/cs2/teams/natus-vincereworks.?opponent=on head-to-head accepts both too. Matches have no slug. -
?include=on match detail.?include=teams,tournament,oddsembeds the related resources, so a match view is one request instead of four. Omit it and the response is byte-for-byte what it was. -
?sort=on/teams,/playersand/rankings, with a-prefix for descending. Previously the parameter was accepted and silently ignored; an unrecognised key is now a400listing the accepted ones. Cursors are bound to the sort they were minted under — cursors issued before this change keep working on the default order. -
CORS for browser callers. The rate-limit and quota headers are now readable cross-origin via
Access-Control-Expose-Headers, and the interactive "try it" panel in these docs executes real requests. -
Documentation. Every operation, parameter and schema field is now documented, every endpoint carries a real response example captured from production, and the reference is grouped by resource rather than by URL segment (old reference URLs redirect). The spec is published at /openapi.yaml. Nothing in these docs describes unshipped behaviour any more — the fields previously flagged as intended-but-not-yet-live are all shipped.
2026-07
- Per-match data availability — matches now carry
data_available, telling you which sub-resources actually hold data for that match:{maps, stats, vetoes, rounds, depth}. Each key is the sub-resource's own path segment, soGET /v1/{game}/matches/{id}/{key}returns rows if and only if that key is true — no more fetching an endpoint to discover it's empty.?has=rounds,depthfilters a list the same way (comma-separated, AND-ed). Coverage is not uniform: round-by-round and per-player depth come from a single upstream, so matches it doesn't cover carry results and little else. The flags describe what is stored now, not a forecast — a scheduled match reads all-false and flips as data lands./coveragealso now declaresround_statsandplayer_depth, two capabilities that were being served but never listed. - Roster history —
GET /v1/{game}/teams/{id}/roster-historyreturns the five a team actually fielded in each recent completed match, newest first, with the roster changes between them (players_in/players_out,has_debutant,days_since_prev). Readcontinuitybefore interpretingchanged_count: aresetis an organisation rename or full rebuild, not five stand-ins, and matches where a team used more than five players across maps are omitted rather than guessed at. - Market odds line live —
GET /v1/{game}/oddsserves the derivedeo_marketline: a de-vigged aggregate combined from multiple bookmakers and exchanges (published only with ≥2 contributing books;book_counton every line; contributing books are never named). - WebSocket tickets —
POST /v1/{game}/ws-tokenmints a short-lived (60s) signed ticket for the WebSocket handshake (wss://…/v1/ws?token=…), so raw API keys stay out of URLs. The?apiKey=handshake fallback is deprecated and will be removed. - Initial
v1surface for Counter-Strike 2:matches,odds,teams,tournaments,players,rankings,coverage. - WebSocket channel for update notifications. Metering is settled: a per-key concurrent-connection cap (5 on the standard plan), one metered request to mint the ticket, and no per-message charge.
- Interactive API reference generated directly from the OpenAPI spec.
Coming soon
- Modeled odds — our own proprietary line (
eo_modelsource) is in development, gated behind an accuracy-validation step. It will be documented and served here once that clears, with its track record (calibration + Brier score) published alongside. - Additional markets beyond match winner, as their data clears the same quality bar.
- Additional game namespaces beyond
cs2, following the same/v1/{game}/{resource}convention.