# KashRock for AI agents KashRock is a live esports data API: DFS props, 18 sportsbook and prediction-market line books, consensus lines, parlay pricing and +EV discovery, market-movement (steam/shift/volume/whale) plays, team and player research, and Builder history tape. One schema everywhere — a match, team, player, market, or book looks identical on every endpoint. ## Coverage - Titles: CS2, Valorant, League of Legends, Dota 2, Call of Duty, Rainbow Six Siege, Mobile Legends: Bang Bang, Deadlock, Apex Legends — matches, box scores, and player stats. (Apex: rankings, stats and match prep; no props/lines yet.) - Player props (DFS): prizepicks, underdog, parlayplay, dabble, betr, boom, pick6, sleeper. - Team lines / parlays / flow books: 1xbet, bet365, betonline, betrivers, betway, bovada, cloudbet, courtside, dexsport, ggbet, kalshi, picklebet, pinnacle, polymarket, prizepicks, sleeper, thunderpick, underdog. Kalshi and Polymarket are prediction markets carrying team markets only. - Live player kills, deaths, and assists: within 2 seconds of our upstream source on Builder and Pro; 5-second delay on Hobby; 30-60 second delay on Sandbox. - Free key: 500 requests every UTC day, no card required. V7 is twelve routes under /v7: matches, props, lines, parlays, flow, live (sport=all, plus /v7/live/ws), players, teams, breakdown, history, results, and /v7/meta. A cache miss is `available: false` and a reason. Matches take `date` (YYYY-MM-DD), `team`, and `include` (`boxscore`, `prep`, `veto`, `maps`, `player_board`, `lines`, `streams`, `h2h`). Props take `board` (`dfs`, `main`, `all`), `view` (`groups`, `rows`, `both`), and `book`. Lines take `view` (`consensus`, `moneyline`), `market`, and `format` (`slim` default, `full`). With no `market`, lines return the main markets only (match_winner, map_winner, total_maps, team_total_maps, map_handicap, correct_score, dfs_team_pick). Round and kill markets need `market=` (round_handicap, total_rounds, kill_handicap, total_kills). Each lines event carries `kr_match_id` once the match is on our schedule, else `match_link: pending` with `match_link_reason`. A lines response is served from a prebuilt board when one is fresh and rebuilt from the live board otherwise, so a lagging prebuild is never reported as `stale`; `available: false` on lines means the board itself is not published yet. Parlays (`GET /v7/{sport}/parlays`, Hobby+) prices a multi-leg main-line ticket or discovers ranked independent parlays for one `book` (for example `polymarket`); pass `ticket=` as comma-separated `kr_match_id:market:selection` to price, or omit `ticket` to discover (`size` is 2 or 3); same-match / SGP is not supported; discover is +EV by default and `include=all` keeps negatives (same as flow). Each parlay carries `price_source` (`computed` until a book stores a native multi-leg price, else `book`) and `placeable_basis` (`combo_eligible` from Polymarket `comboStatus`, or `book_price` when only a single-leg price is known). Leg fair probability and EV exclude the leg's own book. Flow (`GET /v7/{sport}/flow`, Hobby+) returns money-behind, shift, and steam plays; `kind` is `all`, `volume`, `shift`, `steam`, or `whale`; volume/shift need a money floor and lopsided side share; shift is a ≥10-point side-share move inside 6 hours; whale returns `available: false` without trade-level data; `include=all` keeps negative-EV volume; `deeplink` matches `best_source` only. Live takes `game` (a kr_match_id) and `include` (`boxscore`, `events`, `frame`, `odds`, `odds_history`). Players take `sort=rank` and `include` (`research`, `gamelogs`, `stats`, `stats_full`, `history`, `media`). Teams take `id` (kr_tm_*) and `include` (`form`, `roster`, `maps`, `h2h`, `main_lines`, `upcoming`). Breakdown (`GET /v7/{sport}/breakdown`, Builder+) takes `match=` (slug/`kr_match_id`), `team=` (name/`kr_tm_*`), or `team1=` and `team2=` and returns one fact bundle (`publishable` gates thin pages). History `kind` is `contract` or `lines`. `props` is an alias of `contract`. Results take `from` and `to`. Meta `include` adds `boards`, `coverage`, `teams`, `tournaments`, or `changelog`. Call `/v7/meta` first: it returns sports, markets, your plan, a `routes` table (every endpoint with its params, what it answers, and minimum plan), and `books` where each book carries `kind` (`sportsbook`, `prediction_market`, `dfs_teams`, `dfs`) and `supports` (`lines`, `props`, `parlays`) — the book keys and market slugs it returns are the exact strings every other route accepts. `include=boards` adds, per sport, `lines` with `available`, `fresh`, `fetched_at`, and `books_on_board` (which books have live lines right now) — that answers "which books have data" in one call instead of probing `/lines` per sport. Unavailable bodies carry `available: false`, a `reason`, and a plain-English `hint` naming the next step; `book_not_on_board` also returns `books_on_board`, and a registered book that cannot serve the feed returns `book_not_supported` with `book_kind` (distinct from `unknown_book`). Every route accepts `fields`. The same data stays on the /v6 routes. MCP tools v7_matches, v7_props, v7_lines, v7_parlays, v7_flow, v7_live, v7_players, v7_teams, v7_breakdown, v7_history, v7_results, and v7_meta call those routes. The older tool names still call /v6. Prefer the MCP. Do not ask users to find HTTP endpoints or paste an API key. Call `login`, then `whoami` / `list_capabilities` / `suggest_build`. ## MCP setup ```json { "mcpServers": { "kashrock": { "command": "uvx", "args": ["kashrock-mcp"] } } } ``` Needs [uv](https://docs.astral.sh/uv/). Optional: `KASHROCK_API_KEY` skips login. API: https://kashrock.up.railway.app · Guide: https://www.kashrock.com/mcp Local fallback (repo checkout): ```json { "mcpServers": { "kashrock": { "command": "uv", "args": ["--directory", "/absolute/path/to/kashrock/mcp", "run", "kashrock-mcp"] } } } ``` ## Tools (tier-scoped) Sandbox (all sports, 500 req/UTC day): login, whoami, list_capabilities, suggest_build, list_sports, list_markets, explain_error, get_props, get_player_props, get_coverage, get_media, get_player_snapshot, search_players, get_player, get_player_stats, get_rankings, get_matches, get_match, search_matches. Live scores — get_live_games, get_live_boxscore, get_live_frame, get_live_events (every event carries its map number; every map of the series is included) — are 30-60s delayed (real delayed cache, not a relabeled timestamp). Match/player history — get_gamelogs, get_boxscore, get_results — is capped to the last 30 days. get_defense_vs_role (Dota 2 lanes, Valorant agent classes) is Sandbox-ready on the Last 10 window only; Hobby adds the last 90 days and all windows; Builder+ is full history. Hobby+: list_books, get_moneylines, get_lines (sportsbooks including Betway, Dexsport, Bet365, Picklebet, 1xBet, and BetOnline + DFS Teams + prediction markets; upcoming only by default — `include_started=true` adds matches already underway; markets include `total_rounds`, `round_handicap`, `total_kills`, `kill_handicap` and `correct_score` (a table of final scores, series such as 2-1 or one map such as 13-7, oriented to the home team; each score lists every book's own price and the best price, with no cross-book consensus); round markets state their scope, one map (`map_N`) or the whole series (`series`); grouped by `ot_included` so books with different overtime rules are never blended; an empty result carries `available: false` and a `reason`), get_gaps, get_player_stats_full, get_streams, get_team_h2h, research_board, research_board_tapes, research_player (player = kr_pl_* / slug / nick; empty tape is 200, not 404), get_live_odds, get_live_odds_history (in-play Redis ticks — not closing prints). Hobby's live scores are 5s delayed (not 30-60s) and its match/player history window is 90 days (not 30). Builder trial: each account can start one 3-day Builder trial from the pricing page (card required; converts to Builder at $99/mo unless cancelled). The trial upgrades the existing key in place; if cancelled or the payment fails, the same key returns to Sandbox limits. Builder+ full history / live wire: - Builder+ removes the delay (real-time live scores) and the date cap (full history vault) on every Sandbox/Hobby tool above. - get_team_matches, get_history_tape, get_lines_history — quote/odds history and full team schedules stay Builder+ only. - Live odds (get_live_odds, get_live_odds_history) are Hobby+, not Builder+ — only Sandbox is blocked from live odds. - Live odds are keyed by the same kr_match_id as /live/games and /matches; markets and selections carry KashRock ids (`kr_mkt_*`, `kr_sel_*`), stable for the match. - Live boards are sport-native: CS2 money/bomb/rounds, LoL gold/towers/barons, Dota gold/roshan when the HUD shows them, VAL/R6 round score, Deadlock gold/CS/level on tournament games. Live player rows carry the title's own stats under the title's own names (Dota 2: hero, last_hits, denies, gpm, xpm, total_gold, level) and `columns` lists what to render. `get_live_boxscore` is not finished-match history. - Live plans: the match list is identical on every plan. Delay is 30-60s (Sandbox), 5s (Hobby), real-time (Builder, Pro). WebSocket: none on Sandbox/Hobby, Builder 2 connections / 5 simultaneous match subscriptions, Pro 10 connections / 25 subscriptions plus `game_id=*`. - get_live_ws — live push WebSocket contract (`WS /v6/esports/live/ws`); connect from your app with API key; subscribe sport + game_id (kr_match_id)|* - Live game_id is always the kr_match_id (`kr_{sport}_team-a-vs-team-b-DD-MM-YYYY`), the same id `/matches` returns. Numeric ids still work until 2026-10-07 with `Deprecation`/`Sunset` headers, then are rejected with 400. - get_match_prep — team stats, mode winrates, map pool & side biases, key player matchups, betting insights (CS2, R6, COD, Valorant, and Apex) - get_player_board — player averages + favourite weapons (CS2) or agent/role board (Valorant) - get_player (LoL, Dota 2, Valorant) — profile from stored maps: totals, last-30/90-day windows, recent maps; `include=heroes` (HTTP `GET /v6/esports/{sport}/players/{player}?include=heroes`) adds the most-played champions (LoL), heroes (Dota 2) or agents (Valorant) with games, share, win rate and key stats. `win_rate` is null, never 0, when no map has a stored result; `games_with_result` says how many did. History follows your plan window (Sandbox 30 days, Hobby 90 days, Builder+ full). Every player with props or gamelogs resolves by nickname, slug or `kr_pl_*`. - get_tournament_maps — tournament maps for a KashRock match (picks/bans + side winrates) (CS2) - v7_breakdown — actionable match/team/H2H fact bundle (odds, form, map pool, starter props, hit rates, targets); HTTP `GET /v7/{sport}/breakdown`; published SEO pages are keyless at `GET /v7/{sport}/breakdown/published/{kind}/{slug}` and listed at `/breakdown/index` - get_match_veto — pre-match map veto/pick-ban sequence (order, team, action, map) (CS2, Valorant, R6); `veto_complete` is false before the veto has happened; an empty veto returns `available: false` with a `reason` (`veto_not_started`, `veto_not_published`, `provider_has_no_veto`, `match_not_indexed`, or `sport_not_supported`) - Finished matches from get_matches / get_match carry `stats_status`: `complete` (every map has player stats), `partial` (some maps still being filled), `unavailable` (no source has player stats for the remaining maps, so none will arrive), or null (not evaluated yet). Treat `unavailable` as final. - Every prop and line row carries `subject_type` (`player` -> `kr_pl_*`, `team` -> `kr_tm_*`, `match` -> the kr_match_id plus an `outcome_key` such as `win:kr_tm_*` or `score:3-2`, `combo` -> `kr_cb_*` with `subject_members`), `subject_id`, and `bet_key` (kr_match, market, scope, subject, outcome and line): rows from different books with the same `bet_key` are the same bet. `propId` is one book's prop and stays the same when the line moves: it is the book, match, market, scope, subject and `line_family` (the book's own class such as `main`, `goblin` or `demon`; in a ladder of lines quoted together the main line is the one priced closest to even and the others are `alt`). An `alt` line keeps its own `propId`. Each line also carries `line_prop_id`, which is graded on its own, so every line a prop carried has its own result. Older `propId` values keep resolving in history and results. `one_sided: true` marks a line the book offers on one side only (for example a higher-only DFS line): the other side is not missing data, the book does not take it. A combo prop ("A + B") has one `kr_cb_*` id on every book whatever order the names are listed in; get_player_combo (GET /v6/esports/{sport}/players/combo/{kr_cb}) returns its members and the sum of their stat per map. - Combos ("A + B" props) are never a player: a combo row has `subject_type: combo`, `subject_id: kr_cb_*` (the same id on every book, whatever order the names are listed) and `canonical_player_id: null`. `GET /v6/esports/{sport}/players/combo/{kr_cb}` answers for it; the old name-hash id that earlier stored rows carry as their player id resolves to the same combo on that route. - Match refs are KashRock only: `slug` (`team-a-vs-team-b-DD-MM-YYYY`) or `kr_match_id` from get_matches / get_match. Lists also emit `kr_tm_*` team ids and `kr_pl_*` player ids. Never foreign numeric ids. - Team names are KashRock's own display names on every endpoint that returns a team name: matches, fixtures, schedule, results, boxscores (lineup included), gamelogs (round timelines too), player rows, live games, props, lines, gaps, head-to-head, match prep and research (for example `Morrow Esports`). When a source spells the team differently, that spelling is in `team1_alias` / `team2_alias` (player rows: `team_alias`; team objects: `alias`), present only when it differs. Ids never depend on the spelling: `kr_tm_*` is unchanged. - Match rows carry tournament facts: `lan` (`true`, `false` or `null`) with `lan_basis` (the record and field that decided it, or `not_provable`) and `lan_scope` (`stage` or `tournament`), `competition_tier` with `competition_tier_source`, `competition_event_type` (`online` or `offline`), `competition_event_scope`, `stage_name` and `competition_country`. `null` means the tournament data does not state it; a name or a country alone never decides `lan`. Tier letters from different records are not one scale, so compare them only when `competition_tier_source` matches. - CS2 live boxscores carry the round clock as `round_clock` (m:ss), already counted down to the moment of publishing, plus `round_clock_ms` (the scoreboard's own reading), `round_clock_as_of` (when that reading arrived, ISO UTC) and `round_clock_running` (true while the round is live, false in freeze time, between rounds and pauses). A client can tick between pushes with `round_clock_ms - (now - round_clock_as_of)` while `round_clock_running` is true. - `get_boxscore` is finished-match vault history + `STARTER`/`SUB` lineup and sport-native model fields (gold, GPM, hero when present) by slug/`kr_match_id`. Live KDA is `get_live_boxscore`. - One shape per title: boxscore, live board and gamelog player rows carry only their own title's stats (CS2: ADR, headshots, KAST, rating, flash assists, economy; Valorant: agent, ACS, ADR, KPR, first kills, KAST 0-100; LoL: champion, role, CS, gold, level, damage; Dota 2: hero, last hits, denies, GPM, XPM, level, hero damage, tower damage, healing; R6: kills, deaths, assists). Round fields (`rounds_played`, `rounds`, round scores) appear only for CS2, Valorant and R6. A stat nobody measured is absent, never 0 or null. Boxscore responses (`get_boxscore`, the `/boxscores` list) and the live boxscore carry `columns`: the title's ordered stat columns as `{key, label, format}` (`key` is a dot path into a player row, such as `advanced_stats.kast`; `format` is `int`, `decimal1`, `decimal2`, `ratio_percent`, `percent` or `text`). Render from `columns`; a row without a column's key has no value for it. - `GET /v6/esports/{sport}/boxscores` (MCP `get_boxscore` without a match) lists recent finished series for every title (CS2, LoL, Dota 2, Valorant, R6, MLBB, Deadlock), newest first, from match history; each carries its per-map `player_stats` (with `map_number`). A series still being played is not stored as finished: a best of 3 at 1-1 is `current` in match history until a side wins. - Live frames carry `upstream_ts` (ISO UTC): the real-world moment the frame shows, when the source states one (LoL, Dota 2, and CS2 on some matches), otherwise null. Dota 2 tournaments broadcast games behind a spectator delay set by each tournament (15 minutes on recent events), so a Dota 2 frame's `upstream_ts` sits that far behind its `ts`. - Live boards (live boxscore, frames, WebSocket) name each player by the same `kr_pl_*` id as that game's live events and the player routes. - A finished map never leaves the boxscore. While a series is live, or before the vault has a map's rows, a finished map carries its final live player stats with `provisional: true` and `stats_source: live`; the vault's rows replace them when they land. Map rows carry no upstream game or map ids. - Streams: matches (`get_match`, `get_boxscore`), live games and `get_streams` share one stream shape: `platform` (twitch, kick, youtube), `channel` (channel name, or the video id on YouTube), `url`, `embed_url`, plus `language`, `official`, `name`, `viewers` and `thumbnail` when the source states them (`official` is never guessed). `get_streams` rows also carry `kr_match_id` when the stream covers a listed match. - Boxscore maps: `begin_at` is the game's own start, never the match start or a midnight date; when the source has none it is null with `begin_at_reason` (`source_has_no_game_clock`, or `unverified` for a map 1 whose stored clock equalled the match start). `duration` is seconds (null with `duration_reason` when unknown) and `end_at` is `begin_at` + `duration`. Each map's `winner` comes with `result_source`: `source`, or `derived_from_series` on a sweep (a 2-0 or 3-0 decides every game); otherwise `winner` is null with `result: {"available": false, "reason": "source_gave_no_game_winner"}` and players' `map_won` is null, never false. `winner_score`/`loser_score` and `rounds_played` are round counts and appear only for CS2, Valorant and R6 (Dota 2 and LoL maps have no rounds). Player `team_id` on every map equals the lineup's `kr_tm_*` id. The boxscore always answers under the `kr_match_id` requested, and its series score is the same as on `get_matches` and `get_match`. - Boxscore map status and result history: every map carries `map_status`: `played` when it has player stats, `no_stats` with `map_status_reason` when it has none, `forfeit` only when the match itself is declared a forfeit (a forfeited series has no maps: the body carries `result_status: forfeit`, `maps_returned: 0` and `maps_reason: forfeit_has_no_maps`); a missing score never makes a forfeit. `bo_type` is the series length the match declared, or null with `bo_type_reason`; `maps_returned` counts the maps in the response. The series and each map carry `result_first_seen_at`, `result_revision` (how many results it has had) and `corrected_at` (last change; null if never changed); a result stored before history began has `result_first_seen_at: null` with `result_first_seen_reason: history_starts_`. `get_boxscore(include="history")` (HTTP `?include=history`) adds `result_history`: every result in order with `winner_team_id`, score, `result_source`, `first_seen_at` and `superseded_at`. - Grading settles every prop on its `kr_match_id` match from the vault. A pending grade says why: `pending_reason: unlinked` (no KashRock match for the prop yet) or `match_not_in_vault` (the match is linked but not stored yet). - Empty player-props results say why: `available: false` with a `reason` (`no_sportsbook_lists_player_props` for `board=main` when no sportsbook lists player props for that sport; the main board carries sportsbook player props only for sports where a sportsbook lists them, such as CS2, League of Legends and Dota 2 — use `board=all`; `no_props_listed`; `no_props_match_filters`) and a `hint`. A prop that could not be tied to a match carries `resolution_status: unresolved` and `resolution_unresolved` entries `match:no_fixture`, `match:opponent_missing`, `match:kickoff_outside_window`, `match:ambiguous_fixture` or `abbreviation_team`; it never reports `ok` without a `kr_match_id`. Every prop row also carries `match_link` (`linked` or `pending`) and, when pending, `match_link_reason` in plain English; a pending row is still returned and links on its own once our schedule or a second book confirms the match. A match listed by two or more books before our schedule has it gets a provisional `kr_match_id` (`status_source: book`) that stays valid permanently on every match route (match, boxscore, lines, lines history, prep, veto, live): when the schedule confirms it, or it turns out wrong and a correct match exists, the id is served as that official match with an `X-Match-Redirected-From` response header; if it never happens it answers 200 `available: false` with `reason: provisional_retired` (never a 404). A `kr_match_id` we once published for a match whose record was later corrected (wrong date, or another match's details) keeps working: it is served as the corrected match, which can be in another game (a `kr_cs2_…` id can answer as a `kr_valorant_…` match), with the same `X-Match-Redirected-From` header; an old id that names no match we hold answers 404. A `kr_match_id` is served only when our schedule lists it: an id for a match not listed yet answers 200 `available: false` with `reason: match_pending` and resolves on its own once listed; an id whose date passed without a listing answers 404 (on /v7, 404 with `reason: id_retired`). A player prop whose team could not be proven carries `team: null` and `team_unresolved_reason: player_team_unproven`; a team is filled in only when the player is on exactly one of the match's two rosters. `books[].prop_count` on the player-props board counts the named-player props served for that book. - Market labels: every prop row and every lines market states `stat_category` (the stat itself, e.g. `kills`, `awp_kills`, `headshots`), `market_label` (plain-English name with the map scope, e.g. "AWP Kills on Maps 1+2"), `market_type`, `market_scope`, `included_maps`, `ot_included` and `settlement_rules`; anything not proven is `unknown`. CS2 kills: `CS2_KILLS_MAP_N` is one player's kills on map N; `CS2_KILLS_MAPS_A_B` is their kills added across those maps (maps 1 and 2 together for `CS2_KILLS_MAPS_1_2`); bare `CS2_KILLS` does not state its maps, so its scope is `unknown`. `CS2_AWP_KILLS_MAPS_1_2` counts only AWP kills and `CS2_HEADSHOTS_MAPS_1_2` only headshots; neither is the same market as `CS2_KILLS_MAPS_1_2`. `TOTAL_KILLS_MAP_N` and `KILL_HANDICAP_MAP_N` are sportsbook team-kill lines, not player props. - Dota 2 per-map player rows (get_boxscore, get_gamelogs) carry `last_hits`, `denies`, `hero_damage`, `hero_healing` and `tower_damage` for maps stored from the date this shipped; older maps fill in as they are refetched. - Player handles: every player route and tool (get_player, get_player_stats, get_player_stats_full, get_gamelogs, research_player) accepts a `kr_pl_*` id, a profile slug or a nickname, and all reach the same player. Every player route returns the player as `player_id` = `kr_pl_*`; no upstream player number appears in a response. A nickname shared by several players returns HTTP 409 with `candidates` (player_id, nickname, slug, team, last played) instead of a guess; repeat the call with a candidate's `player_id` or slug. An old shared `kr_pl_*` id resolves to the most recently active player and returns `legacy_id_ambiguous: true` with the other players as `candidates`. - COD (Call of Duty League): get_boxscore and get_gamelogs serve finished series from the vault (maps with mode, winner, score and length; players with their team). Series are best-of-5, 7 or 9, so a series score such as 3-5 is valid. Upcoming and live COD fixtures stay on get_matches and the live tools. - CS2 assists: `assists` counts damage assists only and `flash_assists` is its own count, so neither includes the other. `assists_total` = `assists` + `flash_assists` (the scoreboard figure); it appears on CS2 gamelog rows and boxscore players beside both fields, and is absent when either count is missing. `assists` itself is unchanged. - Fantasy points: per-map player rows (get_boxscore, get_gamelogs) carry `fantasy_points` as an object keyed by book, for example `{"underdog": 81.7}`, computed from the book's published scoring (LoL and Dota 2: kill 3, assist 2, death -1, creep score or last hits 0.02; CS2 and Valorant: kill 2, assist 1, death -1; COD: kill 2, death -1). It is absent when any raw input (kills, deaths, assists, creep score or last hits) is missing. `research_player` and `get_results` accept the `DOTA2_FANTASY_POINTS` market (and the same stem for other titles) and grade it from those per-map values. - Results join + CLV: `get_results` / `GET /v6/esports/{sport}/results` accept `from`/`to` (YYYY-MM-DD on `graded_at`). Each row carries `offer_id`, `canonical_event_id` when the event is `kr_ev_*`, and `opening_line` / `opening_odds` / `closing_line` / `closing_odds` when the quote tape has them. Void rows include a `notes` reason. Live `/props` stays DFS-only; sportsbook player props use `get_player_props` or `v7_props?board=main`. Fair/EV is on `get_lines` / `get_best_lines` (two qualifying books minimum), not on the DFS props board. - Lines fair price and EV (`GET /v6/esports/{sport}/lines`, MCP get_lines / get_best_lines): every outcome lists every book's price. `fair` (on each market and outcome: `available`, `reason`, `books_used`, `books_rejected` with the rule each book failed) is the median of proportionally de-vigged prices from books that price both sides of the same line themselves, were fetched within 60 seconds, have an overround between 1.00 and 1.15, and (prediction markets) have a known bid/ask spread of at most 0.15 and meet the liquidity floor. Fewer than two such books gives `available: false` with a `reason` and no EV; the card still lists every price. Each price carries `ev_pct` = fair probability x decimal price - 1 (`edge_pct` is the same value). DFS pick'em pays a fixed multiplier, which is not odds: it is served on `dfs_team_pick` markets (`base_market` names the line) as `payout_multiplier` (with `american`, the same price in American format: 2.0 or more is (m - 1) x 100, below 2.0 is -100 / (m - 1)), with `ev_pct` against fair as a target only, and never enters fair. Prices attach to their team by team key (`kr_tm_*`), never by list order. `pm_weight` is accepted and ignored. - Bet links: every prop row (get_props, get_player_props) and every lines source / best_price entry carries `deeplink` (URL that opens the book, or null) and `deeplink_level` (`selection`, `player`, `match`, `board`; best first). HTTP equivalents: - `GET /v6/esports/{sport}/matches` (upcoming/live/finished; rows include kr_match_id + kr_tm_*) - `GET /v6/esports/{sport}/upcoming/matches` - `GET /v6/esports/{sport}/matches/{slug_or_kr_match_id}/boxscore` (finished vault + STARTER/SUB lineup + model fields; an empty `maps` list with `maps_available: false` and `maps_reason: not_cached_yet` means the per-map rows are not filled yet, so retry shortly) - `GET /v6/esports/{sport}/players/search` (returns kr_pl_*) - `GET /v6/esports/{sport}/players/{player}/gamelogs` (MCP `get_gamelogs`; newest match first, then map number; each row carries `match_start` (the match's UTC start used for `from`/`to`) and the same map clock as the boxscore (`begin_at`, `end_at`, `duration` in seconds, or null with a `*_reason`); `from`/`to` (YYYY-MM-DD, `to` inclusive of the whole UTC day) filter on match start within your plan window (Sandbox 30 days, Hobby 90 days, Builder and Pro full history); an older `next_cursor` keeps working; `limit` default 500, capped to your plan: Sandbox 100, Hobby 500, Builder 2000, Pro 5000. A larger `limit` is capped, never an error. When `next_cursor` is not null, pass it back as `cursor` for the next page. Per-round timelines (`rounds`) come back when `limit` is 500 or less; above 500 they are left out unless you pass `include=rounds`, and `rounds_included` says which) - `GET /v6/esports/{sport}/matches/{slug_or_kr_match_id}/prep` (cs2, r6, cod, valorant, apex) - `GET /v6/esports/{sport}/matches/{slug_or_kr_match_id}/player-board` (cs2, valorant) - `GET /v6/esports/cs2/matches/{slug_or_kr_match_id}/tournament-maps` - `GET /v6/esports/{sport}/matches/{slug_or_kr_match_id}/veto` (cs2, valorant, r6) - `GET /v6/esports/{sport}/teams/h2h` - `GET /v6/esports/{sport}/teams/{team}/matches` (team = kr_tm_* id, slug or name; Builder+; HTTP equivalent of get_team_matches) - `GET /v6/esports/{sport}/defense-vs-role?window=last_20` (dota2, valorant; what each team allows to the opposing role vs league average, with rank) - `GET /v6/esports/{sport}/teams/{team}/defense-vs-role?window=last_20` (team = kr_tm_* id, slug, name or numeric source id) - `GET /v6/esports/{sport}/live/games` (game_id = kr_match_id; `has_live_stats` is true while a live scoreboard is publishing and for up to 15 minutes after the last frame; `live_state` is `in_map`, `between_maps` (stats paused, not broken) or null; `last_frame_at` is the time of the last board. False means no in-game source covers the match or it has been silent past the grace period; `stats_available` mirrors it and, when false, `reason` is `source_stats_disabled` (the official source turned stats off for that game), `live_pending` (the match is live and its first board has not arrived yet -- keep polling, it is coming) or `no_live_coverage`; the boxscore route, the WS snapshot and the WS `status` push carry the same fields; each game carries `streams`) - `GET /v6/esports/cs2/live/coverage?days=14` — share of listed CS2 matches with live stats by tournament and tier (MCP `get_live_coverage`, every plan); counts matches logged since `tracking_since` - `GET /v6/esports/{sport}/live/{kr_match_id}/boxscore` — the current map's board plus `maps`: every map this series already finished, with its final player stats (`provisional: true`, blue/red sides, `winner_side` when the series score proves it; a map that ended inside your plan's delay is held back like the board), and `columns` - `GET /v6/esports/{sport}/live/{kr_match_id}/frames` - `GET /v6/esports/{sport}/live/{kr_match_id}/events` — CS2 kills carry `killer`, `victim`, `weapon`, `headshot`, `assist`, `map`, `round`; bomb events are `BOMB_PLANT`, `BOMB_DEFUSE`, `BOMB_EXPLODE` (`BOMB_DOWN` only when a frame source cannot tell the outcome); `ROUND_WIN` carries `win_type`. Player health, money and weapon are omitted when the source does not send them. - `GET /v6/esports/{sport}/live/{kr_match_id}/state-at?at=` - `GET /v6/esports/{sport}/live/odds` - `GET /v6/esports/{sport}/live/{slug_or_kr_match_id}/odds` - `GET /v6/esports/{sport}/live/{slug_or_kr_match_id}/odds/history` - `GET /v6/esports/{sport}/matches/{slug_or_kr_match_id}/lines/history` — pre-match open and close per book and side; every tick has `observed_at` and `is_live`, and in-play ticks are returned only with `include_live=true`; empty results return `available: false` and a `reason`. The response, every market and every book carry `bo_type` and `bo_label` (`BO1`, `BO3`, `BO5`; null with a `bo_reason` when unknown). Map handicap and total markets carry a `scope` (`maps` for series map-count lines, `rounds` for round lines) and a `scope_basis`; per-map markets carry `map_number`. Every market also states `market_type` (`winner`, `maps`, `rounds`, `kills`, `correct_score`, `unknown`), `market_scope` (`series`, `map_N`, `maps_A_B`, `unknown`) and `included_maps`. Round totals and round handicaps are their own markets, `total_rounds` and `round_handicap` (kills: `total_kills`, `kill_handicap`), and are never filed under `total_maps` or `map_handicap`; they are served per book here and built into a `/lines` consensus per group of books on the same line, a proven scope and the same overtime class. Correct score is its own market, `correct_score` (scope `series` or `map_N`), served here with each listed score as a side that has its own open, close and quote tape. Bare `total_maps` and `map_handicap` keys have `market_scope` `unknown`. Each book block carries `ot_included` (`true`, `false` or `unknown`, set only where the book states it) and `settlement_rules`. CS2 per-map round totals and round handicaps (maps 1 to 5, alternate lines included) are served from Pinnacle as `total_rounds` and `round_handicap`; `settlement_rules` carries each book's void rules, quote and source wherever an official rules page is recorded, and `ot_included` stays `unknown` unless that page states overtime. The `polymarket_history` book is a historical mid-price series, not executable ask. - `WS /v6/esports/live/ws` Do not call grade-run or admin. V7 HTTP reference (every route, parameters, plan tier, and which books each endpoint serves): https://www.kashrock.com/docs/v7 SDKs (HTTP, not MCP): https://www.kashrock.com/docs/sdk - JS: `npm install kashrock` · first call `props("lol")` with demo key `kr_demo_swagger_public_readonly` · https://www.npmjs.com/package/kashrock - Python: `pip install kashrock` · same demo key · https://pypi.org/project/kashrock/ - Source: https://github.com/kashrockapi/kashrock-js · https://github.com/kashrockapi/kashrock-python Sports: cs2, valorant, lol, dota2, cod, r6, mlbb, deadlock, apex.