---
title: CS2 API
description: CS2 esports data — tournaments, teams with ELO and map pools, player stats, matches with map-by-map scores and predictions.
badge: pro
---

# CS2 API

The CS2 API covers competitive Counter-Strike 2: tournaments, teams (with ELO
ratings, form and map pools), players (K/D, damage, clutch stats), matches
with map-by-map scores, and model-based win predictions.

Base URL: `https://sports.bzzoiro.com/csgo/api/v2/`

## Access

Requires the **Sports Addon** ($5/month) — it unlocks the tennis, CS2, darts,
hockey, basketball and horse racing APIs plus their MCP servers. Get it at
[/addons/](/addons/). You also need a free account token from
[/register/](/register/).

```bash
Authorization: Token YOUR_API_KEY
```

Without a token you get **401**:

```json
{"error": "authentication required", "code": "authentication_required",
 "detail": "Send Authorization: Token <YOUR_TOKEN> on every request.",
 "register": "https://sports.bzzoiro.com/register/"}
```

With a token but no addon you get **402**:

```json
{"error": "Sports Addon required", "code": "addon_required",
 "detail": "Tennis, CS:GO, darts, hockey, basketball and horse racing APIs require the Sports Addon ($5/mo).",
 "pricing_url": "https://sports.bzzoiro.com/pricing/"}
```

List endpoints paginate with `limit` (default 50, max 200) and `offset`, and
return `{count, next, previous, results}`.

## Endpoints

| Endpoint | Description |
|---|---|
| `GET /csgo/api/v2/tournaments/` | List tournaments |
| `GET /csgo/api/v2/tournaments/{id}/` | Tournament detail |
| `GET /csgo/api/v2/teams/` | List/search teams with ELO and winrate |
| `GET /csgo/api/v2/teams/{id}/` | Team detail: map pool, round winrates, form |
| `GET /csgo/api/v2/players/` | List/search players |
| `GET /csgo/api/v2/players/{id}/` | Player detail: K/D, damage, clutches |
| `GET /csgo/api/v2/matches/` | List matches |
| `GET /csgo/api/v2/matches/live/` | Matches in play right now |
| `GET /csgo/api/v2/matches/{id}/` | Match detail with map-by-map scores |
| `GET /csgo/api/v2/matches/{id}/odds/` | Per-bookmaker odds, every market |
| `GET /csgo/api/v2/matches/{id}/stats/` | Pre-match team form, map pool, head-to-head |
| `GET /csgo/api/v2/matches/{id}/player-stats/` | Per-player form with percentiles |
| `GET /csgo/api/v2/predictions/` | Model win probabilities |
| `GET /csgo/api/v2/predictions/{id}/` | One prediction |

The machine-readable schema is public at `/csgo/api/schema/`, with an
interactive UI at [/csgo/api/docs/](/csgo/api/docs/) and a themed explorer at
[/docs/explorer/csgo/](/docs/explorer/csgo/).

## Tournaments

**Query parameters**

| Param | Type | Description |
|---|---|---|
| `include_inactive` | bool | Include finished/inactive tournaments |
| `limit` / `offset` | int | Pagination (default 50, max 200) |

Responses are cached ~5 minutes.

```json
{"count": 12, "results": [
  {"id": 87, "name": "IEM Cologne 2026", "start_date": "2026-07-22",
   "end_date": "2026-08-03", "prize_pool": "$1,000,000"}
]}
```

## Teams

**Query parameters**

| Param | Type | Description |
|---|---|---|
| `search` | string | Name search |
| `country` | string | ISO country code |
| `limit` / `offset` | int | Pagination |

Cached ~5 minutes.

```bash
curl -H "Authorization: Token YOUR_API_KEY" \
  "https://sports.bzzoiro.com/csgo/api/v2/teams/?search=vitality"
```

```json
{"count": 1, "results": [
  {"id": 342, "name": "Team Vitality", "short_name": "VIT",
   "country_code": "FR", "country": "France",
   "elo_rating": 2144, "win_rate": 0.71}
]}
```

`GET /teams/{id}/` adds the map pool (per-map winrates, picked/banned rates),
round winrates by side (T/CT), pistol-round winrate and recent form.

