Python sports odds API guide
Last updated 2026-10-30
Use requests: send GET https://api.bettingdataapi.com/odds/ with sport_key and your key in the x-api-key header, then read data. No SDK is required. The example below runs as-is with a free key.
Install and authenticate
pip install requests pandas export BETTING_DATA_API_KEY=YOUR_API_KEY
Fetch NBA moneylines
# pip install requests
import os
import requests
API_KEY = os.environ["BETTING_DATA_API_KEY"] # keep keys out of source code
BASE = "https://api.bettingdataapi.com"
resp = requests.get(
f"{BASE}/odds/",
headers={"x-api-key": API_KEY},
params={"sport_key": "basketball_nba", "markets": "h2h"},
timeout=10,
)
resp.raise_for_status()
body = resp.json()
for event in body["data"]:
print(event["home_team"], "vs", event["away_team"], event["start_time"])
for book in event.get("books", []):
prices = [(o["name"], o["price"]) for o in book["outcomes"]]
print(" ", book["book"], book["market"], prices)If the league has no upcoming games (between seasons) the data array is empty; try another sport_key from GET /sports/.
Handle quota and errors
import time
import requests
def get_json(path, api_key, params=None, attempts=3):
"""GET with handling for 429 (quota or temporary block) and network errors."""
for attempt in range(attempts):
try:
r = requests.get(f"https://api.bettingdataapi.com{path}", headers={"x-api-key": api_key},
params=params or {}, timeout=10)
except requests.RequestException:
time.sleep(2 ** attempt)
continue
if r.status_code == 429:
wait = int(r.headers.get("Retry-After", "60"))
# A daily-quota 429 can mean hours until 00:00 UTC. Do not spin.
raise RuntimeError(f"Rate limited; retry after {wait} s: {r.json().get('detail')}")
if r.status_code >= 400:
raise RuntimeError(f"HTTP {r.status_code}: {r.json().get('detail')}")
print("remaining today:", r.headers.get("X-RateLimit-Remaining"))
return r.json()
raise RuntimeError("network error after retries")Best price per outcome with pandas
import pandas as pd
body = get_json("/odds/", API_KEY, {"sport_key": "basketball_nba", "markets": "h2h"})
rows = [
{
"event_id": e["event_id"], "start_time": e["start_time"],
"home": e["home_team"], "away": e["away_team"],
"book": b["book"], "outcome": o["name"], "price": o["price"],
}
for e in body["data"] for b in e.get("books", []) for o in b["outcomes"]
]
df = pd.DataFrame(rows)
best = df.loc[df.groupby(["event_id", "outcome"])["price"].idxmax()]
print(best[["home", "away", "outcome", "book", "price"]])Or let the API do it: GET /best-lines/?sport_key=basketball_nba.
Historical data
Business plan. Cursor pagination example: historical odds in Python.
Next
Try it with a free key
25 requests per day, NFL, NBA and MLB moneylines, no credit card.
Get a free API key