Skip to main content

Overview

TheRundown provides a V2 WebSocket endpoint for streaming real-time data without polling:
EndpointDescription
/api/v2/ws/marketsStreams market price changes (odds updates) as they happen
WebSocket connections do not count against your REST API rate limit.

Connection

Connect using a standard WebSocket client. Authentication is via the key query parameter:
wss://therundown.io/api/v2/ws/markets?key=YOUR_API_KEY
Use wss:// (secure WebSocket), not ws://. Connections on ws:// will be rejected.

Markets WebSocket

GET /api/v2/ws/markets

Streams V2 market price changes in real time. Every time a sportsbook updates a price on any tracked market, you receive a message.

Filter Parameters

All filters are optional. If none are specified, you receive all messages across all sports and markets.
ParameterDescription
affiliate_idsComma-separated sportsbook IDs (e.g., 19,23)
sport_idsComma-separated sport IDs (e.g., 4,6)
event_idsComma-separated event IDs
market_idsComma-separated market IDs (e.g., 1,2,3)

Example Connection URLs

# All NBA market updates
wss://therundown.io/api/v2/ws/markets?key=YOUR_API_KEY&sport_ids=4

# DraftKings + FanDuel moneyline updates only
wss://therundown.io/api/v2/ws/markets?key=YOUR_API_KEY&affiliate_ids=19,23&market_ids=1

# Updates for a specific event
wss://therundown.io/api/v2/ws/markets?key=YOUR_API_KEY&event_ids=EVENT_ID

Message Format

Each message is a JSON object with two top-level fields: meta (message metadata) and data (the update payload). Unlike the REST API’s nested structure, WebSocket messages deliver one price update per message in a flat format — each message represents a single price change for one participant, one market, and one sportsbook.

Example: Market Price Update

{
  "meta": {
    "type": "market_price",
    "version": "v2",
    "timestamp": 1772495104
  },
  "data": {
    "id": 193600383,
    "event_id": "9b9d0cf6007fdaeb15c3a1888dcfd5df",
    "affiliate_id": 26,
    "market_participant_id": 19402291,
    "market_id": 3,
    "line": "1.5",
    "price": "-117",
    "previous_price": "-122.0000",
    "price_delta": 5,
    "is_main_line": true,
    "normalized_market_participant_id": 10,
    "normalized_market_participant_type": 3,
    "sport_id": 7,
    "updated_at": "2026-03-02T23:44:44Z"
  }
}

Meta Fields

FieldTypeDescription
typestring"market_price" for price updates, "heartbeat" for keep-alive
versionstringAPI version ("v2")
timestampnumberUnix epoch timestamp when the message was sent

Data Fields

FieldTypeDescription
idnumberUnique price record ID
event_idstringCanonical V2 event ID
affiliate_idnumberSportsbook ID (see Sportsbook IDs)
market_idnumberMarket type (see Market IDs)
market_participant_idnumberSportsbook-specific participant ID for this market entry
normalized_market_participant_idnumberCanonical participant ID — maps to participant.id in REST API responses
normalized_market_participant_typenumberParticipant type identifier
linestringLine value (e.g., "-4.5" for spread, "224.5" for total, "0" for moneyline)
pricestringCurrent American odds (e.g., "-117", "+150")
previous_pricestringPrice before this update
price_deltanumberNumeric change from previous price
is_main_linebooleanWhether this is the primary line
sport_idnumberSport ID
updated_atstringISO 8601 timestamp of the price change

Heartbeat

The WebSocket endpoint sends a heartbeat message every 15 seconds to keep the connection alive:
{
  "meta": {
    "type": "heartbeat"
  },
  "data": {
    "now": "2026-02-27T01:15:00Z"
  }
}
Your client should detect heartbeats and use them to confirm the connection is healthy. If you stop receiving heartbeats, the connection may have dropped — reconnect.

Message Queue

Each client has a 256-message buffer. If your client cannot consume messages fast enough, the server drops messages rather than blocking. This means:
  • If your processing is slow, you may miss updates
  • Design your client to process messages quickly and offload heavy work asynchronously
  • For high-volume feeds, consider filtering to specific sports or events to reduce message volume

Client Examples

const ws = new WebSocket(
  "wss://therundown.io/api/v2/ws/markets?key=YOUR_API_KEY&sport_ids=4&market_ids=1,2,3"
);

ws.onopen = () => {
  console.log("Connected to TheRundown WebSocket");
};

ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);

  // Skip heartbeats
  if (msg.meta?.type === "heartbeat") return;

  const d = msg.data;
  console.log(
    `Event ${d.event_id} | market=${d.market_id} aff=${d.affiliate_id}`
  );
  console.log(
    `  line=${d.line} price=${d.price} (was ${d.previous_price}, delta=${d.price_delta})`
  );
};

ws.onerror = (error) => {
  console.error("WebSocket error:", error);
};

ws.onclose = (event) => {
  console.log(`Disconnected: code=${event.code} reason=${event.reason}`);
  // Implement reconnection logic here
};

If WebSocket is not an option for your architecture, use the REST delta endpoints to poll for changes efficiently. For the full list of market types you can filter on, see Market IDs.

Best Practices

The unfiltered market feed can be very high volume. Always apply sport_ids, market_ids, or event_ids filters to receive only the data you need. This keeps your 256-message buffer from overflowing.
WebSocket connections can drop due to network issues, server deployments, or idle timeouts. Always implement reconnection logic with exponential backoff (e.g., 1s, 2s, 4s, 8s, max 30s).
If you have not received any message (including heartbeats) for 30+ seconds, assume the connection is dead and reconnect. Do not wait for the WebSocket close event, as it may not fire reliably in all network conditions.
Keep your message handler fast. Parse the JSON and push work to a queue or separate processing thread. A slow message handler causes the 256-message buffer to fill up and messages to be dropped.