Core concepts

The data model

How games, tournaments, matches, maps, rounds and players relate — and which endpoint returns each.

Everything in the API hangs off six entities. Knowing which one owns a fact tells you which endpoint to call.

Game (cs2)
 └─ Tournament            /v1/cs2/tournaments
     └─ Match             /v1/cs2/matches
         ├─ Map result    /v1/cs2/matches/{id}/maps
         │   └─ Round     /v1/cs2/matches/{id}/rounds
         ├─ Veto step     /v1/cs2/matches/{id}/vetoes
         ├─ Player stats  /v1/cs2/matches/{id}/stats
         └─ Depth         /v1/cs2/matches/{id}/depth
Team                      /v1/cs2/teams
Player                    /v1/cs2/players

The entities

Game is the namespace segment in every path. cs2 is the only populated title today, but the shape is game-agnostic by construction — a second title extends the same paths rather than replacing them. A game that isn't onboarded returns 404 unknown_game.

Tournament is an event. It has dates, a tier, a prize pool and a region; its status (upcoming/ongoing/finished) is derived from its dates, not stored, so a tournament with no known start date has a null status and is excluded by any status filter.

Match is one fixture between two teams. format (bo1/bo3/bo5) tells you how many maps to expect at most. The two sides are team_a and team_b — stable slots, not home/away; CS2 has no home side.

Map result is one map within a series, numbered from 1. Rounds are per-team rows within a map — two per round, one for each side.

Team and Player are the competitors. A player's team_id is their current team; the team they played for in a specific match is on that match's stat line, which is not always the same.

Which id joins to which

Everything is a UUIDv7. The joins that matter:

  • A match carries team_a_id, team_b_id, tournament_id.
  • A map result, veto, stat line, round and depth row all carry match_id.
  • A stat line and a round both carry map_number, so they join to a map result on (match_id, map_number).
  • A stat line's map_number is null for the whole-match aggregate. Filter for null to avoid double-counting a player across per-map and aggregate rows.

List rows vs detail rows

A list row is denormalised for display: a match from /v1/cs2/matches carries team_a_name, team_b_name, team_a_short, team_b_short and tournament_name alongside the ids.

A detail row is not: /v1/cs2/matches/{id} returns bare ids. Ask for the related resources with ?include=teams,tournament rather than making three more requests. This catches people who prototype against the list then switch to detail — see fetching a full match.

What isn't exposed

Markets and market types are modelled internally but have no endpoint of their own — you get a market_id on each odds line, which groups lines belonging to the same proposition. Per-book bookmaker prices are never served in any form; see the market line.

On this page