One API key, one JSON shape, every sport we carry. Pick a sport to see what the board holds right now.
Measured live at 2026-09-09 21:53 UTC. This page is regenerated from the API, so the numbers move.
Sports
Quick start: sports and tournaments
Copy it as it stands. It was run against the live API before this page rendered.
import datetime as dt
import time
import requests
API_KEY = "YOUR_API_KEY"
BASE_URL = "https://api.oddspapi.io/v4"
def get(path, params):
"""The key is the query parameter apiKey, never a header.
Read the status code first: a 429 body is valid JSON too."""
query = dict(params, apiKey=API_KEY)
for attempt in range(3):
response = requests.get(f"{BASE_URL}/{path}", params=query, timeout=180)
if response.status_code == 429:
time.sleep(5 * (attempt + 1))
continue
if response.status_code == 404:
return None
response.raise_for_status()
return response.json()
raise RuntimeError("rate limited on /" + path)
# Every sport we carry, with the id the rest of the API keys on.
sports = get("sports", {})
print(len(sports), "sports")
# Tournaments for one sport. futureFixtures above zero proves a circuit is
# live. futureFixtures of zero proves nothing on its own, so confirm a
# circuit from /fixtures grouped on tournamentId.
tournaments = get("tournaments", {"sportId": 10})
live = [t for t in tournaments if t["futureFixtures"] > 0]
live.sort(key=lambda t: -t["futureFixtures"])
for tournament in live[:5]:
print(tournament["tournamentId"], tournament["tournamentName"],
tournament["categoryName"], tournament["futureFixtures"])
Run against the live API at 2026-09-09 21:53 UTC. First line of output: 69 sports
What each sport holds right now
| Sport | sportId | Tournaments with fixtures | Fixtures, next 9 days | Market families | Tournament pages |
|---|---|---|---|---|---|
| Football | 10 |
548 | 5,936 | 152 | Premier League, LaLiga, Bundesliga, Serie A, Ligue 1, UEFA Champions League, MLS, Brasileiro Serie A, Eredivisie, Saudi Pro League |
| Basketball | 11 |
77 | 331 | 84 | NBA, WNBA, Euroleague |
| Tennis | 12 |
29 | 1,089 | 71 | Tournaments run per event, so start from the sport page |
| Baseball | 13 |
19 | 460 | 98 | MLB |
| American Football | 14 |
4 | 327 | 100 | NFL, NCAA, Regular Season |
| Ice Hockey | 15 |
40 | 496 | 145 | NHL |
| MMA | 20 |
5 | 18 | 5 | Tournaments run per event, so start from the sport page |
| Esports | 17, 18, 16 |
13 | 82 | 8 | League of Legends, Counter-Strike 2 |
Every figure in this table was read from the API at 2026-09-09 21:53 UTC.
Questions developers ask
How do I list every sport?
GET /v4/sports with the key as the query parameter apiKey. Every other endpoint keys on the sportId it returns.
Is the free tier a different feed?
No. The free tier reads the same endpoints, the same fixture ids and the same JSON shape as a paid key. It differs on request volume.
Why do two bookmakers show the same price so often?
Because several distinct slugs ship one feed. cloneOf does not track all of it, so group on the price tuple before you average or count. Every board count on these pages carries both numbers, the count of bookmakers and the count of independent prices.
One key, every sport on this page
The same fixture id carries through /v4/fixtures, /v4/odds,
/v4/historical-odds, /v4/scores and /v4/settlements.
