/stream/futures/{sport}

Description

This endpoint provides an alternative to polling the /futures/odds endpoint and allows you to passively listen for updates. This endpoint leverages the SSE responses of the http protocol.

Best Practices

While our endpoint supports passing multiple leagues, we recommend grouping up to 10 leagues per connection.

Example Requests

Python

Requirements

import requests
from requests.exceptions import ChunkedEncodingError
import json
import sseclient # pip install sseclient-py

last_entry_id: str | None = None
while True:
    try:
        params = {
            "key": "1234-5678-124",
            "sportsbook": ["DraftKings", "BetMGM", "Fanatics"],
            "league": ["NBA", "NCAAB"],
						# "is_main": True,
        }
        if last_entry_id:
          params["last_entry_id"] = last_entry_id
      
        r = requests.get(
            "https://api.opticodds.com/api/v3/stream/futures/basketball",
            params=params,
            stream=True,
        )
        client = sseclient.SSEClient(r)
        for event in client.events():
            if event.event == "futures":
                data = json.loads(event.data)
                last_entry_id = data.get("entry_id")
                print("futures data", ":", data)
            elif event.event == "locked-futures":
                data = json.loads(event.data)
                last_entry_id = data.get("entry_id")
                print("locked-futures data", ":", data)
            else:
                print(event.event, ":", event.data)
    except ChunkedEncodingError as ex:
        print("Disconnected, attempting to reconnect...")
    except Exception as e:
        print("Error:", r.status_code, r.text)
        break

Node.js

const EventSource = require("eventsource");  // npm install eventsource

const url = "https://api.opticodds.com/api/v3/stream/futures/basketball";
const params = {
  key: "1234-5678-124",
  sportsbook: ["DraftKings", "BetMGM", "Fanatics"],
  league: ["NBA", "NCAAB"],
};
const lastEntryId = null;

function connectToStream() {
  // Construct the query string with repeated parameters
  const queryString = new URLSearchParams();
  queryString.append("key", params.key);
  params.sportsbook.forEach((sportsbook) =>
    queryString.append("sportsbook", sportsbook)
  );
  params.league.forEach((league) => queryString.append("league", league));
  if (lastEntryId) {
    queryString.append("last_entry_id", lastEntryId);
  }

  console.log(`${url}?${queryString.toString()}`);

  const eventSource = new EventSource(`${url}?${queryString.toString()}`);

  eventSource.onmessage = function (event) {
    try {
      const data = JSON.parse(event.data);
      console.log("message data:", data);
    } catch (e) {
      console.log("Error parsing message data:", e);
    }
  };

  eventSource.addEventListener("futures", function (event) {
    const data = JSON.parse(event.data);
    lastEntryId = data.entry_id;
    console.log("futures data:", data);
  });

  eventSource.addEventListener("locked-futures", function (event) {
    const data = JSON.parse(event.data);
    lastEntryId = data.entry_id;
    console.log("locked-futures data:", data);
  });

  eventSource.onerror = function (event) {
    console.error("EventSource failed:", event);
    eventSource.close();
    setTimeout(connectToStream, 1000); // Attempt to reconnect after 1 second
  };
}

connectToStream()

Example Events

Connected Event

event: connected
retry: 5000
data: ok go

Ping Event

event: ping
retry: 5000
data: 2024-08-28T18:57:49Z

Futures Event

When a future gets unsuspended or posted for the first time.

event: futures
id: 1730079534820-2
retry: 5000
data: {
  "entry_id": "1786100320255-0",
  "type": "futures",
  "data": [
    {
      "id": "type_wyndham_championship_2026_winner-sport_golf-league_pga",
      "sport": { "id": "golf", "name": "Golf" },
      "league": { "id": "pga", "name": "PGA" },
      "sportsbook": { "id": "prophet_x", "name": "Prophet X" },
      "market": "Wyndham Championship 2026 Winner",
      "market_id": "wyndham_championship_2026_winner",
      "future_type": "PLAYER",
      "tournament": { "id": "5D5012F4946104B3", "name": "Wyndham Championship 2026" },
      "is_live": true,
      "start_date": "2026-08-06T10:50:00Z",
      "odds": [
        {
          "id": "pga:prophet_x:wyndham_championship_2026_winner:max_greyserman",
          "name": "Max Greyserman",
          "selection": "Max Greyserman",
          "normalized_selection": "max_greyserman",
          "grouping_key": null,
          "is_main": true,
          "selection_line": null,
          "player_id": "FFB12766B9F6",
          "team_id": null,
          "price": 706,
          "points": null,
          "timestamp": 1786100320.2449124
        }
      ]
    }
  ]
}

Locked Futures Event

When a future gets suspended or taken off the board.

event: locked-futures
id: 1730079527180-0
retry: 5000
data: {
  "entry_id": "1786100320255-0",
  "type": "locked-futures",
  "data": [
    {
      "id": "type_wyndham_championship_2026_winner-sport_golf-league_pga",
      "sport": { "id": "golf", "name": "Golf" },
      "league": { "id": "pga", "name": "PGA" },
      "sportsbook": { "id": "prophet_x", "name": "Prophet X" },
      "market": "Wyndham Championship 2026 Winner",
      "market_id": "wyndham_championship_2026_winner",
      "future_type": "PLAYER",
      "tournament": { "id": "5D5012F4946104B3", "name": "Wyndham Championship 2026" },
      "is_live": true,
      "start_date": "2026-08-06T10:50:00Z",
      "odds": [
        {
          "id": "pga:prophet_x:wyndham_championship_2026_winner:max_greyserman",
          "name": "Max Greyserman",
          "selection": "Max Greyserman",
          "normalized_selection": "max_greyserman",
          "grouping_key": null,
          "is_main": true,
          "selection_line": null,
          "player_id": "FFB12766B9F6",
          "team_id": null,
          "price": 706,
          "points": null,
          "timestamp": 1786100320.2449124
        }
      ]
    }
  ]
}
Path Params
string
required
Query Params
string
sportsbook
array of strings
required
sportsbook*
league
array of strings
Defaults to []
league
market
array of strings
Defaults to []
market
future_id
array of strings
Defaults to []
future_id
tournament_id
array of strings
Defaults to []
tournament_id
player_id
array of strings
Defaults to []
player_id
team_id
array of strings
Defaults to []
team_id
string
string
string
string
Defaults to false
string
Defaults to false
string
Defaults to false
string
Defaults to false
Response

Language
Credentials
LoadingLoading…
Response
Click Try It! to start a request and see the response here! Or choose an example:
text/event-stream