Get a key

Live in-game

Mid-map K/D/A and sport-native state. Redis-backed telemetry — not the finished-match boxscore path. MCP: get_live_boxscore. CS2 money/bomb, LoL gold/towers, Dota gold, Deadlock gold/CS/level on tournament games. Mobile Legends live lists can be empty. Sandbox: 30-60 second delay (all sports). Hobby: 5 second delay (all sports). Builder+: real-time, under 2 seconds. Live odds below are Hobby+ (Sandbox is blocked); the WebSocket wire stays Builder+.

Live plans at a glance

Every plan sees the same list of live matches. Plans differ in how fresh the in-game data is and in how many WebSocket connections and subscriptions you can hold.

FeatureSandboxHobbyBuilderPro
Live Match ListSame on all plansSame on all plansSame on all plansSame on all plans
Data Delay30-60 seconds5 secondsReal-timeReal-time
WebSocket ConnectionsNoneNone210
WebSocket SubscriptionsNoneNone525
Subscribe To All GamesNoNoNoYes

A connection is one open WebSocket. A subscription is one match you follow on it, so Builder's 5 means five matches at the same time across your connections, and Pro's 25 means twenty-five. Delay is how old the data is when we serve it. Pro can also subscribe to every live game at once.

What has_live_stats means

Each game in the live list carries has_live_stats. It is true when we are publishing a fresh live scoreboard for that match right now: player kills, deaths, assists, health, money and weapons, plus bomb and round state. It is false when the match is live but no in-game data source covers it. That is common for smaller regional events. The match is still listed, with its score and live odds, so you always know it is being played. Check has_live_stats before you subscribe, and use the coverage endpoint below to see how often each tournament and tier has it.

Stats pause for a few minutes between maps. For 15 minutes after the last board, has_live_stats stays true and live_state says why: in_map while boards are publishing, between_maps while they are paused. It is null when there are no stats. last_frame_at is the time of the last board. The boxscore endpoint returns the same two fields, and its reason reads between_mapswhile paused. A dashboard can show "paused" instead of "broken". Paused samples are not counted as missing stats in the coverage numbers.

A listed game never comes back as an empty success. Every game also carries stats_available, which matches has_live_stats, and a reason when it is false: source_stats_disabled means the official stats feed has turned stats off for that game, and no_live_coverage means no in-game source covers the match. We re-check a disabled game every few minutes. The boxscore endpoint and the WebSocket snapshot carry the same fields, and the WebSocket sends a statusupdate when a game's stats switch off or come back.

Live stats coverage

GET/v6/esports/cs2/live/coverage
ParamTypeRequiredDescription
daysquery—Days back, 1 to 14. Default 14.

Share of listed CS2 matches that had live stats, by tournament and by tier. A match counts once it had a live scoreboard at any point. Coverage only includes matches logged since tracking_since.

cURLGET
curl -H "X-API-Key: YOUR_API_KEY" \
  "https://kashrock.up.railway.app/v6/esports/cs2/live/coverage"
200 · coverage
{
  "sport": "cs2",
  "window_days": 14,
  "tracking_since": "2026-10-01T00:00:00+00:00",
  "matches": 120,
  "matches_with_live_stats": 54,
  "coverage_pct": 45,
  "tiers": [
    {
      "tier": "s",
      "matches": 20,
      "matches_with_live_stats": 20,
      "coverage_pct": 100
    },
    {
      "tier": "c",
      "matches": 100,
      "matches_with_live_stats": 34,
      "coverage_pct": 34
    }
  ],
  "tournaments": [
    {
      "tournament": "CCT Europe 2026 Series 10",
      "tier": "b",
      "matches": 12,
      "matches_with_live_stats": 12,
      "coverage_pct": 100
    }
  ]
}

List live games

GET/v6/esports/{sport}/live/games
ParamTypeRequiredDescription
sportpathYescs2, valorant, lol, dota2, cod, r6, mlbb, deadlock
cURLGET
curl -H "X-API-Key: YOUR_API_KEY" \
  "https://kashrock.up.railway.app/v6/esports/cs2/live/games"
