ESPN doesn't have an official public API — but they have an undocumented one that powers their website. It's free, returns JSON, and covers every major sport. Here's how to use it.
The Endpoints
ESPN's scoreboard API follows a consistent pattern:
https://site.api.espn.com/apis/site/v2/sports/{sport}/{league}/scoreboard?dates={YYYYMMDD}
Examples:
- NBA: basketball/nba/scoreboard?dates=20261225
- NFL: football/nfl/scoreboard?dates=20261015
- NCAAMB: basketball/mens-college-basketball/scoreboard?dates=20260315
Basic Python Example
import requests
url = "https://site.api.espn.com/apis/site/v2/sports/basketball/nba/scoreboard"
resp = requests.get(url, params={"dates": "20261225"})
data = resp.json()
for event in data["events"]:
home = event["competitions"][0]["competitors"][0]
away = event["competitions"][0]["competitors"][1]
print(f"{away['team']['abbreviation']} @ {home['team']['abbreviation']}: "
f"{away['score']}-{home['score']}")
What You Can Extract
Each game event contains: - Scores: home/away, by period - Game clock: period, time remaining, status (scheduled/in-progress/final) - Team info: full name, abbreviation, logo URL, record - Win probability: ESPN's own WP estimate (useful as a feature, not a trading signal) - Leaders: top scorers, rebounders, assists
Scaling Up: Async Scraping
For production use, you need async requests to scrape thousands of games efficiently:
import aiohttp
import asyncio
async def fetch_scoreboard(session, sport, league, date):
url = f"https://site.api.espn.com/apis/site/v2/sports/{sport}/{league}/scoreboard"
async with session.get(url, params={"dates": date}) as resp:
return await resp.json()
async def scrape_season(sport, league, dates):
async with aiohttp.ClientSession() as session:
tasks = [fetch_scoreboard(session, sport, league, d) for d in dates]
return await asyncio.gather(*tasks)
Rate limiting: ESPN will throttle you if you're too aggressive. Add await asyncio.sleep(0.5) between requests. Our production scraper handles this automatically with exponential backoff.
From Data to Trading
Raw ESPN data is the foundation of everything we build at ZenHodl:
- 60,000+ games scraped across 7 sports (2020-2026)
- 25.6 million in-game state snapshots (score + time + period every few seconds)
- Elo ratings computed from game results for 900+ teams
- WP models trained on these snapshots to predict fair win probability
The gap between our model's fair probability and Polymarket's ask price is what creates tradeable edges.
Want the Full Pipeline?
This blog post covers the basics. The ZenHodl course Module 1 goes deep: - Async scraper with retry logic and rate limiting - Multi-sport support (NBA, NFL, NCAAMB, CFB, NHL, MLB) - Efficient Parquet storage - Data quality checks and validation - Module 1 is completely free — download it here
Or if you just want the data without scraping it yourself, check out our ESPN WP Training Dataset — 25.6M rows, ready to use.
Part of the ZenHodl blog. We write about sports analytics, prediction markets, and building trading bots with Python.