Understat xG & Shot Data - Football Analytics
Pricing
from $0.80 / 1,000 player-season aggregate rows
Understat xG & Shot Data - Football Analytics
Scrapes Understat: player/team season aggregates, per-match xG/PPDA/win-probabilities, shot-level data with x/y coords + xG, situation/formation/game-state breakdowns, player career histories, and fixtures across the Big 5 + RFPL (2014–2025). Multi-season + multi-league in a single run.
Pricing
from $0.80 / 1,000 player-season aggregate rows
Rating
5.0
(1)
Developer
Omar Eldeeb
Actor stats
2
Bookmarked
6
Total users
4
Monthly active users
9 days ago
Last modified
Categories
Share
Understat xG & Shot Data — Football Analytics
Scrapes understat.com: player and team season aggregates, per-match xG/PPDA/win-probabilities, shot-level data with x/y coordinates, situation/formation/game-state breakdowns, player career multi-season histories, and upcoming fixtures across the Big 5 European leagues + RFPL (2014/15 → 2025/26).
Why this actor
Most Understat scrapers stop at league-level aggregates. This one exposes every data tier Understat has, in a single run, with multi-season + multi-league support out of the box.
| Workload | Typical alternative | This actor |
|---|---|---|
| EPL one-season player stats (~600 players) | $0.59 | $0.59 (parity) |
| All 6 leagues × one season player stats | $3.04 | ~$2.99 |
| 5 years EPL player stats in one run | impossible (5 separate runs) | ~$2.95 |
| Shot-level data, EPL one season (~9,500 shots) | unavailable | ~$7.22 |
| Every EPL match + shots in one run | unavailable | ~$7.22 |
Pricing is pure pay-per-event. Per-row meters mean you pay for what you extract — no per-run fee, no per-league surcharge.
Modes
Pick a mode to determine what the actor scrapes:
| Mode | What you get | Source |
|---|---|---|
league_players | One row per (player × season). xG, xA, npxG, xGChain, xGBuildup, per-90 derivations, position, appearances. | /league/{LEAGUE}/{SEASON} HTML or /main/getPlayersStats/ POST when filters are set |
league_teams | One row per (team × season). W/D/L, GF/GA, xG/xGA, npxG/npxGA, npxGD, PPDA, deep, points, xPoints. | /league/{LEAGUE}/{SEASON} (aggregated from per-match history array) |
league_matches | One row per played match. Score, xG home/away, win-probability forecast. | /league/{LEAGUE}/{SEASON} datesData |
match_shots ⭐ | One row per shot. x/y normalized pitch coordinates, xG model value, situation, body part, result, derived distance and angle, score at time of shot. | /match/{ID} (parses shotsData + match_info) |
team_situations | Long-format: one row per (team × season × breakdown × label). Covers situation (OpenPlay/SetPiece/Corner/FreeKick/Penalty), formation (4-3-3 etc), gameState (leading/tied/trailing), timing, shotZones, attackSpeed, result. | /team/{TEAM}/{SEASON} statisticsData |
player_career | One row per (player × every season the player appears in). Full multi-season trajectory for any player ID. | /player/{ID} playersData |
fixtures | One row per upcoming match. Date/time, teams, pre-match win-probability forecast (xG fields are null until played). | /league/{LEAGUE}/{SEASON} datesData (isResult=false) |
⭐ match_shots is the killer feature — most scrapers don't expose it.
Pricing
| Event | Price |
|---|---|
league-season-scraped | $0.01 — one league × one season "discovery" fetch |
team-season-scraped | $0.005 |
player-season-scraped | $0.0008 |
match-scraped | $0.004 |
shot-scraped | $0.0006 |
situation-breakdown-scraped | $0.003 |
player-career-scraped | $0.0008 |
fixture-scraped | $0.0008 |
No actor-start fee. No per-league surcharge. First 100 chargeable events of every run are free — enough to taste every tier including a 75-shot heatmap and a multi-season career bundle.
Cost cap
The maxCostUsd input (default $25) is a hard cap. The actor estimates cost up-front from your scope and warns you. Mid-run, every charge is checked against the cap; once hit, the run stops gracefully and writes a cost_cap_reached status row to the dataset.
Without this, a typo like all 6 leagues × all 11 seasons × match_shots could cost ~$375. With it, you're protected.
Sample inputs
Player season stats (parity workload)
{"mode": "league_players","leagues": ["EPL"],"seasons": [2024]}
Multi-season comparison (one run, not five)
{"mode": "league_players","leagues": ["EPL"],"seasons": [2020, 2021, 2022, 2023, 2024]}
Forwards-only with date range
{"mode": "league_players","leagues": ["La_liga"],"seasons": [2024],"positions": ["F"],"dateStart": "2024-08-01","dateEnd": "2024-12-31","minAppearances": 5}
Shot map for a single match
{"mode": "match_shots","matchIds": [26630]}
All shots in a season (heavy — uses your cost cap)
{"mode": "match_shots","leagues": ["EPL"],"seasons": [2024],"maxCostUsd": 10}
Mo Salah's complete career
{"mode": "player_career","playerIds": [1250]}
Arsenal's situation breakdowns
{"mode": "team_situations","leagues": ["EPL"],"seasons": [2024],"teams": ["Arsenal"]}
Upcoming Premier League fixtures
{"mode": "fixtures","leagues": ["EPL"],"seasons": [2025]}
Output
Every row carries:
type— discriminator (player_season,team_season,match,shot,team_situation,player_career_season,fixture, orstatus)_meta.mode— which mode produced the row_meta.scrapedAt— ISO 8601 timestamp_meta.source— always"understat"
Set includeRawJson: true to attach the original Understat record under _raw (useful for power users; off by default to keep the dataset light).
Resume
Long runs check-point progress to a named key-value store keyed by mode + input scope. If a run is interrupted, the next run with the same input picks up where it left off — already-completed (league × season) or (matchId) or (playerId) units are skipped.
Limits
- leagues × seasons ≤ 60 — protects against accidental over-scope (e.g. 6 × 11 = 66 league-seasons).
- Cost cap defaults to $25/run.
match_shotsthrottles requests to 6 s between matches to mirror the empirically safe rate. Expect a full EPL season (~380 matches × ~25 shots) to take ~40 minutes.- RFPL team names are Cyrillic — the actor URL-encodes them automatically.
- Future fixtures have null
xGfields until played.
Coverage
| League | Slug | Earliest season |
|---|---|---|
| English Premier League | EPL | 2014/15 |
| Spanish La Liga | La_liga | 2014/15 |
| German Bundesliga | Bundesliga | 2014/15 |
| Italian Serie A | Serie_A | 2014/15 |
| French Ligue 1 | Ligue_1 | 2014/15 |
| Russian Premier League | RFPL | 2014/15 |
Notes
- This actor scrapes a publicly accessible website. Be mindful of Understat's resources — the built-in throttle is conservative, and
match_shotsadds an extra delay because of its high fan-out. - The xG values are Understat's neural-net model output. If you need the underlying training data or the model itself, you'll need to talk to them.
- Data freshness lags Understat's updates by minutes-to-hours depending on their crawl cadence.