200 · live
{
  "sport": "cs2",
  "total": 1,
  "games": [
    {
      "game_id": "kr_cs2_voca-vs-liquid-23-09-2026",
      "match_id": "kr_cs2_voca-vs-liquid-23-09-2026",
      "kr_match_id": "kr_cs2_voca-vs-liquid-23-09-2026",
      "blue_code": "Voca",
      "red_code": "Liquid",
      "league": "CCT Europe 2026 Series 10",
      "has_live_stats": true,
      "stats_available": true,
      "reason": null,
      "live_state": "in_map",
      "last_frame_at": "2026-09-23T18:04:41.870+00:00",
      "streams": [
        {
          "platform": "kick",
          "channel": "cct_cs2",
          "url": "https://kick.com/cct_cs2",
          "embed_url": "https://player.kick.com/cct_cs2",
          "language": "en",
          "official": true,
          "name": "CCT CS2",
          "viewers": 1112
        }
      ]
    }
  ]
}

Live boxscore

GET/v6/esports/{sport}/live/{game_id}/boxscore
ParamTypeRequiredDescription
sportpathYesSame sport slugs as matches.
game_idpathYesThe kr_match_id from Live Games — the same id Matches returns. Numeric ids return 400.

boxscore is the map being played. mapslists every map this series already finished, with each player's final stats on that map, so map 1 stays in the response after map 2 starts. These rows are marked provisionaluntil the match history has the map, and a map that ended inside your plan's delay appears once the delay has passed. winner_side is set when the series score proves who won the map.

Each title carries only its own stats. Dota 2 has no ADR or headshots, LoL has no rounds, and a stat nobody measured is left out rather than shown as 0. columnslists the title's stat columns in display order. Each column has a key (a path into a player row, such as advanced_stats.kast), a label and a format (int, decimal1, decimal2, ratio_percent, percent or text). Build your table from it, and show a blank when a row has no value for a column.

cURLGET
curl -H "X-API-Key: YOUR_API_KEY" \
  "https://kashrock.up.railway.app/v6/esports/cs2/live/kr_cs2_voca-vs-liquid-23-09-2026/boxscore"
200 · live
{
  "sport": "cs2",
  "game_id": "kr_cs2_voca-vs-liquid-23-09-2026",
  "boxscore": {
    "map_name": "Anubis",
    "round": 16,
    "round_phase": "bomb",
    "round_clock": "0:07",
    "blue": {
      "map_score": 10,
      "kills": 69
    },
    "red": {
      "map_score": 5,
      "kills": 44
    },
    "board": {
      "sides": {
        "blue": "CT",
        "red": "T"
      },
      "bomb_planted": true
    },
    "players": [
      {
        "nickname": "Jorko",
        "side": "blue",
        "kills": 17,
        "deaths": 7,
        "assists": 2,
        "alive": true,
        "health": 84,
        "money": 2400,
        "weapon": "m4a1_silencer"
      },
      {
        "nickname": "dare",
        "side": "red",
        "kills": 10,
        "deaths": 14,
        "assists": 2,
        "alive": false,
        "health": 0,
        "money": 1300
      }
    ]
  },
  "maps": [
    {
      "map_number": 1,
      "map_name": "Ancient",
      "status": "finished",
      "provisional": true,
      "stats_source": "live",
      "blue_score": 13,
      "red_score": 9,
      "winner_side": "blue",
      "players": [
        {
          "nickname": "Jorko",
          "side": "blue",
          "kills": 21,
          "deaths": 12,
          "assists": 4,
          "headshots": 11,
          "adr": 92.4
        },
        {
          "nickname": "dare",
          "side": "red",
          "kills": 14,
          "deaths": 17,
          "assists": 3,
          "headshots": 6,
          "adr": 71.8
        }
      ]
    }
  ],
  "columns": [
    {
      "key": "kills",
      "label": "Kills",
      "format": "int"
    },
    {
      "key": "deaths",
      "label": "Deaths",
      "format": "int"
    },
    {
      "key": "assists",
      "label": "Assists",
      "format": "int"
    },
    {
      "key": "adr",
      "label": "ADR",
      "format": "decimal1"
    },
    {
      "key": "headshots",
      "label": "Headshots",
      "format": "int"
    },
    {
      "key": "kpr",
      "label": "KPR",
      "format": "decimal2"
    },
    {
      "key": "first_kills",
      "label": "First Kills",
      "format": "int"
    },
    {
      "key": "flash_assists",
      "label": "Flash Assists",
      "format": "int"
    },
    {
      "key": "advanced_stats.kast",
      "label": "KAST",
      "format": "ratio_percent"
    },
    {
      "key": "advanced_stats.rating",
      "label": "Rating",
      "format": "decimal2"
    }
  ]
}

