wss://therundown.io/api/v2/ws carries multiple logical channels — markets, scores, plays, and live — over one connection. Instead of encoding filters in the connection URL, you send JSON subscribe messages after connecting, and you can add or remove subscriptions at any time without reconnecting.
WebSocket access is enabled on real-time API tiers (Ultra and above) by default. The
plays and live channels additionally require the live game state entitlement, which is also included from Ultra up — see Rate Limits. Each plan also caps concurrent connections and subscriptions per connection — see the connection limits table.When to Use the Multiplexed Endpoint
Connect
Authenticate with thekey query parameter, the same as the dedicated markets stream:
Subscribe to a Channel
Send asubscribe action with a client-chosen id, the channel, and optional params filters:
The server acknowledges each successful subscription:
Channels and Filters
Handle Incoming Messages
Channel data arrives wrapped in a delta envelope tagged with your subscriptionid. Route messages on the top-level type field, then on id:
Heartbeats arrive in the same format as on the dedicated markets stream —
{"meta": {"type": "heartbeat"}, ...} — and are not tagged with a subscription id. For the full payload field reference per channel, see the WebSocket reference; play-by-play payloads are documented under Play messages.
Request a Snapshot
Deltas only tell you what changed — to start from current state, add"snapshot": true to params when subscribing. Snapshot requests need a bounded scope: event_ids, or sport_ids plus date (plays snapshots require event_ids):
subscribed ack, the server sends one or more snapshot frames followed by snapshot_complete; live deltas begin after that. An active subscription can also request a fresh snapshot at any time without reconnecting:
Unsubscribe
Send anunsubscribe action with the subscription’s id:
id first, then unsubscribe the old one — you may briefly receive duplicate messages while both are active, so deduplicate during the overlap.
Handle Errors
Errors are returned as{"type": "error", "id": "...", "code": "...", "message": "..."}:
Complete Client Example
A client that subscribes to NBA market prices and scores on one connection, routes messages by subscriptionid, and resubscribes automatically after a reconnect:
Streaming Play-by-Play
On an Ultra plan or higher, add aplays subscription to receive live play-by-play for in-progress games:
meta.type of "play" in the inner payload — description, period, running score, and (as attribution rolls out) the players involved. For the full play payload, see Play messages in the reference.
To load the plays that happened before you subscribed, either add "snapshot": true to the subscription (plays snapshots return up to 500 current plays), or fetch the timeline from the REST endpoint GET /api/v2/events/{eventID}/plays and apply streamed plays on top.
If you want scores, live_game_state, and plays together for the same games, subscribe to the live channel instead of pairing scores with plays — it delivers all in-game updates on one subscription (score/status deltas arrive with meta.type of "score", plays with "play"), which also conserves your plan’s subscription slots.
Best Practices
Resubscribe on every reconnect
Resubscribe on every reconnect
Subscriptions live only as long as the connection. After any disconnect, re-send all subscribe messages as soon as the new connection opens — put the subscribe logic in your
onopen handler so it runs on every connect, as in the example above.Re-sync state after a reconnect
Re-sync state after a reconnect
You may have missed updates while disconnected. Resubscribe with
"snapshot": true so each subscription replays current state before deltas resume, or fetch current state from the REST API (or the delta endpoints). Either way your local cache won’t serve stale prices or scores; snapshot frames are metered like the equivalent REST reads.Filter every subscription
Filter every subscription
An unfiltered
markets subscription streams every price change across all sports and books, and the connection’s 1024-message buffer drops messages if you fall behind. Scope each subscription with sport_ids, event_ids, or market_ids to what you actually consume.Use meaningful subscription ids
Use meaningful subscription ids
The
id is how you route incoming messages, so name subscriptions after what they carry (nba-markets, mlb-plays) rather than opaque strings. Keep a map of active ids to handlers and you can add or remove feeds without touching your message loop.Consolidate subscriptions where filters allow
Consolidate subscriptions where filters allow
Plans cap subscriptions per connection — from 3 on Ultra to 50 on Enterprise (error code
subscription_limit; see the connection limits table). One markets subscription filtered to two sports uses one slot; two single-sport subscriptions use two. The live channel replaces a scores + plays pair with a single slot. Split subscriptions only when you need to route or manage the feeds independently.Next Steps
WebSocket Reference
Full protocol details and payload field tables
WebSocket Streaming
The dedicated markets stream, heartbeats, and reconnection patterns
Play-by-Play REST Endpoint
Backfill the play timeline before streaming
Efficient Polling
REST delta endpoints as a fallback or backfill