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 connections do not count against your REST API rate limit.

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 market updates in the same structure as the V2 REST API.

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 representing a market update for a specific event:
{
  "event_id": "abc123",
  "sport_id": 4,
  "market_id": 2,
  "market_name": "Point Spread",
  "period_id": 0,
  "participants": [
    {
      "participant_id": 1,
      "name": "Boston Celtics",
      "lines": [
        {
          "line": -4.0,
          "is_main": true,
          "prices": {
            "19": { "price": -110, "affiliate_id": 19, "timestamp": "2026-02-12T18:30:00Z" }
          }
        }
      ]
    }
  ],
  "meta": {
    "type": "market_update",
    "timestamp": "2026-02-12T18:30:00Z"
  }
}

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 data = JSON.parse(event.data);

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

  // Process market update
  console.log(
    `Update: event=${data.event_id} market=${data.market_name}`
  );

  for (const participant of data.participants || []) {
    for (const line of participant.lines || []) {
      for (const [affId, price] of Object.entries(line.prices || {})) {
        console.log(
          `  ${participant.name}: ${line.line} @ ${price.price} (${affId})`
        );
      }
    }
  }
};

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

ws.onclose = (event) => {
  console.log(`WebSocket closed: code=${event.code} reason=${event.reason}`);
};

V2 Hedge WebSocket

The Hedge WebSocket delivers pre-computed hedge and arbitrage opportunities. These are pairs (or sets) of bets across different sportsbooks that guarantee a profit or minimize risk.

Connection

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

Filter Parameters

ParameterTypeDescription
keystringRequired. Your API key
sport_idsstringComma-separated sport IDs
market_idsstringComma-separated market IDs

Example Message

{
  "event_id": "abc123",
  "sport_id": 4,
  "market_id": 1,
  "market_name": "Moneyline",
  "bets": [
    {
      "affiliate_id": 19,
      "affiliate_name": "DraftKings",
      "participant_name": "Boston Celtics",
      "price": 150,
      "wager": 100.00,
      "payout": 250.00
    },
    {
      "affiliate_id": 23,
      "affiliate_name": "FanDuel",
      "participant_name": "Los Angeles Lakers",
      "price": -140,
      "wager": 145.00,
      "payout": 248.57
    }
  ],
  "profit_pct": 2.1,
  "meta": {
    "type": "hedge_update",
    "timestamp": "2026-02-12T18:30:00Z"
  }
}
const ws = new WebSocket(
  `wss://therundown.io/api/v2/ws/hedge?key=${API_KEY}&sport_ids=4`
);

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

  console.log(`Hedge opportunity: ${data.market_name} (${data.profit_pct}% profit)`);
  for (const bet of data.bets || []) {
    console.log(
      `  ${bet.affiliate_name}: ${bet.participant_name} @ ${bet.price} ($${bet.wager})`
    );
  }
};

V1 WebSocket (Legacy)

The V1 WebSocket delivers line updates in the legacy flat format. 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

Example Message

{
  "event_id": "abc123",
  "affiliate_id": 19,
  "moneyline": { "money_line_away": 150, "money_line_home": -170 },
  "spread": { "point_spread_away": 3.5, "point_spread_away_money": -110 },
  "total": { "total_over": 220.5, "total_over_money": -110 }
}

Heartbeat Handling

All WebSocket endpoints send periodic heartbeat messages to keep the connection alive. Your client must handle these to avoid treating them as data updates.
{
  "meta": {
    "type": "heartbeat",
    "timestamp": "2026-02-12T18:30:00Z"
  }
}
If you do not receive a heartbeat within 60 seconds, the connection may be stale. Close and reconnect.

Heartbeat Handler Pattern

let lastHeartbeat = Date.now();

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

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

  // Process actual data update
  handleUpdate(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 data = JSON.parse(event.data);
      if (data.meta?.type === "heartbeat") return;
      onMessage(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`,
  (data) => {
    console.log("Market update:", data.event_id, data.market_name);
  }
);

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