Frame & events

Also: /{sport}/live/{game_id}/frames, /{sport}/live/{game_id}/events.

CS2 events name the map and round. Kills carry the killer, victim, weapon, headshot flag and assist. Bomb events say what happened: BOMB_PLANT, BOMB_DEFUSE or BOMB_EXPLODE. Each round win states how it ended in win_type: bomb_defused, bomb_exploded, elimination or time_expired. Sides are blue or red, matching the match teams. The board shows which team plays CT in board.sides. Health, money and weapon are left out when the source did not send them, never filled with zeros. Some matches only have team-level kill counts instead of per-kill detail.

200 · events
{
  "sport": "cs2",
  "game_id": "kr_cs2_voca-vs-liquid-23-09-2026",
  "total": 3,
  "events": [
    {
      "type": "KILL",
      "side": "blue",
      "map": 1,
      "round": 16,
      "killer": "Jorko",
      "victim": "dare",
      "killer_side": "blue",
      "victim_side": "red",
      "weapon": "ak47",
      "headshot": true,
      "assist": null,
      "info": "Jorko killed dare with ak47 (headshot)",
      "ts": "2026-09-23T18:04:11.402+00:00"
    },
    {
      "type": "BOMB_PLANT",
      "side": "red",
      "map": 1,
      "round": 16,
      "player": "dare",
      "site": "A",
      "info": "dare planted the bomb",
      "ts": "2026-09-23T18:04:20.115+00:00"
    },
    {
      "type": "BOMB_DEFUSE",
      "side": "blue",
      "map": 1,
      "round": 16,
      "player": "Jorko",
      "info": "Jorko defused the bomb",
      "ts": "2026-09-23T18:04:41.870+00:00"
    }
  ]
}

Finished series stats stay on /{sport}/matches/{slug}/boxscore (MCP get_boxscore).

Shifting betting odds (live & prematch)

Real-time shifting odds across sportsbooks (Bovada, Thunderpick, Cloudbet, BetRivers, Pinnacle, Courtside, Betway, Dexsport, Picklebet, 1xBet, BetOnline, Bet365) + prediction markets (Kalshi, Polymarket) with automated shift detection. Filter by match state with status=all|live|prematch.

GET/v6/esports/{sport}/live/odds
ParamTypeRequiredDescription
sportpathYescs2, lol, dota2, valorant, …
statusquery—Filter: all (default) | live | prematch
cURLGET
curl -H "X-API-Key: YOUR_API_KEY" \
  "https://kashrock.up.railway.app/v6/esports/cs2/live/odds?status=all"
200 · odds games
{
  "sport": "cs2",
  "status": "all",
  "total": 31,
  "games": [
    {
      "kr_match_id": "kr_cs2_sashi-vs-color-14-09-2026",
      "match_slug": "sashi-vs-color-14-09-2026",
      "home_team": "Sashi",
      "away_team": "Color",
      "status": "live",
      "score": {
        "home": "0",
        "away": "1",
        "current_period": 2
      },
      "open_markets": 134,
      "shifts_count": 100,
      "stream_url": "https://player.kick.com/cct_cs2",
      "updated_at": "2026-09-14T14:07:25.205791+00:00"
    },
    {
      "kr_match_id": "kr_cs2_ecstatic-vs-linx-legaxy-14-09-2026",
      "match_slug": "ecstatic-vs-linx-legaxy-14-09-2026",
      "home_team": "Ecstatic",
      "away_team": "Linx Legaxy",
      "status": "prematch",
      "event_time": "2026-09-14T17:00:00.000Z",
      "books": [
        "betwinner",
        "novibet",
        "onexbet"
      ],
      "open_markets": 1,
      "shifts_count": 0,
      "updated_at": "2026-09-14T14:01:49.088378+00:00"
    }
  ]
}

