Skip to main content
TheRundown provides WebSocket endpoints for real-time data delivery. Instead of polling REST endpoints, open a persistent connection and receive updates as they happen. WebSocket traffic does not increment the HTTP request counter, but pushed messages and snapshots are still metered as data points.
WebSocket access is enabled on real-time API tiers by default. If your key does not have WebSocket access, use the market delta endpoint for ongoing updates.

Available WebSocket Endpoints

EndpointURLDescription
V2 Marketswss://therundown.io/api/v2/ws/marketsReal-time market/price updates for all V2 data
V1 (Legacy)wss://therundown.io/api/v1/wsLegacy format line updates

V2 Markets WebSocket

The primary WebSocket for real-time odds data. Delivers individual price updates in a flat format — each message contains one price change for one participant, market, and sportsbook.

Connection

wss://therundown.io/api/v2/ws/markets?key=YOUR_API_KEY

Filter Parameters

All filters are optional. Without filters, you receive updates for all sports and markets.
ParameterTypeDescription
keystringRequired. Your API key
sport_idsstringComma-separated sport IDs (e.g., 2,4,6)
market_idsstringComma-separated market IDs (e.g., 1,2,3)
event_idsstringComma-separated event IDs for specific games
affiliate_idsstringComma-separated sportsbook IDs

Example: Filtered Connection

wss://therundown.io/api/v2/ws/markets?key=YOUR_API_KEY&sport_ids=4&market_ids=1,2,3&affiliate_ids=19,23

Message Format

Each message is a JSON object with meta (message metadata) and data (the price update). Each message represents a single price change — not a full market snapshot.
{
  "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"
  }
}

Code Examples

const API_KEY = "YOUR_API_KEY";

const ws = new WebSocket(
  `wss://therundown.io/api/v2/ws/markets?key=${API_KEY}&sport_ids=4&market_ids=1,2,3`
);

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

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

  // Handle heartbeat
  if (msg.meta?.type === "heartbeat") {
    console.log("Heartbeat received");
    return;
  }

  // Process market price update
  const d = msg.data;
  console.log(
    `Update: 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(`WebSocket closed: code=${event.code} reason=${event.reason}`);
};

V1 WebSocket (Legacy)

The V1 WebSocket delivers line updates with meta.type identifying the market type (e.g., "moneyline", "spread", "total"). The data object contains market-specific fields with _delta suffixes showing the change from the previous value. Use this only if your application has not migrated to V2.

Connection

wss://therundown.io/api/v1/ws?key=YOUR_API_KEY

Filter Parameters

ParameterTypeDescription
keystringRequired. Your API key
sport_idsstringComma-separated sport IDs
affiliate_idsstringComma-separated sportsbook IDs
event_idsstringComma-separated event IDs
datestringDate filter (e.g., 2026-02-12)

Example Message

{
  "meta": {
    "type": "spread"
  },
  "data": {
    "point_spread_away": 0.5,
    "point_spread_home": -0.5,
    "point_spread_away_delta": 0.4999,
    "point_spread_home_delta": -0.5001,
    "point_spread_away_money": 115,
    "point_spread_away_money_delta": 114.9999,
    "point_spread_home_money": -175,
    "point_spread_home_money_delta": -175.0001,
    "line_id": 17994923,
    "event_id": "541a8c85c41cd7ec7ecc74521ee71bc4",
    "sport_id": 12,
    "affiliate_id": 19,
    "date_updated": "2026-03-02T23:45:24Z",
    "event_date": "2026-03-08T19:45:00Z",
    "format": "American",
    "period_id": 0
  }
}

Heartbeat Handling

All WebSocket endpoints send heartbeat messages every 15 seconds to keep the connection alive. Your client must handle these to avoid treating them as data updates.
{
  "meta": {
    "type": "heartbeat"
  },
  "data": {
    "now": "2026-02-12T18:30:00Z"
  }
}
Heartbeats arrive every 15 seconds. If you do not receive one within 60 seconds (i.e., four missed heartbeats), the connection is likely stale. Close and reconnect.

Heartbeat Handler Pattern

let lastHeartbeat = Date.now();

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

  if (msg.meta?.type === "heartbeat") {
    lastHeartbeat = Date.now();
    return; // Do not process as data
  }

  // Process actual price update
  handleUpdate(msg.data);
};

// Monitor heartbeat health
setInterval(() => {
  const elapsed = Date.now() - lastHeartbeat;
  if (elapsed > 60_000) {
    console.warn("No heartbeat in 60s, reconnecting...");
    ws.close();
    // Trigger reconnection logic
  }
}, 10_000);

Reconnection Best Practices

WebSocket connections can drop due to network issues, server maintenance, or client-side timeouts. Always implement automatic reconnection.
  1. Exponential backoff — start at 1 second, double on each failure, cap at 30 seconds.
  2. Add jitter — randomize the delay slightly to avoid thundering herd reconnections.
  3. Reset backoff on success — once a connection is established and receives data, reset the delay to 1 second.
  4. Refetch REST state after reconnect — you may have missed updates during the disconnection. Fetch the latest state from the REST API to ensure consistency.
function createReconnectingWebSocket(url, onMessage) {
  let ws;
  let reconnectDelay = 1000;
  const MAX_DELAY = 30000;

  function connect() {
    ws = new WebSocket(url);

    ws.onopen = () => {
      console.log("WebSocket connected");
      reconnectDelay = 1000; // Reset backoff
    };

    ws.onmessage = (event) => {
      const msg = JSON.parse(event.data);
      if (msg.meta?.type === "heartbeat") return;
      onMessage(msg.data);
    };

    ws.onclose = () => {
      const jitter = Math.random() * 1000;
      const delay = Math.min(reconnectDelay + jitter, MAX_DELAY);
      console.log(`WebSocket closed. Reconnecting in ${Math.round(delay)}ms...`);

      setTimeout(() => {
        reconnectDelay = Math.min(reconnectDelay * 2, MAX_DELAY);
        connect();
      }, delay);
    };

    ws.onerror = (error) => {
      console.error("WebSocket error:", error);
      ws.close(); // Triggers onclose -> reconnect
    };
  }

  connect();

  return {
    close: () => {
      reconnectDelay = MAX_DELAY + 1; // Prevent reconnection
      ws.close();
    },
  };
}

// Usage
const connection = createReconnectingWebSocket(
  `wss://therundown.io/api/v2/ws/markets?key=${API_KEY}&sport_ids=4`,
  (update) => {
    console.log(`Price update: event=${update.event_id} market=${update.market_id} price=${update.price}`);
  }
);

Tips for Production

Always specify sport_ids and market_ids in your WebSocket URL. Receiving updates for all sports and markets generates significant traffic that your client may not need.
Do not block the WebSocket message handler with slow operations (database writes, API calls). Queue updates and process them in a separate thread or task.
After a reconnect, fetch the latest state from the REST API to fill any gaps. WebSocket updates are incremental — if you miss one, your state may be stale.
Track heartbeat intervals and reconnection frequency. Alert if the connection drops repeatedly, which may indicate a network issue or an invalid API key.

Next Steps

Building an Odds Screen

Use WebSocket data in a real-time UI

Getting Live Odds

REST API for initial data load

Rate Limits

WebSocket does not count against REST limits

V1 to V2 Migration

Migrate from V1 WebSocket to V2