## Players

**Query parameters**

| Param | Type | Description |
|---|---|---|
| `search` | string | Name/nickname search |
| `team` | int | Filter by team id |
| `limit` / `offset` | int | Pagination |

`GET /players/{id}/` returns career aggregates: K/D ratio, average damage per
round, headshot rate, opening kills, AWP usage and clutch stats.

```json
{"id": 1874, "nickname": "ZywOo", "name": "Mathieu Herbaut",
 "country_code": "FR", "team": {"id": 342, "name": "Team Vitality"},
 "kd_ratio": 1.32, "avg_damage": 86.4, "headshot_rate": 0.42,
 "opening_kills_per_round": 0.14, "clutch_win_rate": 0.38}
```

## Matches

**Query parameters**

| Param | Type | Description |
|---|---|---|
| `tournament` | int | Filter by tournament id |
| `team` | int | Matches involving this team id |
| `status` | string | `notstarted`, `inprogress`, `finished`, `postponed`, `cancelled` |
| `date_from` / `date_to` | date | `YYYY-MM-DD`. Without either, the list returns the next 7 days, oldest first. Any of the filters above lifts that window and returns the full archive, newest first. |
| `limit` / `offset` | int | Pagination |

```bash
curl -H "Authorization: Token YOUR_API_KEY" \
  "https://sports.bzzoiro.com/csgo/api/v2/matches/?team=342&status=finished"
```

```json
{"count": 18, "results": [
  {"id": 5531, "tournament": {"id": 87, "name": "IEM Cologne 2026"},
   "home_team": {"id": 342, "name": "Team Vitality", "short_name": "VIT"},
   "away_team": {"id": 199, "name": "FaZe Clan", "short_name": "FAZE"},
   "start_time": "2026-08-01T18:00:00+00:00", "status": "finished",
   "home_score": 2, "away_score": 1, "best_of": 3, "stage": "Semifinal"}
]}
```

`GET /matches/live/` returns in-play matches only (cached 30 seconds).
`GET /matches/{id}/` adds `maps[]` — one entry per map with the map name and
per-map round scores:

```json
{"id": 5531, "best_of": 3, "home_score": 2, "away_score": 1,
 "maps": [
   {"map": "Inferno", "home_rounds": 13, "away_rounds": 7},
   {"map": "Mirage", "home_rounds": 10, "away_rounds": 13},
   {"map": "Nuke",   "home_rounds": 13, "away_rounds": 11}
 ]}
```

## Odds

`GET /csgo/api/v2/matches/{id}/odds/` returns every price we hold for one match: the
match-winner market flattened per bookmaker, and below it every other market the
books offered, grouped by kind, line and period.

```bash
curl -H "Authorization: Token YOUR_API_KEY" \
  "https://sports.bzzoiro.com/csgo/api/v2/matches/8980/odds/"
```

```json
{"match_id": 8980, "start_time": "2026-07-09T13:00:00+00:00",
 "home_team_name": "FaZe", "away_team_name": "MOUZ",
 "bookmakers_count": 16, "source": "multi",
 "bookmakers": [
   {"bookmaker": "Bet365", "bookmaker_slug": "bet365",
    "odds_home": 1.47, "movement_home": "STABLE",
    "odds_away": 2.59, "movement_away": "STABLE",
    "updated_at": "2026-07-09T10:04:43Z"}
 ],
 "markets": [
   {"market_kind": "OU", "market_family": "OU", "market_line": 2.5,
    "market_period": "FT", "selections": ["OVER", "UNDER"],
    "bookmakers": [
      {"bookmaker": "Bet365", "bookmaker_slug": "bet365",
       "prices": {"OVER": {"price": 2.02, "movement": "STABLE"},
                  "UNDER": {"price": 1.74, "movement": "STABLE"}},
       "updated_at": "2026-07-09T10:04:45Z"}
    ]}
 ]}
```

