League tables
Current and past league tables, tournament group tables, and where a team has a table entry.
A league table belongs to one season of one competition, so you need both ids. This guide shows how to get them, then how to read league tables, past seasons and tournament groups.
Every example sends your key in the X-Api-Key header. Keep that key on your server.
Find the competition and its season
Search for the competition, then read current_season_id from it:
curl "https://api.soccer-data.co/v1/competitions?search=Premier" \
-H "X-Api-Key: YOUR_API_KEY"
# The Premier League is competition_705uv6jm8myn
curl "https://api.soccer-data.co/v1/competitions/competition_705uv6jm8myn" \
-H "X-Api-Key: YOUR_API_KEY"
# data.current_season_id is season_hr1ie7yrsdmt
For earlier seasons, /competitions/{competition_id}/seasons lists them all.
Tip Read
current_season_idfrom the competition instead of hard-coding a season id. Your table then moves on to the new season by itself.
The current table
curl "https://api.soccer-data.co/v1/competitions/competition_705uv6jm8myn/seasons/season_hr1ie7yrsdmt/standings" \
-H "X-Api-Key: YOUR_API_KEY"
{
"data": [
{
"team": { "id": "team_hof12pbei216", "name": "Manchester City" },
"position": 1,
"matches_played": 5,
"wins": 5,
"draws": 0,
"losses": 0,
"goals_for": 13,
"goals_against": 5,
"goal_difference": 8,
"points": 15,
"group_label": null
},
{
"team": { "id": "team_f0ibhxdw7hs5", "name": "Arsenal" },
"position": 2,
"matches_played": 5,
"wins": 4,
"draws": 0,
"losses": 1,
"goals_for": 8,
"goals_against": 4,
"goal_difference": 4,
"points": 12,
"group_label": null
}
]
}
The example shows the first two of 20 rows. Rows come in position order, and the whole table arrives in one response: there are no pages to walk through.
Tables move during matches, so they follow your plan's live refresh: normally every 15 seconds on paid plans and every 5 minutes on Free.
Row fields
| Field | Meaning |
|---|---|
team |
The team's id and name. |
position |
Place in the table. |
matches_played, wins, draws, losses |
The team's record. |
goals_for, goals_against, goal_difference |
Goals scored, conceded and the difference. |
points |
Points so far. |
group_label |
The group, such as "A", in a tournament. null in a league. |
A past season
Swap in an older season id. Premier League 25/26 is season_98l95mxugqs9:
curl "https://api.soccer-data.co/v1/competitions/competition_705uv6jm8myn/seasons/season_98l95mxugqs9/standings" \
-H "X-Api-Key: YOUR_API_KEY"
Arsenal top that table, with 85 points from 26 wins in 38 matches.
How many past seasons exist depends on the competition. Check Coverage before you promise users a particular season.
Warning The season must belong to the competition in the path. If it doesn't, you get a
400saying the season doesn't belong to that competition.
Tournament groups
Tournaments with a group stage have one table per group. Start by listing the groups. This is the 2022 World Cup:
curl "https://api.soccer-data.co/v1/competitions/competition_7vpwx91c7j7h/seasons/season_st2tyewgtq2j/groups" \
-H "X-Api-Key: YOUR_API_KEY"
{
"data": [
{ "group_label": "A", "name": "Group A" },
{ "group_label": "B", "name": "Group B" }
]
}
The real response runs from Group A to Group H. Then ask for one group's table with group:
curl "https://api.soccer-data.co/v1/competitions/competition_7vpwx91c7j7h/seasons/season_st2tyewgtq2j/standings?group=A" \
-H "X-Api-Key: YOUR_API_KEY"
{
"data": [
{
"team": { "id": "team_z5p9xh26ys6y", "name": "Netherlands" },
"position": 1,
"matches_played": 3,
"wins": 2,
"draws": 1,
"losses": 0,
"goals_for": 5,
"goals_against": 1,
"goal_difference": 4,
"points": 7,
"group_label": "A"
}
]
}
Leave group out and you get every group's table in one list, sorted by group_label and then by position.
A league without groups returns an empty list from /groups.
Cups
Competitions played only as knockouts, like the FA Cup, have no table. Their standings come back as an empty list.
Where a team has tables
/teams/{team_id}/standings lists every competition and season in which a team has a table entry:
curl "https://api.soccer-data.co/v1/teams/team_f0ibhxdw7hs5/standings" \
-H "X-Api-Key: YOUR_API_KEY"
{
"data": [
{
"competition": { "id": "competition_705uv6jm8myn", "name": "Premier League" },
"seasons": [
{ "id": "season_hr1ie7yrsdmt", "name": "Premier League 26/27", "group_label": null },
{ "id": "season_98l95mxugqs9", "name": "Premier League 25/26", "group_label": null }
]
},
{
"competition": { "id": "competition_b25me9a3vyf7", "name": "UEFA Europa League" },
"seasons": [
{ "id": "season_dgf6gghi4mf8", "name": "UEFA Europa League 22/23", "group_label": "A" }
]
}
]
}
This is a list of places to look, not a list of positions. To find where the team finished, fetch that season's table. When the season has a group_label, pass it as group to get just that group's table.
If you only need one team's position, points and recent form for a season, /teams/{team_id}/stats gives you all three in a single call.
Common mistakes
- Pairing a season with the wrong competition. You get a
400. - Expecting positions from
/teams/{team_id}/standings. It only lists seasons; fetch the table for positions. - Sending
pageorper_pageto the standings endpoint. Tables aren't paged, so the parameter is rejected. - Expecting a table for a knockout cup. There isn't one; the list is empty.
- Forgetting
groupin a tournament. Without it you get every group mixed into one list.
Related endpoints
# League tables
Current and past league tables, tournament group tables, and where a team has a table entry.
A league table belongs to one season of one competition, so you need both ids. This guide shows how to get them, then how to read league tables, past seasons and tournament groups.
Every example sends your key in the `X-Api-Key` header. Keep that key on your server.
## Find the competition and its season
Search for the competition, then read `current_season_id` from it:
```bash
curl "https://api.soccer-data.co/v1/competitions?search=Premier" \
-H "X-Api-Key: YOUR_API_KEY"
# The Premier League is competition_705uv6jm8myn
curl "https://api.soccer-data.co/v1/competitions/competition_705uv6jm8myn" \
-H "X-Api-Key: YOUR_API_KEY"
# data.current_season_id is season_hr1ie7yrsdmt
```
For earlier seasons, [`/competitions/{competition_id}/seasons`](/docs/reference/competitions/seasons) lists them all.
> **Tip:** Read `current_season_id` from the competition instead of hard-coding a season id. Your table then moves on to the new season by itself.
## The current table
```bash
curl "https://api.soccer-data.co/v1/competitions/competition_705uv6jm8myn/seasons/season_hr1ie7yrsdmt/standings" \
-H "X-Api-Key: YOUR_API_KEY"
```
```json
{
"data": [
{
"team": { "id": "team_hof12pbei216", "name": "Manchester City" },
"position": 1,
"matches_played": 5,
"wins": 5,
"draws": 0,
"losses": 0,
"goals_for": 13,
"goals_against": 5,
"goal_difference": 8,
"points": 15,
"group_label": null
},
{
"team": { "id": "team_f0ibhxdw7hs5", "name": "Arsenal" },
"position": 2,
"matches_played": 5,
"wins": 4,
"draws": 0,
"losses": 1,
"goals_for": 8,
"goals_against": 4,
"goal_difference": 4,
"points": 12,
"group_label": null
}
]
}
```
The example shows the first two of 20 rows. Rows come in `position` order, and the whole table arrives in one response: there are no pages to walk through.
Tables move during matches, so they follow your plan's live refresh: normally every 15 seconds on paid plans and every 5 minutes on Free.
### Row fields
| Field | Meaning |
|---|---|
| `team` | The team's `id` and `name`. |
| `position` | Place in the table. |
| `matches_played`, `wins`, `draws`, `losses` | The team's record. |
| `goals_for`, `goals_against`, `goal_difference` | Goals scored, conceded and the difference. |
| `points` | Points so far. |
| `group_label` | The group, such as `"A"`, in a tournament. `null` in a league. |
## A past season
Swap in an older season id. Premier League 25/26 is `season_98l95mxugqs9`:
```bash
curl "https://api.soccer-data.co/v1/competitions/competition_705uv6jm8myn/seasons/season_98l95mxugqs9/standings" \
-H "X-Api-Key: YOUR_API_KEY"
```
Arsenal top that table, with 85 points from 26 wins in 38 matches.
How many past seasons exist depends on the competition. Check [Coverage](/docs/concepts/coverage) before you promise users a particular season.
> **Warning:** The season must belong to the competition in the path. If it doesn't, you get a `400` saying the season doesn't belong to that competition.
## Tournament groups
Tournaments with a group stage have one table per group. Start by listing the groups. This is the 2022 World Cup:
```bash
curl "https://api.soccer-data.co/v1/competitions/competition_7vpwx91c7j7h/seasons/season_st2tyewgtq2j/groups" \
-H "X-Api-Key: YOUR_API_KEY"
```
```json
{
"data": [
{ "group_label": "A", "name": "Group A" },
{ "group_label": "B", "name": "Group B" }
]
}
```
The real response runs from Group A to Group H. Then ask for one group's table with `group`:
```bash
curl "https://api.soccer-data.co/v1/competitions/competition_7vpwx91c7j7h/seasons/season_st2tyewgtq2j/standings?group=A" \
-H "X-Api-Key: YOUR_API_KEY"
```
```json
{
"data": [
{
"team": { "id": "team_z5p9xh26ys6y", "name": "Netherlands" },
"position": 1,
"matches_played": 3,
"wins": 2,
"draws": 1,
"losses": 0,
"goals_for": 5,
"goals_against": 1,
"goal_difference": 4,
"points": 7,
"group_label": "A"
}
]
}
```
Leave `group` out and you get every group's table in one list, sorted by `group_label` and then by `position`.
A league without groups returns an empty list from `/groups`.
## Cups
Competitions played only as knockouts, like the FA Cup, have no table. Their standings come back as an empty list.
## Where a team has tables
[`/teams/{team_id}/standings`](/docs/reference/teams/standings) lists every competition and season in which a team has a table entry:
```bash
curl "https://api.soccer-data.co/v1/teams/team_f0ibhxdw7hs5/standings" \
-H "X-Api-Key: YOUR_API_KEY"
```
```json
{
"data": [
{
"competition": { "id": "competition_705uv6jm8myn", "name": "Premier League" },
"seasons": [
{ "id": "season_hr1ie7yrsdmt", "name": "Premier League 26/27", "group_label": null },
{ "id": "season_98l95mxugqs9", "name": "Premier League 25/26", "group_label": null }
]
},
{
"competition": { "id": "competition_b25me9a3vyf7", "name": "UEFA Europa League" },
"seasons": [
{ "id": "season_dgf6gghi4mf8", "name": "UEFA Europa League 22/23", "group_label": "A" }
]
}
]
}
```
This is a list of places to look, not a list of positions. To find where the team finished, fetch that season's table. When the season has a `group_label`, pass it as `group` to get just that group's table.
If you only need one team's position, points and recent form for a season, [`/teams/{team_id}/stats`](/docs/reference/teams/stats) gives you all three in a single call.
## Common mistakes
- **Pairing a season with the wrong competition.** You get a `400`.
- **Expecting positions from `/teams/{team_id}/standings`.** It only lists seasons; fetch the table for positions.
- **Sending `page` or `per_page` to the standings endpoint.** Tables aren't paged, so the parameter is rejected.
- **Expecting a table for a knockout cup.** There isn't one; the list is empty.
- **Forgetting `group` in a tournament.** Without it you get every group mixed into one list.
## Related endpoints
- [Standings for a season](/docs/reference/competitions/standings)
- [Groups in a season](/docs/reference/competitions/groups)
- [Seasons of a competition](/docs/reference/competitions/seasons)
- [Team standings history](/docs/reference/teams/standings)
- [Team season stats](/docs/reference/teams/stats)