Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.ticktock.bet/llms.txt

Use this file to discover all available pages before exploring further.

Why scopes

Same URL, different field set per caller. A bookmaker on cs2.odds.basic calling GET /cs2/v1/matches/{id} gets the markets and rounds. A stats consumer on cs2.stats calling the same URL gets the stats but no markets. An internal operator calling it gets everything plus operational fields. One URL, one serializer, scope-filtered output. Use /v1/whoami to introspect what your key currently grants.

Bundles

Bundles are issuance shorthand — pick one (or several) at key creation, the system expands them to a flat scope list. There is no “bundle” entity in the database — just the resulting scope set on your key.
BundleUse caseIncludes
cs2.odds.basicBookmakers consuming odds + settlementsmatches list/read/detail, markets read, settlements, WS for matches & markets, tournaments, teams, players
cs2.odds.scoreboard-addonAdd-on to odds.basic for live scoreboardlive HP/money/weapon scoreboard + WS scoreboard frames (front-running risk; priced separately)
cs2.statsStats-only consumersmatches list/read/detail, tournaments, teams, players, maps. No odds, no live frames.
cs2.scoretockPublic-portal partners (finished matches only)matches list/read/detail, teams/players/tournaments read
cs2.operatorInternal services and our own UIsevery cs2:*:* scope. Issued only to internal systems.

Granular scopes

Format: cs2:{resource}:{action}. Keys can also be issued with raw scopes a la carte (e.g. add cs2:matches:scoreboard to a stats key on request).
ScopeGrants
cs2:matches:listMatch list — basic shape (id, teams, event, status, scheduled_at, score)
cs2:matches:readSingle-match read — same shape as list, scoped to one match
cs2:matches:detailAdds rounds, events, timeline, demo URL, streams (with VOD anchors), per-player stats
cs2:matches:scoreboardAdds live scoreboard (HP/money/weapons). Front-running risk — gated explicitly
cs2:matches:internalsOperator-only: integration IDs, data_delay_seconds, suspended, booking_status, source_url
cs2:markets:readActive market offers, odds, suspend status
cs2:markets:settlementsSettlement audit log
cs2:tournaments:list / cs2:tournaments:readTournament directory and detail
cs2:teams:list / cs2:teams:readTeam directory, profile, form, h2h
cs2:players:list / cs2:players:readPlayer directory and profile
cs2:maps:readGlobal map analytics + active pool
cs2:stream:matchesWS frames: match.*, round.*, event.*
cs2:stream:marketsWS frames: market.*, bet_settlement
cs2:stream:scoreboardWS frames: scoreboard.frame

Scope → fields

The same URL emits different fields depending on which scopes you have. Fields outside your scope set are simply absent from the response — not nulled, just not emitted.

Example: GET /cs2/v1/matches/{id}

Field groupRequired scope
id, team_a, team_b, event, bo_type, status, scheduled_at, score_a/bcs2:matches:list or cs2:matches:read
started_at, finished_at, maps[]cs2:matches:read
rounds[], events[], streams[], demo_url, player_stats[]cs2:matches:detail
markets[]cs2:markets:read
scoreboardcs2:matches:scoreboard
oddin_match_id, data_delay_seconds, suspended, …cs2:matches:internals
The same logic applies to Round, Stream, Market, and other shapes. Detailed mappings live alongside the corresponding endpoint pages in the API Reference.

Content negotiation

Every REST endpoint serves both JSON (default) and XML. Pass Accept: application/xml and the response body comes back as XML on the same URL. The seven AMQP-feed endpoints additionally expose a .xml sibling URL for clients whose HTTP infrastructure prefers a distinct cache key per format.

Two XML dialects

DialectEndpointsSchema
Feed XML (hand-tuned, byte-compatible with AMQP feed)The seven endpoints belowOddin-style envelopes. Stable wire format.
Stats XML (mechanical JSON→XML)Everything else (teams, players, tournaments, maps, per-match analytics, settlements, search, sports catalog)Predictable: dict → element with attribute children; list → wrapper with singularised item tags; null → element absent.

Feed XML endpoints

These use hand-tuned envelopes that match the AMQP feed byte-for-byte — useful for clients that already have an Oddin/Sportradar-style XML pipeline.
EndpointXML root element
GET /v1/alive<alive>
GET /v1/whoami<bookmaker_details>
GET /cs2/v1/matches<schedule>
GET /cs2/v1/matches/{id}<fixtures_fixture> (compact, not the rich JSON detail)
GET /cs2/v1/matches/{id}/summary<match_summary>
GET /cs2/v1/matches/{id}/markets<odds_change> over active B2B-feed offers
GET /cs2/v1/markets/descriptions<market_descriptions>

Stats XML

Every other endpoint produces a mechanical XML rendering of the JSON shape. Example: GET /cs2/v1/teams/11712 with Accept: application/xml returns:
<team>
  <data hltv_team_id="11712" name="Sashi" world_ranking="42" country_code="DK" rating="1.04">
    <map_stats>
      <map_stat map_name="de_mirage" matches_played="18" win_pct="0.55"/>
      <map_stat map_name="de_inferno" matches_played="12" win_pct="0.50"/>
    </map_stats>
  </data>
</team>
Rules: scalars become attributes, nested dicts become child elements, lists become wrapper elements with singularised item tags, null is elided. There is no XSD for this dialect today — consumers parse it via XPath / element-tree.
Pass the Accept: application/xml header on any GET, or use the .xml URL form on the seven feed endpoints. Errors (4xx/5xx) keep the JSON {"detail": "..."} shape regardless of Accept so error parsing stays uniform.
WebSocket frames are JSON only.
Last modified on May 12, 2026