Sofascore League Standings & Tables Scraper
Pricing
from $1.50 / 1,000 standings rows
Sofascore League Standings & Tables Scraper
Scrape league tables and standings from Sofascore for football, basketball, ice hockey, baseball and more. One row per team, with position, played, W/D/L, points, goal difference and promotion or relegation status. Handles group stages. No API key and no proxy needed.
Pricing
from $1.50 / 1,000 standings rows
Rating
0.0
(0)
Developer
Elena Vance
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
2 days ago
Last modified
Categories
Share
Scrape league tables from Sofascore for football, basketball, ice hockey, baseball and more — one row per team, ready to open in Excel or feed into a spreadsheet.
No API key. No login. No residential proxy. Runs on the Apify free plan.
What you get
One dataset row per team per table, with a stable column set across every sport:
| Tournament | Season | Group | Pos | Team | P | W | D | L | Pts | GD | Promotion |
|---|---|---|---|---|---|---|---|---|---|---|---|
| Premier League | 26/27 | 1 | Arsenal | 1 | 1 | 0 | 0 | 3 | +4 | Champions League | |
| Premier League | 26/27 | 20 | Wolverhampton | 1 | 0 | 0 | 1 | 0 | −4 | Relegation | |
| FIFA World Cup | 2026 | Group A | 1 | Mexico | 0 | 0 | 0 | 0 | 0 | 0 | |
| NBA | 26/27 | 1 | Oklahoma City | 0 | 0 | 0 | 0 |
Group-stage competitions (World Cup groups, MLB divisions, NHL conferences)
fill the group column. A single-table league leaves it empty.
What makes this different
- Built for league tables, not bolted on. Where other Sofascore Actors offer standings at all, it is a checkbox inside a broad multi-mode scraper. Here it is the product: the input, the schema and the pricing are all shaped around one row per team per table.
- Nested tables come back whole. The NHL returns its league table, both
conferences and all four divisions — 96 rows from 32 teams — and group
stages fill a
groupcolumn. SetgroupScopetooverallto keep only the top table and bill 32 rows instead. - One column set across every sport, so a spreadsheet built against a football table works unchanged for basketball, ice hockey and baseball.
- Off-season is reported, not faked. A competition with no table yet succeeds with a status message saying so and bills nothing beyond the start event, instead of returning an empty run you have to diagnose.
- A $0.00005 start fee, against up to $0.08 on comparable Actors. For a run that fetches one table, that is the whole price difference.
Input
| Field | Description |
|---|---|
tournaments | Required. Sofascore tournament URLs or numeric IDs. Paste the URL straight from your browser. |
seasonId | Optional. Defaults to the latest season. A season pinned in a URL's #id: fragment wins over this. |
standingsTypes | total (default), home, away. Each adds one request per tournament. |
groupScope | all (default) or overall — see Overlapping tables. |
seasonFallback | Default on — see Off-season behaviour. |
maxItems | Stop after this many rows. |
proxyConfiguration | Optional. Only needed to pin a country or a specific proxy. |
{"tournaments": ["https://www.sofascore.com/tournament/football/england/premier-league/17",132],"standingsTypes": ["total"]}
Some IDs to start from: 17 Premier League · 8 LaLiga · 23 Serie A · 35 Bundesliga · 34 Ligue 1 · 7 Champions League · 242 MLS · 132 NBA · 234 NHL · 11205 MLB · 16 FIFA World Cup.
Sport coverage
Verified against live data across 14 competitions:
| Sport | Table | Sport-specific columns |
|---|---|---|
| Football | ✅ | points, scoresFor/scoresAgainst, goalDifference, promotion |
| Basketball | ✅ | percentage, gamesBehind — no points |
| Ice hockey | ✅ | points, overtimeLosses, normaltimeLosses |
| Baseball | ✅ | percentage, gamesBehind — no draws |
| Tennis | ❌ | A draw is not a table; there is nothing to return |
| Pure knockout cups | ❌ | No table until a league/group phase exists |
Columns a sport doesn't use are null, never missing — so the CSV keeps the
same header everywhere.
Overlapping tables
Some leagues return several nested tables at once. The NHL sends the league table plus 2 conferences plus 4 divisions, so 32 teams produce 96 rows — the same team three times, at a different position in each. MLB does the same (9 tables, 90 rows for 30 teams).
Those are real, distinct standings, not duplicates. But if you only wanted the
league table, set groupScope to overall:
| League | all | overall |
|---|---|---|
| NHL | 96 rows | 32 rows |
| MLB | 90 rows | 30 rows |
| Premier League | 20 rows | 20 rows (already one table) |
| FIFA World Cup | 60 rows | 60 rows (groups don't overlap) |
overall keeps the one table that contains every team. Where no such table
exists — a group stage, where the groups split the teams rather than nesting —
nothing is dropped and the run says so.
Either way the run logs which choice you got and what the other would have returned.
Off-season behaviour
Sofascore lists a season as soon as it is scheduled, but a table only exists once it has started. Measured on 2026-08-24, the Champions League and the NHL both had no table for 2026/27 while 2025/26 was complete.
With seasonFallback on (the default), the Actor returns the previous
season's table instead of nothing and records what happened:
seasonId/seasonYear— the season the rows actually came fromseasonRequested— set only when a fallback happened
So you can always tell "here is 26/27" from "26/27 hasn't started, here is 25/26". Turn the toggle off to get an empty result instead.
Notes
- A competition with no table is reported in the run's status message, not as a failure — it is a real answer.
- A run where every request failed fails loudly rather than finishing green with an empty dataset.
- Requests go directly to Sofascore. A proxy IP is only fetched if a request is actually blocked, and then a datacenter IP (included in every Apify plan) is tried before any residential one.
The Sofascore Actor suite
Six Actors from the same publisher, built on one shared HTTP layer and one ID space — so the output of one is the input of the next.
| Actor | What it returns |
|---|---|
| Live Scores & Fixtures | Fixtures and live results by date across 19 sports. Emits eventId. |
| Match Stats, xG & Lineups | Per-match shotmaps with pitch coordinates, lineups, ratings, momentum. Takes eventId. |
| League Standings & Tables | This Actor. |
| Team & Player Data | Squads, bios, market values, transfers, match logs. Takes team and player IDs. |
| Season Stats | Season leaderboards and complete team statistics. Takes uniqueTournamentId. |
| Rankings & Search | FIFA, ATP/WTA, UEFA and UFC tables, plus name-to-ID search. |
How they chain. Live Scores emits the eventId that Match Stats takes.
Rankings' search turns a name into the team, player and tournament IDs the
other five accept. Standings and Season Stats share uniqueTournamentId, and
Team & Player emits that same ID as primaryTournamentId.
All six need no API key, no login and no headless browser; none requires a residential-proxy add-on, and all run on the Apify free plan.
Development
python -m venv .venv && .venv/bin/pip install -r requirements.txt -r requirements-dev.txt.venv/bin/python -m pytest # unit tests, no network.venv/bin/python -m src # local run, reads storage/key_value_stores/default/INPUT.json
See docs/architecture.md for how the transport and season resolution work, and docs/ci-cd.md for the pipeline.