v3
OpenAPI 3.1.02026-08-055966167.2 KBMultiplexed WebSocket — subscribe to markets, scores, plays, stats, and live channels
Establishes a single WebSocket connection carrying multiple logical channels. Instead of query-parameter filters, send JSON subscribe messages after connecting.
Connection: wss://therundown.io/api/v2/ws?key=YOUR_API_KEY
Channels:
- markets — market price updates (same payloads as /api/v2/ws/markets)
- scores — score and event-status updates (no live game state fields)
- plays — play-by-play deltas for live games. Requires an Ultra plan or higher (the live game state entitlement); a plays, stats, or live subscribe from a non-entitled key is rejected with error code forbidden.
- stats — changed team and player box-score rows, delivered inline with meta.type=game_stats. Requires an Ultra plan or higher. This is a delta-only channel with no snapshot, replay, or resume; bootstrap and recover from both event game-stat REST endpoints. Each changed stat row is one stats data point.
- live — score/status deltas including live_game_state, play-by-play deltas, and game-stat deltas, on one subscription. Requires an Ultra plan or higher; live_game_state and game_state are accepted aliases.
Subscribe (client → server):
{
"action": "subscribe",
"id": "p1",
"channel": "plays",
"params": { "sport_ids": [3], "event_ids": ["<eventID>"] }
}
The server acknowledges with {"type":"subscribed","id":"p1","sequence":N,"message":"subscribed to plays"}. Supported params filters: sport_ids, event_ids (plus market_ids and affiliate_ids on the markets channel). stats has no stat, team, or player filter. Unsubscribe with {"action":"unsubscribe","id":"p1"}.
Delta messages (server → client) are wrapped in an envelope tagged with your subscription id:
{
"type": "delta",
"id": "p1",
"sequence": 42,
"sub_sequence": 7,
"delta_last_id": "...",
"data": { "meta": { "type": "play" }, "data": { ... } }
}
A stats delta uses the same outer envelope. Its inner payload has meta.type=game_stats; data.team_stats[] and data.player_stats[] contain only changed rows, and each row's value remains a JSON string. Upsert those rows into the REST-bootstrapped box score rather than replacing a whole group. If both row arrays are absent and complete=true, the frame is the terminal completion marker; mark the cached box complete and treat repeats as idempotent. If both arrays are absent without complete=true, the frame is an invalidation fallback: refetch both GET /api/v2/events/{eventID}/stats and GET /api/v2/events/{eventID}/players/stats. Row deltas and completion markers use an RFC 3339 updated_at; the fallback uses numeric Unix seconds. Do not use delta_last_id as a game-stat replay cursor. Each changed nested row is one stats data point; either zero-row variant costs one stats data point.
Snapshots: add "snapshot": true to params to receive current state (snapshot frames, then snapshot_complete) before deltas begin; an active subscription can request a fresh snapshot at any time with {"action":"snapshot","id":"p1"}. Snapshot requests need a bounded scope and are metered as data points like the equivalent REST reads. Snapshots are not supported on stats; a request returns snapshot_error with code unsupported_snapshot. A live snapshot does not include a game-stat baseline.
Errors: {"type":"error","id":"...","code":"...","message":"..."} with codes forbidden (plays/stats/live without Ultra+), invalid_channel, missing_id, duplicate_id, and subscription_limit (plan's concurrent subscription cap reached). A slow multiplexed client may receive the connection-level error code buffer_overflow immediately before the connection closes with reason buffer_overflow:reconnect_and_catchup.
Queue and recovery: each non-market subscription has its own 1024-message outbound queue; market subscriptions are sized separately. If a live frame cannot be queued, the server closes the connection rather than continue with a silent gap. Reconnect, re-send subscriptions, and recover current state before applying new deltas. For stats, refetch both event game-stat REST resources.
Concurrent connection and subscription limits vary by tier. See the WebSocket reference for full protocol details.