An official Python SDK is coming soon. In the meantime, this guide shows how to use TheRundown API directly with the
requests library for REST calls and websockets for real-time streaming.Installation
Copy
pip install requests websockets
Configuration
Copy
import os
API_KEY = os.environ.get("THERUNDOWN_API_KEY", "YOUR_API_KEY")
BASE_URL = "https://therundown.io/api/v2"
WS_URL = "wss://therundown.io/api/v2/ws/markets"
HEADERS = {
"X-TheRundown-Key": API_KEY,
}
Getting Sports
Copy
import requests
response = requests.get(f"{BASE_URL}/sports", headers=HEADERS)
response.raise_for_status()
sports = response.json()["sports"]
for sport in sports:
print(f"{sport['sport_id']}: {sport['sport_name']}")
Getting Events with Odds
Copy
import requests
from datetime import date
sport_id = 4 # NBA
today = date.today().isoformat()
response = requests.get(
f"{BASE_URL}/sports/{sport_id}/events/{today}",
headers=HEADERS,
params={
"market_ids": "1,2,3", # Moneyline, Spread, Total
"affiliate_ids": "19,23", # DraftKings, FanDuel
"main_line": "true",
}
)
response.raise_for_status()
data = response.json()
for event in data["events"]:
away = event["teams"][0]["name"]
home = event["teams"][1]["name"]
print(f"\n{away} @ {home}")
for market in event.get("markets", []):
print(f" {market['name']}:")
for participant in market["participants"]:
for line in participant["lines"]:
for aff_id, price in line["prices"].items():
p = price["price"]
display = "N/A" if p == 0.0001 else f"{p:+d}" if p > 0 else str(int(p))
line_val = f" ({line['line']})" if line.get("line") else ""
print(f" {participant['name']}{line_val}: {display} @ {aff_id}")
Filtering by Affiliate
Copy
# Only DraftKings lines
response = requests.get(
f"{BASE_URL}/sports/4/events/{date.today()}",
headers=HEADERS,
params={
"market_ids": "1,2,3",
"affiliate_ids": "19", # DraftKings only
"main_line": "true",
}
)
Getting Player Props
Copy
# NBA player points, rebounds, assists, 3PT
response = requests.get(
f"{BASE_URL}/sports/4/events/{date.today()}",
headers=HEADERS,
params={
"market_ids": "29,35,38,39", # Points, Rebounds, 3PT, Assists
"affiliate_ids": "19",
}
)
response.raise_for_status()
for event in response.json()["events"]:
print(f"\n{event['teams'][0]['name']} @ {event['teams'][1]['name']}")
for market in event.get("markets", []):
print(f" {market['name']}:")
for participant in market["participants"]:
for line in participant["lines"]:
price = line["prices"].get("19", {}).get("price", "N/A")
print(f" {participant['name']}: {line.get('line')} ({price})")
Historical Odds
Copy
event_id = "abc123"
# Full market history
response = requests.get(
f"{BASE_URL}/events/{event_id}/markets/history",
headers=HEADERS,
params={"affiliate_ids": "19"},
)
history = response.json()["history"]
for entry in history:
print(f"{entry['timestamp']}: {entry['market_name']} {entry['line']} @ {entry['price']}")
# Opening lines
openers = requests.get(
f"{BASE_URL}/events/{event_id}/openers",
headers=HEADERS,
params={"market_ids": "1,2,3"},
).json()
# Closing lines
closing = requests.get(
f"{BASE_URL}/events/{event_id}/closing",
headers=HEADERS,
params={"market_ids": "1,2,3"},
).json()
WebSocket Streaming
Copy
import asyncio
import json
import websockets
async def stream_odds(sport_ids="4", market_ids="1,2,3"):
url = (
f"{WS_URL}?key={API_KEY}"
f"&sport_ids={sport_ids}"
f"&market_ids={market_ids}"
)
async with websockets.connect(url) as ws:
print("Connected to WebSocket")
async for message in ws:
data = json.loads(message)
if data.get("meta", {}).get("type") == "heartbeat":
continue
event_id = data.get("event_id")
market = data.get("market_name", "Unknown")
print(f"Update: {event_id} - {market}")
for participant in data.get("participants", []):
for line in participant.get("lines", []):
for aff_id, price in line.get("prices", {}).items():
print(f" {participant['name']}: {price['price']} ({aff_id})")
asyncio.run(stream_odds())
WebSocket with Reconnection
Copy
import asyncio
import json
import random
import websockets
async def stream_with_reconnect(sport_ids="4", market_ids="1,2,3"):
url = (
f"{WS_URL}?key={API_KEY}"
f"&sport_ids={sport_ids}"
f"&market_ids={market_ids}"
)
reconnect_delay = 1.0
max_delay = 30.0
while True:
try:
async with websockets.connect(url) as ws:
print("WebSocket connected")
reconnect_delay = 1.0
async for message in ws:
data = json.loads(message)
if data.get("meta", {}).get("type") == "heartbeat":
continue
# Process update
print(f"Update: {data.get('event_id')} - {data.get('market_name')}")
except (websockets.ConnectionClosed, ConnectionError) as e:
jitter = random.uniform(0, 1)
delay = min(reconnect_delay + jitter, max_delay)
print(f"Disconnected ({e}). Reconnecting in {delay:.1f}s...")
await asyncio.sleep(delay)
reconnect_delay = min(reconnect_delay * 2, max_delay)
asyncio.run(stream_with_reconnect())
Error Handling
Copy
import requests
import time
def api_get(path, params=None, max_retries=3):
"""Make an API request with retry logic for rate limits."""
for attempt in range(max_retries):
response = requests.get(
f"{BASE_URL}{path}",
headers=HEADERS,
params=params,
)
if response.status_code == 200:
return response.json()
if response.status_code == 429:
wait = (2 ** attempt) + random.uniform(0, 1)
print(f"Rate limited. Retrying in {wait:.1f}s...")
time.sleep(wait)
continue
response.raise_for_status()
raise Exception("Max retries exceeded")
# Usage
data = api_get(
f"/sports/4/events/{date.today()}",
params={"market_ids": "1,2,3"},
)