This post first appeared on SportsAPIs.dev.
What is the ESPN API?
The ESPN API refers to a collection of undocumented endpoints that developers have discovered by reverse-engineering ESPN's web and mobile applications. While ESPN doesn't offer an official public API, these hidden endpoints provide free access to a wealth of sports data:
- Real-time Scores: Live scoreboards across all major sports
- Team & Player Stats: Comprehensive statistics and performance data
- News & Headlines: Latest sports news articles
- Standings & Rankings: League standings and power rankings
- Game Summaries: Play-by-play data, box scores, and game details
- Schedules: Upcoming games and event calendars
Key Benefits
- Free Access: No subscription or payment required
- No Authentication: No API key or token needed
- JSON Responses: Clean, structured data format
- Comprehensive Coverage: All major US sports plus international soccer
API Base URLs
ESPN's hidden API uses several base domains, each serving different types of data:
| Domain | Purpose |
|---|---|
site.api.espn.com |
General site data - scores, news, teams, standings |
sports.core.api.espn.com |
Core sports data - athletes, detailed stats, odds, venues |
site.web.api.espn.com |
Web-specific APIs - search, game summaries, athlete overviews |
fantasy.espn.com |
Fantasy sports data (may require authentication for private leagues) |
cdn.espn.com |
Content delivery - optimized for speed and caching |
Supported Sports & Leagues
The ESPN API covers a wide range of sports and leagues. Here are the most commonly used:
Football
- NFL (
football/nfl) - College Football (
football/college-football)
Basketball
- NBA (
basketball/nba) - WNBA (
basketball/wnba) - Men's CBB (
basketball/mens-college-basketball) - Women's CBB (
basketball/womens-college-basketball)
Baseball
- MLB (
baseball/mlb) - College Baseball (
baseball/college-baseball)
Hockey
- NHL (
hockey/nhl)
Soccer
- MLS (
soccer/usa.1) - Premier League (
soccer/eng.1) - La Liga (
soccer/esp.1) - And many more...
Other Sports
- Golf
- Tennis
- MMA/UFC
- Racing
Key Endpoints
Most endpoints follow a consistent pattern: /apis/site/v2/sports/{sport}/{league}/{endpoint}
| Endpoint | Description | Example |
|---|---|---|
/scoreboard |
Live scores and game status | /sports/football/nfl/scoreboard |
/news |
Latest news articles | /sports/basketball/nba/news |
/teams |
All teams in a league | /sports/baseball/mlb/teams |
/teams/{id} |
Specific team details | /sports/football/nfl/teams/12 |
/summary |
Game details with box score | /sports/basketball/nba/summary?event=401234567 |
/standings |
League standings | /sports/hockey/nhl/standings |
/rankings |
Power rankings (college) | /sports/football/college-football/rankings |
Common Query Parameters
| Parameter | Description | Example |
|---|---|---|
dates |
One day (YYYYMMDD) or a whole month (YYYYMM). Ranges return HTTP 400, see date filters | ?dates=20251201 |
limit |
Maximum number of results | ?limit=50 |
season |
Specific season year | ?season=2025 |
seasontype |
Season type (1=preseason, 2=regular, 3=postseason) | ?seasontype=2 |
week |
Specific week (football) | ?week=10 |
groups |
Conference/division filter |
?groups=80 (FBS) |
ESPN Game Summary Endpoint (summary?event=)
The summary endpoint returns everything ESPN has on one game: box score, play-by-play, win probability, betting lines, injuries and news. It is the ESPN endpoint developers search for most, and it has the least documentation.
https://site.api.espn.com/apis/site/v2/sports/{sport}/{league}/summary?event={eventId}
https://site.web.api.espn.com/apis/site/v2/sports/{sport}/{league}/summary?event={eventId}
Both hosts returned the same JSON for the same game in our tests. Pick either one.
Where the event ID comes from
Every scoreboard response has an events array, and each event has an id. That id is your event value. For example, basketball/nba/scoreboard?dates=20250605 returns Game 1 of the 2025 NBA Finals (Pacers at Thunder) with id 401766122, so the summary URL is basketball/nba/summary?event=401766122. A missing or wrong ID returns HTTP 400.
What the summary response contains
| Key | What it holds |
|---|---|
header |
Teams, score, date, status and which side is home |
boxscore |
Team stats (teams) and player stat lines (players) |
plays |
Play-by-play. Our NBA Finals test game had 503 plays, each with an id, text, period and clock
|
winprobability |
Home win probability after every play (see below) |
leaders |
Top performer per stat category for each team |
odds, pickcenter, againstTheSpread
|
Betting lines and against-the-spread records. Often empty for finished games: both were empty for our NBA test game |
injuries |
Injury report for both teams |
gameInfo |
Venue, attendance and officials |
standings, seasonseries
|
League table and the season head-to-head |
news, article, videos
|
Recap and highlight content |
Other sports use the same path with their own sport and league. Football responses add drives and scoringPlays. Soccer responses add keyEvents (a match timeline), commentary and rosters. A 2026 World Cup match looks like soccer/fifa.world/summary?event=760506 (Portugal vs Spain).
ESPN Win Probability Data
ESPN publishes a win probability for every play, and there are two ways to get it. Use the summary endpoint unless you have a reason not to.
From the summary endpoint
The winprobability array has one row per play:
{"homeWinPercentage": 0.665, "tiePercentage": 0.0, "playId": "4017661224"}
Values are fractions, not percents. The away side's chance is 1 - homeWinPercentage - tiePercentage. To see which play moved the number, match playId to the id of an entry in plays:
import requests
event_id = '401766122' # 2025 NBA Finals, Game 1
url = f'https://site.api.espn.com/apis/site/v2/sports/basketball/nba/summary?event={event_id}'
data = requests.get(url).json()
plays = {p['id']: p for p in data['plays']}
for row in data['winprobability'][-5:]:
play = plays.get(row['playId'], {})
clock = play.get('clock', {}).get('displayValue', '')
print(f"{row['homeWinPercentage']:.1%} Q{play.get('period', {}).get('number')} {clock} {play.get('text', '')}")
From the core API
https://sports.core.api.espn.com/v2/sports/basketball/leagues/nba/events/{eventId}/competitions/{eventId}/probabilities
For NBA games the competition ID is the same as the event ID. This version is paginated (?limit= and ?page=) and each item is a $ref link, so every play costs one more request. The records do carry extra fields: awayWinPercentage, spreadCoverProbHome and totalOverProb. The spread and total fields were all 0 in our test, so the summary array gives you the same useful numbers in one call.
ESPN Scoreboard URLs by League
Every URL below returned HTTP 200 when we tested it. With no dates parameter you get today's games, or the current week for football.
| League | Scoreboard URL |
|---|---|
| NBA | https://site.api.espn.com/apis/site/v2/sports/basketball/nba/scoreboard |
| NFL | https://site.api.espn.com/apis/site/v2/sports/football/nfl/scoreboard |
| College football | https://site.api.espn.com/apis/site/v2/sports/football/college-football/scoreboard |
| MLB | https://site.api.espn.com/apis/site/v2/sports/baseball/mlb/scoreboard |
| NHL | https://site.api.espn.com/apis/site/v2/sports/hockey/nhl/scoreboard |
| Premier League | https://site.api.espn.com/apis/site/v2/sports/soccer/eng.1/scoreboard |
| La Liga | https://site.api.espn.com/apis/site/v2/sports/soccer/esp.1/scoreboard |
| FIFA World Cup | https://site.api.espn.com/apis/site/v2/sports/soccer/fifa.world/scoreboard |
| Every soccer league | https://site.api.espn.com/apis/site/v2/sports/soccer/all/scoreboard |
| PGA Tour | https://site.api.espn.com/apis/site/v2/sports/golf/pga/scoreboard |
Cricket is the odd one out. It takes a numeric league ID in place of a slug: cricket/8048/scoreboard returned a match, while cricket/scoreboard returned 404.
Date filters
| Format | Example | What we got back |
|---|---|---|
| YYYYMMDD | basketball/nba/scoreboard?dates=20250605 |
One day: Game 1 of the 2025 NBA Finals |
| YYYYMM | basketball/nba/scoreboard?dates=202506 |
A whole month: all 7 games of the 2025 NBA Finals |
| YYYYMM | soccer/fifa.world/scoreboard?dates=202606 |
79 World Cup matches from June 2026 |
| Season + week | football/nfl/scoreboard?dates=2025&seasontype=2&week=1 |
Week 1 of the 2025 NFL regular season (16 games) |
| YYYYMMDD-YYYYMMDD | basketball/nba/scoreboard?dates=20250601-20250630 |
HTTP 400. Ranges failed for every league we tried |
Need a range? Request it month by month and filter the dates yourself.
Code Examples
Here are practical examples for fetching data from the ESPN API:
Get NFL Scores
// Fetch today's NFL scores
fetch('https://site.api.espn.com/apis/site/v2/sports/football/nfl/scoreboard')
.then(response => response.json())
.then(data => {
data.events.forEach(game => {
console.log(game.name, game.status.type.description);
});
});
import requests
# Fetch today's NFL scores
url = 'https://site.api.espn.com/apis/site/v2/sports/football/nfl/scoreboard'
response = requests.get(url)
data = response.json()
for game in data['events']:
print(game['name'], game['status']['type']['description'])
# Fetch today's NFL scores
curl 'https://site.api.espn.com/apis/site/v2/sports/football/nfl/scoreboard'
Get NBA Game Summary
// Fetch detailed game data (replace eventId with actual game ID)
const eventId = '401584793';
const url = `https://site.web.api.espn.com/apis/site/v2/sports/basketball/nba/summary?event=${eventId}`;
fetch(url)
.then(response => response.json())
.then(data => {
console.log('Boxscore:', data.boxscore);
console.log('Play-by-play:', data.plays);
});
import requests
# Fetch detailed game data
event_id = '401584793'
url = f'https://site.web.api.espn.com/apis/site/v2/sports/basketball/nba/summary?event={event_id}'
response = requests.get(url)
data = response.json()
print('Boxscore:', data.get('boxscore'))
print('Play-by-play:', data.get('plays'))
Get Scores for Specific Date
// Fetch MLB scores for a specific date
const date = '20251001'; // YYYYMMDD format
fetch(`https://site.api.espn.com/apis/site/v2/sports/baseball/mlb/scoreboard?dates=${date}`)
.then(response => response.json())
.then(data => console.log(data));
ESPN API 403 and 400 Errors
403 Forbidden
ESPN serves these endpoints through a CDN that blocks some scripted traffic. In our tests, a script that sent a copied Chrome User-Agent header got 403 on every request. The same URLs returned 200 with the default headers of curl, Python requests and Node fetch. If your script sets a browser User-Agent, delete that header first.
If the 403 continues after that, slow down and cache what you fetch. ESPN publishes no rate limit, so there is no safe number to aim for.
400 Bad Request
-
No valid event ID:
summaryneeds?event=with an id from a scoreboard response -
A date range:
?dates=20250601-20250630fails. Use one day or one month
200 with an empty events array
That means no games on that date, not a broken endpoint. Try a date you know had games.
Limitations & Considerations
Important: Unofficial API
The ESPN API is not officially supported. Keep these limitations in mind:
- No Official Documentation: These endpoints are undocumented and discovered through reverse engineering
- No Stability Guarantee: ESPN can modify or remove endpoints at any time without notice
- Unknown Rate Limits: There's no published information about rate limiting or usage quotas
- No Support: ESPN does not provide developer support for these endpoints
- Terms of Service: Heavy automated usage may violate ESPN's terms of service
Recommendations
- For Hobby Projects: The ESPN API works great for personal projects, learning, and experimentation
- For Production Apps: Consider official sports data providers for applications requiring guaranteed uptime and support
- Error Handling: Implement robust error handling since endpoints may change unexpectedly
- Caching: Cache responses to reduce API calls and improve reliability
Community Resources
These community-maintained resources provide additional documentation and tools for working with the ESPN API:
- ESPN API GitHub Repository: Comprehensive community documentation with endpoint references and examples
- ESPN Hidden API Gist: Original endpoint discovery documentation with URL patterns for multiple sports
- ESPN API Tutorial: Step-by-step guide on accessing ESPN's free sports data endpoints
- ESPN Hidden API Guide: Getting started guide with best practices and common use cases
- GPWA Forum Discussion: Community forum thread with additional endpoint discoveries and tips
Frequently Asked Questions
Does ESPN have a free API?
ESPN does not offer an official public API, but developers have discovered undocumented endpoints that provide free access to scores, stats, news, and standings data. These hidden APIs require no authentication or API key, making them accessible for personal projects and experimentation.
Is the ESPN API official?
No. The ESPN API endpoints discussed here are unofficial and undocumented. They were discovered by reverse-engineering ESPN's web and mobile applications. ESPN does not provide official API documentation, support, or any service level agreements for these endpoints.
What sports does the ESPN API cover?
The ESPN API covers NFL, NBA, MLB, NHL, WNBA, college football, men's and women's college basketball, college baseball, MLS, international soccer leagues (Premier League, La Liga, Bundesliga, Serie A, etc.), golf, tennis, MMA/UFC, racing, and many other sports.
Do I need an API key for the ESPN API?
No. The ESPN hidden API endpoints are publicly accessible and do not require authentication, API keys, or tokens. You can make simple HTTP GET requests to retrieve data. However, private fantasy league data may require authentication cookies.
Is the ESPN API reliable for production use?
The ESPN API is not recommended for production applications that require guaranteed stability and uptime. Since these are unofficial endpoints, ESPN can modify or remove them at any time without notice. For production applications, consider using official sports data providers that offer documentation, support, and SLAs.
How do I get NBA scores from the ESPN API?
To get NBA scores, make a GET request to: https://site.api.espn.com/apis/site/v2/sports/basketball/nba/scoreboard. The response includes all games for the current day with scores, team info, and game status. Add ?dates=YYYYMMDD to get scores for a specific date.
What is the difference between site.api.espn.com and site.web.api.espn.com?
For the game summary endpoint, nothing that matters. Both hosts returned the same JSON for the same event in our tests. site.web.api.espn.com also serves some web-only data, such as search and athlete overviews.
How do I get win probability from the ESPN API?
Call the summary endpoint for a game, for example https://site.api.espn.com/apis/site/v2/sports/basketball/nba/summary?event=401766122, and read the winprobability array. Each row has homeWinPercentage, tiePercentage and a playId that matches a play in the plays array. The full example is in the win probability section above.
Why does the ESPN API return 403 Forbidden?
In our tests, ESPN returned 403 when a script sent a copied browser User-Agent header. Remove that header and let your HTTP client send its default. The same requests then returned 200. The 403 and 400 errors section above has more.
Does the ESPN API have a rate limit?
ESPN does not publish one. We did not hit a limit during our tests, but heavy traffic can get blocked without warning. Cache responses and do not poll faster than your app needs.
If you outgrow the hidden endpoints, compare the official providers side by side in the SportsAPIs.dev directory.
Top comments (0)