Audience: LLM coding agents and developers
Scope: NFL-only usage of ESPN's public (undocumented) data APIs
Use cases: fantasy football, analytics, roster management, schedules, box scores, and player stats
- ESPN does not officially document or guarantee these APIs.
- Endpoints are public and widely used but may change without notice.
- All examples below are read-only HTTP GET requests.
- Rate limit conservatively and cache aggressively (daily where possible).
Base NFL league path used throughout:
https://sports.core.api.espn.com/v2/sports/football/leagues/nfl
/athletes?active=true&limit=500
GET https://sports.core.api.espn.com/v2/sports/football/leagues/nfl/athletes?active=true&limit=500
- Results are paginated
- Follow the
"next".$reffield until exhausted - Each athlete is returned as a
$refpointer
- Fetch athletes list
- Resolve each
$refto get full player metadata - Cache results (daily recommended)
{
"id": "4040715",
"fullName": "Patrick Mahomes",
"position": { "abbreviation": "QB" },
"team": { "$ref": ".../teams/12" },
"headshot": "https://a.espncdn.com/i/headshots/nfl/players/full/4040715.png"
}ESPN does not provide a true search endpoint.
- Build your own local index from
/athletes - Normalize names (lowercase, remove punctuation)
- Use fuzzy or prefix matching
Filter athletes client-side:
athlete.fullName.includes("mahomes")
GET /teams
Example:
https://sports.core.api.espn.com/v2/sports/football/leagues/nfl/teams
{
"id": "12",
"name": "Kansas City Chiefs",
"abbreviation": "KC",
"logos": [
{ "href": "https://a.espncdn.com/i/teamlogos/nfl/500/kc.png" }
]
}- Team abbreviation for matchup display
- Logos for UI
- Team ID for schedule and stats lookups
GET /seasons/{season}/types/2/weeks/{week}/events
Example (2024, Week 1):
https://sports.core.api.espn.com/v2/sports/football/leagues/nfl/seasons/2024/types/2/weeks/1/events
{
"id": "401671345",
"competitions": [
{
"competitors": [
{
"team": { "$ref": ".../teams/12" },
"homeAway": "home"
},
{
"team": { "$ref": ".../teams/24" },
"homeAway": "away"
}
]
}
]
}- Identify player team ID
- Find matching competitor in event
- The other competitor is the opponent
GET /events/{eventId}
Example:
https://sports.core.api.espn.com/v2/sports/football/leagues/nfl/events/401671345
"competitions": [
{
"competitors": [
{
"score": "31",
"winner": true
}
],
"status": {
"type": { "completed": true }
}
}
]- Final scores
- Win/loss
- Game completion status
GET /events/{eventId}/competitions/{competitionId}/boxscore
Example:
https://sports.core.api.espn.com/v2/sports/football/leagues/nfl/events/401671345/competitions/401671345/boxscore
{
"athlete": { "id": "4040715" },
"stats": [
"28/42",
"315",
"3",
"1"
]
}Stat meanings are defined by:
boxscore.playerStats[*].labels
Always read labels dynamically.
GET /athletes/{athleteId}/stats
Optional filters:
?season=2024?seasontype=2(regular season)
- Season totals
- Per-game averages
- Trend analysis
GET /teams/{teamId}/roster
GET /teams/{teamId}/depthcharts
- Starter vs backup
- Injury replacement logic
- Handcuff identification
GET /teams/{teamId}/injuries
"status": {
"type": "out",
"abbreviation": "O"
}- Lineup eligibility
- Late-scratch detection
GET /events/{eventId}/competitions/{competitionId}/odds
Useful for:
- Over/Under
- Implied team totals
- DFS optimization
- Cache athletes: daily
- Cache teams: weekly
- Cache schedule: per season
- Cache boxscores: immutable after completion
Avoid resolving $ref repeatedly—memoize aggressively.
| Entity | Stability |
|---|---|
| Athlete ID | Stable |
| Team ID | Stable |
| Event ID | Stable per season |
| Competition ID | Same as Event ID (NFL) |
For most fantasy apps, you need:
- Athlete ID
- Full name
- Position
- Team ID / abbreviation
- Headshot URL
- Weekly opponent
- Game status
- Player game stats
Everything above is derivable from these APIs.
- No official search endpoint
- Inconsistent stat ordering → always read labels
- Bye weeks have no events
- Postseason uses
seasontype=3
These rules are intended for LLM coding agents consuming ESPN NFL data.
- Always resolve
$refURLs before accessing entity fields. - Never assume stat ordering; always read
labelsalongsidestats. - Treat athlete, team, and event IDs as canonical keys.
- Expect missing data (bye weeks, injuries, inactive players).
- Cache aggressively and prefer stale data over refetching.
- Assume eventual consistency: game data may lag live events.
- Never hardcode week counts (international games, flexible scheduling).
Most fantasy workflows require joining these entities:
Athlete ──▶ Team ──▶ Event (Game) ──▶ Competition ──▶ Boxscore
| From | To | Key |
|---|---|---|
| Athlete | Team | athlete.team.$ref |
| Team | Event | competitors[*].team.id |
| Event | Competition | Same ID (NFL) |
| Competition | Boxscore | {eventId}/boxscore |
- Athlete → teamId
- Team → event for given week
- Event → opponent team
- Event → boxscore
- Boxscore → athlete stat line
Compute fantasy points for a QB in Week 5, 2024.
- Athlete ID:
4040715(Patrick Mahomes) - Position: QB
- Team ID resolved from athlete
GET /seasons/2024/types/2/weeks/5/events
Scan events where Mahomes' team appears as a competitor.
GET /events/{eventId}/competitions/{eventId}/boxscore
Locate Mahomes under:
boxscore.playerStats[*].athletes[*]
Example labels:
["C/ATT", "YDS", "TD", "INT"]
Corresponding stats:
["28/42", "315", "3", "1"]
| Stat | Value | Points |
|---|---|---|
| Passing Yards | 315 | 12.6 |
| Passing TDs | 3 | 12 |
| Interceptions | 1 | -2 |
Total: 22.6 fantasy points
| Endpoint | Volatility | Cache Recommendation |
|---|---|---|
/athletes |
Low | Daily |
/teams |
Very Low | Weekly / Season |
/seasons/*/weeks/*/events |
Medium | Per Week |
/events/{id} |
Medium | Until Final |
/boxscore |
High (live) | After Game Final |
/athletes/{id}/stats |
Medium | Daily |
/depthcharts |
Medium | Daily |
/injuries |
High | Hourly (game day) |
- IDs are stable across seasons
- Live games may briefly return partial stats
- Final boxscores are immutable
- Odds endpoints may disappear or lag
Minimum data needed per player per week:
- Athlete ID
- Position
- Team abbreviation
- Opponent abbreviation
- Game status (scheduled / live / final)
- Passing / rushing / receiving stats
All are derivable using:
/athletes/teams/seasons/*/weeks/*/events/events/{id}/boxscore
If you remember only three things:
- Follow
$reflinks - Read stat labels dynamically
- Cache everything you can
This API is powerful, unofficial, and absolutely sufficient for a full fantasy football platform.
End of document