| Field | Meaning |
|---|---|
| `source` | `multi` per-bookmaker prices, `consensus` the single stored price when the match is not yet linked to a priced feed event, `none` when nobody has priced it |
| `bookmakers_count` | Bookmakers with at least one match-winner price |
| `movement_*` | `SHORTENING`, `DRIFTING` or `STABLE` against the previous price we saw; `null` when there is no previous price |
| `market_kind` | Market type: `WINNER`, `AH` (map handicap), `OU` (total maps or rounds) |
| `market_family` | Distinguishes markets sharing a kind (total goals vs corners vs a single side's total) |
| `market_line` | The handicap or total the market is quoted on; `null` for a plain winner market |
| `market_period` | `FT` for the full match; period codes otherwise |

> Prices come from several sources and are pooled per match. A bookmaker appears
> once per market with its most recent price.

## Predictions

**Query parameters**

| Param | Type | Description |
|---|---|---|
| `upcoming` | bool | Only matches not yet started |
| `date_from` / `date_to` | date | `YYYY-MM-DD` |
| `limit` / `offset` | int | Pagination |

```json
{"count": 9, "results": [
  {"id": 2210, "match_id": 5544,
   "home_win_prob": 0.58, "away_win_prob": 0.42,
   "predicted_winner_id": 342, "confidence": 0.58}
]}
```

## Match stats

`GET /matches/{id}/stats/` returns how both sides were playing going into the
match: round win rates split by side, pistol and opening-duel conversion, the
map pool each team brings with its win rate per map, and the head-to-head.

```bash
curl -H "Authorization: Token YOUR_API_KEY" \
  "https://sports.bzzoiro.com/csgo/api/v2/matches/9630/stats/"
```

```json
{"match_id": 9630,
 "teams": [
   {"team": {"id": 723, "name": "The Last Resort", "short_name": "TLR"},
    "map_winrate": 41.4, "map_sample_size": 29,
    "round_winrate_all": 47.0, "round_winrate_t": 42.9, "round_winrate_ct": 51.5,
    "pistol_winrate": 48.3, "headshot_rate": 49.0, "opening_conversion": 50.4,
    "first_to_rounds": {"3": 44.8, "6": 41.4, "9": 41.4, "12": 37.9},
    "kd_ratio": 0.94, "avg_damage": 74.1, "form_results": ["L", "W", "L", "L", "W"]}
 ],
 "map_pool": [
   {"team": {"id": 723, "name": "The Last Resort"},
    "maps": [{"map_name": "Mirage", "winrate": 61.0, "type": "picked"}]}
 ],
 "head_to_head": {"total_matches": 4, "home_wins": 3, "away_wins": 1,
                  "recent_matches": []}}
```

`GET /matches/{id}/player-stats/` returns the same idea per player — kills,
deaths, damage, opening kills, utility and clutch rounds — each with the
percentile that player sits in, so a number can be read without knowing what
good looks like:

```json
{"match_id": 9630, "players": [
  {"nickname": "cadiaN", "real_name": "Casper Møller",
   "team": {"id": 486, "name": "Team OG", "short_name": "OG"},
   "avg_kills": 0.67, "avg_deaths": 0.62, "kd_ratio": 1.07, "kd_percentile": 71.0,
   "avg_damage": 79.4, "damage_percentile": 68.0,
   "avg_clutch_rounds": 0.08, "clutch_percentile": 62.0,
   "favourite_weapons": []}
]}
```

Both return empty lists for matches with no stats collected — mostly tier-3
fixtures and matches added within a few hours of starting.

## Coverage and off-season

Tournaments run all year but not every week has one, and the schedule is usually published only days ahead.

`GET /api/v2/coverage/?sport=csgo` says so directly, without a token:

```json
{"sport": "csgo", "status": "in_season", "events_next_7d": 6,
 "events_next_30d": 6, "priced_next_7d": 0, "live_now": 0,
 "next_event_at": "2026-08-20T08:00:00+00:00", "last_event_at": "2026-08-19T16:00:00+00:00"}
```

`status` is `in_season` (fixtures within seven days), `between_events` (nothing
this week, something later), `off_season` (nothing ahead and the last event is
more than a fortnight old) or `no_fixtures`. Drop the `?sport=` to get every
sport in one call — worth doing before a polling loop decides what to ask for.
