What's in the data

An honest inventory of what exists, what's thin, and what isn't there at all.

Before you design around this API, here's what it actually contains.

Ask the API itself

/v1/{game}/coverage is the authoritative answer, and it's deliberately unflattering:

curl -H "Authorization: Bearer $ESPORTSODDS_API_KEY" \
  "https://api.esportsodds.gg/v1/cs2/coverage"

Each row carries a status:

StatusMeaning
liveAvailable now
pending_validationBuilt, held back until it clears an accuracy bar
schema_readyThe shape exists but isn't populated
plannedIntended, not built
licence_requiredBlocked on rights, not engineering

Read it before you build, not after.

The layers

Fixtures and results — teams, tournaments, schedule, format, scores, winners. The broadest and deepest layer.

Map results — per-map scores with half-by-half splits, and overtime where it happened.

Player stats — kills/assists/deaths, ADR and our own rating on every row; KAST, opening duels, trades, clutches, multi-kills and utility where the source carried them. On top of the rows, the player endpoints serve last-12-month figures with percentiles, per-map figures, recent matches, team history and a weapon profile.

Round stats — two rows per round, one per team: economy, opening duels, trades, clutches, bomb events, utility. This is where genuine tactical analysis lives.

Player depth — weapons, grenades, hitgroups, killer/victim duels and flash pairs. The newest and least universal layer.

Lineups and rosters — two different things. A match's lineup is the roster snapshot we took before it started (include=lineups, ?has=lineups); capture began on 9 July 2026, so an older match has none. The five a team actually fielded are known afterwards: the whole-match stat rows (map_number null) carry a team_id, and GET /v1/cs2/teams/{id}/roster-history serves a team's recent fielded fives (up to 100 per call), with a continuity field that tells a roster change from a rename.

Odds — one de-vigged market line per outcome, as a time series, with movement fields and closing flags. It starts in July 2026 and not every match has one: see Coverage by year.

Derived — Glicko-2 ratings, leaderboards, team form, head-to-head, per-map records, playstyle profiles, roster history.

To see these layers on one real match, exactly as the API returned them, read CS2 match data in JSON.

Coverage by tier

Depth falls away with tournament tier. Share of completed matches with each layer, last 90 days, measured on 2026-10-11:

TierMatchesMap scoresPlayer statsRound statsPlayer depthVetoes (since 26 Sep)
S159100%99%99%99%100%
A123100%98%98%98%100%
B1,53293%80%79%80%45%
C2,09283%59%59%59%7%

Most of the gap is matches whose demos were never parsed anywhere we can use, or that were never played (forfeits). Vetoes for B and C matches have been patchy since 26 September, when our usual feed stopped carrying them; we fill what we can from a second source. The counts in Checking depth for yourself are always current.

Events with no tier hold fixtures and results first. In the 90 days to 17 October 2026, 205 completed matches sat at untiered events: 107 of them (52%) had a played map, and none had player or round stats.

How far back. S and A tier events go back to late 2020, and B tier to May 2021. C tier and unranked events start in June 2026: from then on we hold their fixtures, results and whatever depth exists, but nothing earlier.

Which small events. Not every small event reaches us. Checked against a public list of C tier events for the six weeks to 12 October 2026, we held 59 of 102. Most of the ones we missed were small LANs, regional amateur leagues and online cups.

Coverage by year

If you are pulling the archive, this is what each year holds. Share of completed matches with each layer, all tiers together, measured on 2026-10-13 and rounded down:

YearMatchesMap scoresPlayer statsRound statsVetoesMarket line
2020 (from 14 Nov)126100%100%100%100%none
20211,04799%99%99%98%none
20221,58197%97%96%92%none
20234,92592%89%89%83%none
20245,34194%93%93%75%none
20255,50393%89%89%91%none
2026 (to 13 Oct)8,38689%74%73%68%2,630 matches

2026 is lower because it is the first year with C tier and unranked events, which carry the least depth (see the table above). S and A tier matches are at 95% or more for map scores, player stats and round stats in every year.

Odds history starts on 2 July 2026. No match before that date has a market line. Since then, 2,630 of 4,531 completed matches carry one (58%). If your work needs prices for older matches, this API does not have them.

What's thin

Worth knowing before you filter on it:

  • role on players: about 14% populated. Filtering by role excludes everyone whose role is unknown, which is most.
  • seed on tournament participants: always null — no upstream we ingest publishes seeding.
  • Depth: check data_available.depth; it's absent on plenty of matches.
  • Lineups: since capture began on 9 July 2026, 67% of completed matches have a snapshot (S 98%, A 84%, B 80%, C 62%, none at events with no tier; 4,674 matches, measured 17 October 2026). Check data_available.lineups per match. Where there is none, the whole-match stat rows say who played.
  • tier on tournaments: ~96% populated, S through C.

What isn't there

  • Per-book odds. One aggregate line, never individual bookmaker prices or names.
  • The model line. Empty until it clears its gate — check /v1/cs2/model/metrics.
  • Team logos. logo_url is always empty; no redistribution rights.
  • Sub-second live ticks. The WebSocket pushes at ingestion cadence.

Known limitations has the full list.

Checking depth for yourself

Rather than trusting a figure in a doc that could go stale, count:

# How many completed matches have round-level data?
curl -H "Authorization: Bearer $ESPORTSODDS_API_KEY" \
  "https://api.esportsodds.gg/v1/cs2/matches?status=completed&has=rounds&limit=1"

?has= accepts any subset of maps,stats,map_stats,vetoes,rounds,depth,lineups,odds, so you can size the population that actually supports your analysis before committing to it.

To see what one fully covered match looks like, every layer from the match row down to single rounds, read sample responses.

On this page