Skip to main content
The multiplexed WebSocket at 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 the key query parameter, the same as the dedicated markets stream:
The connection starts with no subscriptions. Until you subscribe, the only messages you receive are heartbeats every 15 seconds.

Subscribe to a Channel

Send a subscribe action with a client-chosen id, the channel, and optional params filters:
The server acknowledges each successful subscription:

Channels and Filters

Unlike REST query parameters, params values are JSON arrays, not comma-separated strings: numbers for sport_ids, market_ids, and affiliate_ids (e.g., [4, 6]), strings for event_ids.

Handle Incoming Messages

Channel data arrives wrapped in a delta envelope tagged with your subscription id. 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):
After the 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:
Snapshot frames are metered as data points by category, the same as the equivalent REST reads. See Snapshots in the reference for per-channel snapshot contents.

Unsubscribe

Send an unsubscribe action with the subscription’s id:
To change a subscription’s filters, unsubscribe and subscribe again. If your application cannot tolerate a gap between the two, subscribe with the new filters under a different 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 subscription id, and resubscribes automatically after a reconnect:
For production use, replace the fixed 3-second reconnect delay with exponential backoff and jitter — see Reconnection Best Practices.

Streaming Play-by-Play

On an Ultra plan or higher, add a plays subscription to receive live play-by-play for in-progress games:
Each play arrives in the delta envelope with 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

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.
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.
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.
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.
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