Maps

How each team fares on one map

How each team fares on one map over the trailing `window`: maps played and won, the team's own picks and bans of the map, `veto_matches` (its matches with a recorded veto — the denominator for picks and bans) and `permaban` (the rule `/teams/{id}/vetoes` uses). A team is listed when it has at least 10 maps played on the map or at least 10 picks plus bans of it in the window, so a map a team never plays because it always bans it still lists that team. Most played first, then most chosen. The result is computed once per ten minutes per parameter set and shared across callers. `limit` default 100, maximum 200. Enveloped `{"data": [MapTeamStat, ...], "meta": {...}}`.

GET
/v1/{game}/maps/{id}/teams

How each team fares on one map over the trailing window: maps played and won, the team's own picks and bans of the map, veto_matches (its matches with a recorded veto — the denominator for picks and bans) and permaban (the rule /teams/{id}/vetoes uses).

A team is listed when it has at least 10 maps played on the map or at least 10 picks plus bans of it in the window, so a map a team never plays because it always bans it still lists that team. Most played first, then most chosen. The result is computed once per ten minutes per parameter set and shared across callers. limit default 100, maximum 200. Enveloped {"data": [MapTeamStat, ...], "meta": {...}}.

Authorization

AuthorizationBearer <token>

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*string

Game slug. cs2 is the only populated title today; an un-onboarded game 404s cleanly.

id*string

The map's name, with or without the de_ prefix and in any case: dust2 and de_dust2 are the same map. A name that is not a competitive map is a 404 not_found.

Query Parameters

window?string

The trailing span of matches the figures cover: a whole number of months with an m suffix, such as 6m, up to 60m, or all for the whole history. Default 6m. Anything else is a 400 naming the parameter.

Default"6m"
era?string

Which Counter-Strike era the matches come from. cs2 keeps only Counter-Strike 2 matches (a match with no recorded version counts as Counter-Strike 2 when it was played on or after 2023-10-16); all (the default) keeps every match.

Default"all"

Value in

  • "cs2"
  • "all"
limit?integer

How many teams to return. Default 100, maximum 200. Not paginated.

Range1 <= value <= 200
Default100

Response Body

application/json

application/json

application/json

application/json

application/json

curl -X GET "https://example.com/v1/cs2/maps/dust2/teams"
{  "data": [    {      "team": {        "id": "019f23d1-fb5c-7605-9cec-10e99a87253d",        "name": "GenOne",        "slug": "genone"      },      "played": 76,      "wins": 50,      "picks": 70,      "bans": 0,      "veto_matches": 96,      "permaban": false    },    {      "team": {        "id": "019f23d1-fb5c-7b79-8e3c-7483ce9bb9b4",        "name": "SPARTA",        "slug": "sparta"      },      "played": 62,      "wins": 42,      "picks": 51,      "bans": 0,      "veto_matches": 77,      "permaban": false    },    {      "team": {        "id": "019f8941-5a46-76f8-86f3-f84b7c3d940f",        "name": "Black Phoenix",        "slug": "black-phoenix"      },      "played": 49,      "wins": 34,      "picks": 41,      "bans": 0,      "veto_matches": 53,      "permaban": false    }  ],  "meta": {    "count": 3,    "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": "rate_limited",    "message": "rate limit exceeded",    "request_id": "019f3d18-c15f-7319-81a7-343e8a80a578"  }}

Mint a short-lived WebSocket ticket POST

Exchanges the caller's API key for a short-lived (60s) signed ticket used to open the WebSocket at `wss://api.esportsodds.gg/v1/ws?token=...`. Keeps the raw key off the wire and out of the browser — browsers can't set headers on a `WebSocket`, which is why tickets exist. Minting is authenticated and rate-limited — each mint draws a token from the key's per-second bucket, like any other call, so reconnect with backoff — but it is **not metered**: a mint consumes no request quota. This is the only part of the WebSocket surface expressible in OpenAPI: the socket itself (`/v1/ws`) is a persistent connection with its own message protocol, so it is documented as prose instead — see **[Live data & WebSocket](/docs/live-data)** for the handshake, the message envelope, subscribing, sequence numbers, reconnection and the connection cap. Read that page before building against this endpoint; a ticket on its own does nothing.

League-wide figures for each competitive map GET

Each competitive map's league-wide figures over the trailing `window`: maps played (a map played out — technical results and walkovers are not counted), the average number of rounds, the rounds played and won on each side, and the bans and picks recorded in the vetoes whose sequence included the map. `veto_matches` is the number of those matches. A map added to the pool recently has fewer, so read a map's bans and picks against its own `veto_matches`, never against another map's. A veto choice counts only on a competitive map and only when its acting team is one of the match's two sides. Every map in the Active Duty pool is listed, with zeros before its first map, plus any retired map still played in the window. Most played first. The result is computed once per ten minutes per parameter set and shared across callers. Enveloped `{"data": [MapStat, ...], "meta": {...}}`.