Match odds & market tree

GET/v6/esports/{sport}/live/{game_id}/odds
ParamTypeRequiredDescription
sportpathYesSport key.
game_idpathYesKashRock slug or kr_match_id.
cURLGET
curl -H "X-API-Key: YOUR_API_KEY" \
  "https://kashrock.up.railway.app/v6/esports/cs2/live/kr_cs2_sashi-vs-color-14-09-2026/odds"
200 · match odds
{
  "sport": "cs2",
  "match_slug": "sashi-vs-color-14-09-2026",
  "kr_match_id": "kr_cs2_sashi-vs-color-14-09-2026",
  "status": "live",
  "book": "thunderpick",
  "total_open_markets": 134,
  "shifts_count": 100,
  "markets": [
    {
      "id": "kr_mkt_01c5bb1f916e",
      "name": "Match Winner",
      "market_type": "match_winner",
      "selections": [
        {
          "id": "kr_sel_ed3628a4e1c8",
          "name": "Sashi",
          "odds": 2.15,
          "american_odds": 115,
          "movement": "steady"
        },
        {
          "id": "kr_sel_df1f329dc5fb",
          "name": "Color",
          "odds": 1.62,
          "american_odds": -161,
          "movement": "steady"
        }
      ]
    }
  ],
  "updated_at": "2026-09-14T14:07:25.205791+00:00"
}

Odds shift history (audit log)

In-play Redis shift log for the current session (about four minutes of ticks, capped). This is not opening/closing moneyline, totals, or spreads — those live on GET /v6/esports/{sport}/matches/{slug}/lines/history.

GET/v6/esports/{sport}/live/{game_id}/odds/history
ParamTypeRequiredDescription
sportpathYesSport key.
game_idpathYesKashRock slug or kr_match_id.
cURLGET
curl -H "X-API-Key: YOUR_API_KEY" \
  "https://kashrock.up.railway.app/v6/esports/cs2/live/kr_cs2_sashi-vs-color-14-09-2026/odds/history"
200 · shift history
{
  "sport": "cs2",
  "game_id": "kr_cs2_sashi-vs-color-14-09-2026",
  "total_shifts": 1,
  "history": [
    {
      "market_id": "kr_mkt_9591238e08b8",
      "market_name": "Map winner & Total rounds - Map 3",
      "selection_id": "kr_sel_16a39ee198dc",
      "selection_name": "Color - Over",
      "prev_odds": 4.4,
      "new_odds": 3.5,
      "odds_delta": -0.9,
      "movement": "shortening",
      "prev_line": 22.5,
      "new_line": 21.5,
      "ts": "2026-09-14T14:07:25.205454+00:00"
    }
  ]
}

WebSocket wire

Push updates under 2 seconds after Redis write. Builder+. Auth via ?api_key= or Bearer on handshake.

GETWS /v6/esports/live/ws
{"op":"subscribe","sport":"cs2","game_id":"kr_cs2_voca-vs-liquid-23-09-2026"}
{"op":"subscribe","sport":"cs2","game_id":"*"}
{"op":"unsubscribe","sport":"cs2","game_id":"kr_cs2_voca-vs-liquid-23-09-2026"}
{"op":"ping"}

Server pushes:

  • hello — plan, ws_limits, latency SLA
  • snapshot — boxscore + meta + odds for one game
  • games_list — when subscribed to game_id=*
  • frame / boxscore / meta / event / odds — live ticks
  • pong — reply to ping

HTTP twins: GET /v6/esports/cs2/live/games, GET /v6/esports/cs2/live/{game_id}/boxscore. Same kr_match_id everywhere; numeric ids are rejected. Health: GET /v6/esports/live/healthreturns measured latency for every live game: seconds from the source's own frame time to our WebSocket publish, as p50 and p95.

Finished series stats stay on /{sport}/matches/{slug}/boxscore (MCP get_boxscore).