OrbTop

Understat xG League & Team Stats Scraper

SPORTSDEVELOPER TOOLS

Independent tool. Trademarks referenced on this page are the property of their respective owners and are used only to identify the public website this tool reads. No affiliation or endorsement.

Understat xG League & Team Stats Scraper

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)

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,
  "npxg_against": 52.4,
  "npxg_diff": 9.31,
  "deep": 285,
  "ppda": 12.39,
  "oppda": 9.96,
  "points": 66,
  "xpoints": 63.2,
  "xg_diff": -8.39,
  "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)
npxg_against Non-penalty xG conceded
npxg_diff Non-penalty xG differential (npxg minus npxg_against)
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
xpoints Expected points — understat's xG-based points model
xg_diff Goals scored minus expected goals (finishing over/underperformance)
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 null if 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.
  • maxItems counts across all requested league × season combinations. Set to 0 to scrape everything.