Understat xG League & Team Stats Scraper
Pricing
Pay per event
Understat xG League & Team Stats Scraper
Scrape expected goals (xG), xGA, NPxG, PPDA and full standings for all 6 understat.com leagues across any available season. One row per team per season, aggregated from match-level history.
Pricing
Pay per event
Rating
0.0
(0)
Developer
BowTiedRaccoon
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
4 days ago
Last modified
Categories
Share
Scrape expected goals (xG), non-penalty xG (NPxG), xGA, PPDA, deep completions, and full W/D/L standings from understat.com — the canonical open xG data source for European football.
Covers all 6 understat leagues (EPL, La Liga, Bundesliga, Serie A, Ligue 1, RFPL) across every available season (2014–present). One aggregated row per team per season.
Features
- 6 leagues in a single run: EPL, La Liga, Bundesliga, Serie A, Ligue 1, RFPL
- All seasons from 2014/2015 onwards — back-fill historical data or track the current season
- Match-level aggregation — sums each team's per-match xG, xGA, NPxG, PPDA, deep completions into a clean season total
- PPDA ratio computed correctly as
passes_attempted / defensive_actions(lower = more pressing) - No proxy required — understat.com is open and datacenter-friendly
Use Cases
- Build football analytics dashboards (xG vs actual goals by team)
- Feed betting or fantasy football models with expected-goals standings
- Compare xG performance across leagues or seasons for research / Substack writing
- Backtest xG-based table predictions
Input
| Field | Type | Default | Description |
|---|---|---|---|
leagues | array | (all 6) | Which leagues to scrape. Valid values: EPL, La_liga, Bundesliga, Serie_A, Ligue_1, RFPL |
seasons | array | ["2024"] | Season start years to scrape. E.g. ["2024"] = 2024/2025, ["2023","2024"] = two seasons |
maxItems | integer | 10 | Maximum records to return (0 = unlimited) |
Example input — EPL last 3 seasons
{"leagues": ["EPL"],"seasons": ["2022", "2023", "2024"],"maxItems": 0}
Example input — All leagues, current season
{"leagues": ["EPL", "La_liga", "Bundesliga", "Serie_A", "Ligue_1", "RFPL"],"seasons": ["2024"],"maxItems": 0}
Output
One record per team per season:
{"season": "2024/2025","league": "EPL","team_name": "Aston Villa","team_id": "71","games_played": 38,"wins": 19,"draws": 9,"losses": 10,"goals_for": 58,"goals_against": 51,"xg_for": 66.39,"xg_against": 55.0,"npxg": 61.71,"deep": 285,"ppda": 12.39,"oppda": 9.96,"points": 66,"source": "https://understat.com/league/EPL"}
Field reference
| Field | Description |
|---|---|
season | Season label (e.g. 2024/2025) |
league | League name |
team_name | Team name as shown on understat.com |
team_id | Understat internal team ID |
games_played | Total matches played |
wins / draws / losses | Season record |
goals_for / goals_against | Goals scored / conceded |
xg_for | Total expected goals (xG) |
xg_against | Total expected goals against (xGA) |
npxg | Non-penalty xG (excludes spot-kick quality) |
deep | Deep completions — passes into the opposition's danger zone |
ppda | Passes per defensive action by opponent (lower = team presses more aggressively) |
oppda | Opponent PPDA — how much the team is pressed |
points | Total league points |
source | Source URL |
Notes
- Data is sourced from understat.com's internal XHR API (
/getLeagueData/{league}/{season}), which returns gzip-compressed JSON. All values are aggregated from per-match history. - PPDA and OPPDA are
nullif a team played 0 defensive actions (extremely rare, data gap seasons only). - The RFPL (Russian Premier League) data coverage may be incomplete for recent seasons.
maxItemscounts across all requested league × season combinations. Set to0to scrape everything.