Understat xG Scraper – Football Data & Analytics
Pricing
from $0.75 / 1,000 league rows
Understat xG Scraper – Football Data & Analytics
Scrape normalized Understat xG data for leagues, teams, players and matches, including shots, rosters, xA, PPDA and global trends. Export JSON, CSV or Excel. Pay $0.75-$1 per 1,000 delivered rows based on your Apify plan.
Pricing
from $0.75 / 1,000 league rows
Rating
0.0
(0)
Developer
DataScraperES
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
a day ago
Last modified
Categories
Share
Scrape normalized Understat expected goals (xG) data for leagues, teams, players, and matches, including shots, rosters, xA, PPDA, and global football trends. This Understat xG scraper returns analysis-ready football analytics for JSON, CSV, Excel, dashboards, or API workflows for $0.75–$1.00 per 1,000 delivered rows, depending on your Apify plan, with no start fee.
This Actor is unofficial and is not affiliated with Understat.
What this Actor does
The Actor offers six focused modes:
- League overview returns team-season summaries, player-season summaries, and fixtures/results.
- Team overview returns player summaries, matches, and tactical/statistical breakdowns.
- Player overview returns one player profile plus match-by-match and shot-by-shot records.
- Match details returns roster appearances and individual shots.
- Find a player ID searches by name so you can run Player overview without manually browsing for an ID.
- Global monthly trends returns monthly home/away goals and xG averages by league.
Every row includes a recordType, sourceUrl, and UTC fetchedAt timestamp. Numeric source values are converted to JSON numbers, while stable identifiers remain strings so they are safe to export and join.
Use cases
- Build football analytics dashboards with goals, xG, xA, xPoints, PPDA, shots, and results.
- Compare teams or players across seasons in spreadsheets, databases, or BI tools.
- Create shot maps and player-performance timelines from event-level rows.
- Monitor fixtures and completed matches for the supported domestic leagues.
- Enrich match analysis with rosters, positions, minutes, cards, and buildup metrics.
How to use
- Open the Actor in Apify Console.
- Select What report do you want?. Each option states the record groups it returns.
- For League, choose a league and season. For Team, Player, Match, or player search, enter the name, ID, or URL in the single Target field. Leave Target empty to run a working example for the selected report.
- Keep the optional section closed for the recommended complete export, or open Choose included data to remove record groups you do not need.
- Click Start and open the Dataset when the run finishes.
Input
The form keeps only five fields in the main section. Every field has an explanatory tooltip, and fields that do not apply to the selected report are safely ignored.
| Main field | Used by | Default / form prefill | Description |
|---|---|---|---|
mode | All runs | league / league | Selects one of six clearly labelled reports. |
league | League | EPL / EPL | Selects EPL, La_liga, Bundesliga, Serie_A, Ligue_1, or RFPL. Other reports ignore it. |
season | League, Team | 2024 / 2024 | Season start year; 2024 means 2024/25. A year in a Team URL takes precedence. |
target | Team, Player, Match, player search | Empty / empty | Accepts a team name/URL, player ID/URL, match ID/URL, or player name according to mode. Empty uses a working example. |
maxItems | All runs | 1000 / 1 | Maximum rows saved and billed. The form starts with a one-row sample costing at most USD 0.001; an API call that omits the field uses the 1,000-row default. Increase it up to 5,000 when needed. |
The collapsed Choose included data section contains includePlayers, includeMatches, includeShots, includeBreakdowns, and includeRosters. All default to true; each tooltip states exactly which reports it affects and which rows it adds. They are shown explicitly in the prefilled input so the generated API examples expose every current user option. Network fallback, timeout, and retry behavior are managed automatically, so users do not need to configure transport settings.
This is the exact prefilled input. It produced the League result example below:
{"mode": "league","league": "EPL","season": 2024,"target": "","maxItems": 1,"includePlayers": true,"includeMatches": true,"includeShots": true,"includeBreakdowns": true,"includeRosters": true}
Output
Results are stored in the run's default Dataset. Depending on the selected mode, recordType is one of league_team, league_player, league_match, team_player, team_match, team_breakdown, player_profile, player_match, player_shot, match_roster, match_shot, player_search_result, or global_trend.
Apify Console also provides purpose-built table views for complete league reports, league tables, shots, tactical breakdowns, player analysis, match rosters, player ID searches, and monthly global trends. Select the view that matches your report to see its most useful fields first; downloads and API access still use the same normalized Dataset rows.
The separate SUMMARY output reports the selected mode, saved and available row counts, record counts by type, request attempts, transport used, truncation state, warnings, and a billing block. That block identifies the event name and shows requested, delivered, and charged rows. A valid source response with no matching records finishes with an empty Dataset and zero row events. A missing entity, access denial, challenge page, or incompatible response fails visibly instead of being saved as successful data.
Each selected mode uses one billing event, and every row saved by that mode creates one event. A mode can return several recordType values; the examples below show one complete, real row for every billing event.
| Selected mode | Billing event | Example record type |
|---|---|---|
| League overview | league-result | league_team |
| Team overview | team-result | team_player |
| Player overview | player-result | player_profile |
| Match details | match-result | match_roster |
| Find a player ID | player-search-result | player_search_result |
| Global monthly trends | global-trend-result | global_trend |
League result example
{"recordType": "league_team","sourceUrl": "https://understat.com/league/EPL/2024","fetchedAt": "2026-09-03T11:26:51.771396Z","league": "EPL","season": 2024,"teamId": "71","teamName": "Aston Villa","matches": 38,"wins": 19,"draws": 9,"losses": 10,"points": 66,"goalsFor": 58,"goalsAgainst": 51,"xG": 66.385246,"xGA": 54.998103,"nonPenaltyXG": 61.710806,"nonPenaltyXGA": 52.714603,"xPoints": 61.5269,"deepCompletions": 285,"deepAllowed": 248,"ppda": 12.388811,"ppdaAllowed": 9.955724}
Team result example
{"recordType": "team_player","sourceUrl": "https://understat.com/team/Arsenal/2024","fetchedAt": "2026-09-03T10:09:13.688569Z","league": null,"season": 2024,"playerId": "5220","playerName": "Kai Havertz","teamName": "Arsenal","position": "F M S","appearances": 23,"minutes": 1880,"goals": 9,"nonPenaltyGoals": 9,"assists": 3,"shots": 53,"keyPasses": 17,"yellowCards": 5,"redCards": 0,"xG": 11.830620590597391,"nonPenaltyXG": 11.830620590597391,"xA": 2.9488715566694736,"xGChain": 16.01736629009247,"xGBuildup": 4.696250442415476}
Player result example
{"recordType": "player_profile","sourceUrl": "https://understat.com/player/7322","fetchedAt": "2026-09-03T10:09:25.201582Z","playerId": "7322","playerName": "Bukayo Saka","favoritePosition": "FWR","league": "EPL","teamName": "Arsenal","season": 2026,"matchCount": 228,"shotCount": 511}
Match result example
{"recordType": "match_roster","sourceUrl": "https://understat.com/match/26602","fetchedAt": "2026-09-03T10:09:34.659174Z","matchId": "26602","side": "h","rosterId": "666169","playerId": "10913","teamId": "89","playerName": "André Onana","position": "GK","minutes": 90,"goals": 0,"ownGoals": 0,"shots": 0,"xG": 0,"keyPasses": 0,"assists": 0,"xA": 0,"yellowCards": 0,"redCards": 0,"subbedIn": 0,"subbedOut": 0,"xGChain": 0.14530973136425018,"xGBuildup": 0.14530973136425018}
Player search result example
{"recordType": "player_search_result","sourceUrl": "https://understat.com/","fetchedAt": "2026-09-03T10:09:45.350435Z","query": "Saka","playerId": "7322","playerName": "Bukayo Saka","teamName": "Arsenal"}
Global trend result example
{"recordType": "global_trend","sourceUrl": "https://understat.com/","fetchedAt": "2026-09-03T10:09:55.484108Z","league": "RFPL","leagueId": "6","year": 2014,"month": 8,"matches": 48,"homeGoalsPerMatch": 1.4583,"awayGoalsPerMatch": 1.125,"homeXGPerMatch": 1.4501787355790536,"awayXGPerMatch": 1.0161208802989374}
You can download Dataset results in JSON, JSONL, CSV, Excel, XML, or RSS formats available in Apify Console.
Pricing
The Actor uses pay-per-event billing. One event always means one successfully delivered Dataset row, regardless of its recordType or selected mode. Apify automatically applies the rate for your plan:
| Apify pricing tier | Price per delivered row | Equivalent per 1,000 delivered rows |
|---|---|---|
| Free | $0.001 | $1.00 |
| Bronze | $0.0009 | $0.90 |
| Silver | $0.0008 | $0.80 |
| Gold | $0.00075 | $0.75 |
| Platinum | $0.00075 | $0.75 |
| Diamond | $0.00075 | $0.75 |
All six modes use this same tier table; only the event name changes as shown in the Output section. Tiers are based on your Apify plan, not on the number of rows in one run. The actual charge remains proportional to the exact number of rows delivered and is not rounded to 1,000-row blocks. For example, 250 rows cost $0.25 on Free, $0.225 on Bronze, $0.20 on Silver, or $0.1875 on Gold, Platinum, and Diamond.
There is no Actor-start event, no fixed per-run fee, and no second automatic Dataset-item event. Empty results, failed requests, discarded records, and rows omitted because of maxItems or the run's maximum charge do not create a row event. Set a maximum total charge in Apify before a large run; if that limit is reached, the Actor saves and charges only the affordable prefix and reports the truncation in SUMMARY.
The Actor's Pricing tab is the authoritative source for the active rate and spending controls.
Related Actors
| Actor | Best for |
|---|---|
| Similarweb Website Traffic & SEO Analytics Scraper | Adding website audience, ranking, and competitor intelligence when club or league research also covers digital reach. |
Limits and data quality
- League coverage is limited to the six leagues listed in the input. Source availability varies by competition and season.
- Season inputs start at 2014, but an accepted year can still return no records when the source has no data for it.
- One mode and one entity are processed per run. The complete response is normalized, then
maxItemsis applied in deterministic order. - The Actor uses 256 MB of memory and bounded network attempts to keep the platform cost represented by the row price under control.
- League mode always includes team summaries. Team mode needs at least one of players, matches, or breakdowns enabled; Match mode needs rosters or shots enabled.
- Source timestamps do not include an explicit timezone. Treat
dateTimeas the source's displayed match time rather than assuming UTC. - Understat values and forecasts can change when the source corrects or refreshes data. Use
fetchedAtto identify your collection time. - The source interfaces used by public pages are not documented as a stable public API and may change without notice. Contract changes are reported as errors instead of silently producing malformed rows.
- Direct access is attempted first. An owner-managed fallback is applied automatically only after transient connection or 5xx failures. Explicit access denials, rate limits, or challenge pages are not bypassed.
- A failed source request cannot produce partial rows because each mode uses one bounded source response.
maxItemsand maximum-charge truncation are clearly marked inSUMMARY; only the rows actually saved are billed.
Frequently asked questions
How do I find a player ID?
Select Find a player ID, enter part of the player's name, and run the Actor. Copy playerId from the matching Dataset row into a new Player overview run.
Do I need a proxy?
No. Network handling is automatic: the Actor tries the efficient direct route first and has an owner-managed fallback for temporary connection or server failures. There is no proxy input to configure.
Which year should I enter for a season?
Enter the year in which the season started. For example, use 2024 for 2024/25.
Why did I receive fewer rows than expected?
Open SUMMARY and check truncated, totalRecords, savedRecords, and warnings. Increase maxItems up to 5,000 or disable record groups you do not need.
Is this an official Understat integration?
No. This Actor is an independent tool and is not endorsed by or affiliated with Understat.
Responsible use
Use this Actor only for lawful collection of public football data and only when you have the right to automate access. Understat's published robots policy observed during development disallows automated crawling, so review the current policy and terms before every production use and obtain permission where required. Do not use proxies or retries to bypass access controls, rate limits, challenge pages, or other technical restrictions. You are responsible for compliance with applicable laws, contractual terms, database rights, privacy rules, and third-party rights.
Support
If a run fails or returns unexpected data, open an issue in the Actor's Issues tab and include the run ID, selected mode, non-sensitive input, expected behavior, and observed behavior. Do not post proxy URLs, credentials, API tokens, or other secrets.
Understat football data Python guide, API examples, and sample dataset