List matches
Search fixtures and results across every competition we cover. Filter by competition, season, team, date range or status to build fixture lists, results pages or a live scoreboard. Results come 20 per page, newest kickoff first.
https://api.soccer-data.co/v1/matches
Live
- There is no single date filter. For one day's matches, send the same date as both date_from and date_to.
- Send status=live to get every match in play right now. It is the easiest starting point for a live scoreboard.
- If you send both competition_id and season_id, the season must belong to that competition, or you get a 400.
- group only works together with competition_id. Without it you get a 400.
- Asking for a page past the last one returns a 400 PAGE_OUT_OF_RANGE error. Use meta.total_pages to know where to stop.
- Score breakdown fields stay null until a match is finished.
- While matches are live, responses are normally at most 5 minutes old on Free and 15 seconds old on Pro and Business. Check live.updated_at to see how recent the clock is. If a fresh answer can't be fetched, you may get an older copy marked X-Cache: STALE instead: see When an answer is stale.
Only return matches from this competition.
Only return matches from this season. If you also send competition_id, the season has to belong to it.
Only return matches this team plays in, at home or away.
Earliest kickoff day to include, as YYYY-MM-DD. The day is read in the utc_offset time zone.
Last kickoff day to include, as YYYY-MM-DD, up to the end of that day. It can't be before date_from.
Time zone for date_from and date_to, as +HH:MM or -HH:MM (up to 14 hours). Use it so a 'day' matches your users' local day. Kickoff times in the response stay in UTC.
Only return matches in this state. Use live for matches being played now.
scheduledlivefinishedpostponedcancelledLeague round number, from 1. Handy for showing one gameweek.
regular covers league and group games. playoff covers knockout rounds, cup ties and promotion or relegation play-offs. all returns both.
regularplayoffallOnly return matches from this tournament group. Not case-sensitive. Needs competition_id.
ABCDEFGHIJKL-utc_date puts the latest kickoff first, which suits results. utc_date puts the soonest first, which suits fixtures. Matches with the same kickoff are ordered by id.
-utc_dateutc_datePage number, starting at 1.
How many matches per page, from 1 to 100.
The match's id. Pass it to the other match endpoints for lineups, stats, events and more.
The competition this match belongs to.
The season this match belongs to.
League round number. Null for cup fixtures. On play-off fixtures it is the play-off round number, not a league gameweek.
regular for league and group games, playoff for knockout, cup and play-off fixtures.
Name of the round for cup and knockout games, such as final, semi_final, quarter_final, round_of_16, group_stage or qualifying. Null for normal league games.
Group letter (A to L) for group-stage games in tournaments that are split into groups. Null otherwise.
Where the match stands: scheduled, live, finished, postponed or cancelled.
Kickoff time in UTC, as an ISO 8601 date-time.
Home team's id.
Home team's name.
Away team's id.
Away team's name.
Home goals in normal time. Null if the match hasn't been played.
Away goals in normal time. Null if the match hasn't been played.
Aggregate score with home and away, included only when it differs from the normal-time score.
Score after 90 minutes, with home and away. Null until the match is finished.
Score after 120 minutes, with home and away. Counts goals only, not shoot-out kicks. Null if there was no extra time.
Penalties scored by each side in a shoot-out, with home and away. Null if the match wasn't settled on penalties.
Whether extra time was played. False for formats that go straight to penalties. Null until finished, or when we can't tell.
Whether the match was decided by a shoot-out. Null until finished.
home, away or draw, taking extra time and penalties into account. Null until finished.
True when the match is played at neither team's home ground. Null means we don't know, so don't treat it as false.
The in-play clock. Null unless status is live.
Detailed state of a live match: not_started, first_half, halftime, second_half, extra_time, penalties, finished, suspended or in_progress.
Minutes since kickoff as a whole number. It keeps counting through the second half rather than starting again, and doesn't split out stoppage time.
first_half, halftime, second_half or finished. Null when match_status is extra_time, penalties, suspended, not_started or in_progress.
When the clock was last updated. If it stops moving, live updates may have stalled, so don't assume the minute is current.
Home team's manager. Can be null.
Manager's id.
Manager's name.
Away team's manager. Can be null.
Manager's id.
Manager's name.
True when pre-match odds exist for this match. Always false for postponed or cancelled matches.
True when in-play odds are available right now. Always false for postponed or cancelled matches.
True when match-level expected goals (xG) exist.
True when a shot map with per-shot xG exists.
full when per-shot xG values exist, none when they don't. Treat any value you don't recognise as none.
The page you're on.
Matches per page.
Matching results across all pages.
Number of pages available.
curl "https://api.soccer-data.co/v1/matches?status=live" \ -H "X-Api-Key: $SOCCER_DATA_KEY"
const res = await fetch("https://api.soccer-data.co/v1/matches?status=live", {
headers: { "X-Api-Key": process.env.SOCCER_DATA_KEY },
});
const { data } = await res.json();
import os, requests
res = requests.get(
"https://api.soccer-data.co/v1/matches",
params={"status": "live"},
headers={"X-Api-Key": os.environ["SOCCER_DATA_KEY"]},
)
data = res.json()["data"]
$data = Http::withHeaders(['X-Api-Key' => env('SOCCER_DATA_KEY')])
->get('https://api.soccer-data.co/v1/matches', ['status' => 'live'])
->json('data');
{
"data": [
{
"id": "match_9pd0ck6g2m9i",
"competition_id": "competition_705uv6jm8myn",
"season_id": "season_98l95mxugqs9",
"matchday": 37,
"stage": "regular",
"stage_name": null,
"group_label": null,
"status": "finished",
"utc_date": "2026-05-15T19:00:00.000Z",
"home_team": {
"id": "team_4mimn5fmya2v",
"name": "Aston Villa"
},
"away_team": {
"id": "team_74p8tg2k93z4",
"name": "Liverpool"
},
"score": {
"home": 4,
"away": 2,
"final_score": null,
"regulation": {
"home": 4,
"away": 2
},
"after_extra_time": null,
"penalty_shootout": null,
"went_to_extra_time": false,
"went_to_penalties": false,
"winner": "home"
},
"is_neutral": false,
"live": null,
"home_manager": {
"id": "manager_17xiheuxdn67",
"name": "Unai Emery"
},
"away_manager": {
"id": "manager_fzvvh82pzed4",
"name": "Arne Slot"
},
"odds_available": true,
"live_odds_available": false,
"xg_available": true,
"shotmap_available": true,
"xg_quality": "full"
}
],
"meta": {
"page": 1,
"per_page": 20,
"total": 100,
"total_pages": 5
}
}
### GET /matches List matches Search fixtures and results across every competition we cover. Filter by competition, season, team, date range or status to build fixture lists, results pages or a live scoreboard. Results come 20 per page, newest kickoff first. - There is no single date filter. For one day's matches, send the same date as both date_from and date_to. - Send status=live to get every match in play right now. It is the easiest starting point for a live scoreboard. - If you send both competition_id and season_id, the season must belong to that competition, or you get a 400. - group only works together with competition_id. Without it you get a 400. - Asking for a page past the last one returns a 400 PAGE_OUT_OF_RANGE error. Use meta.total_pages to know where to stop. - Score breakdown fields stay null until a match is finished. - While matches are live, responses are normally at most 5 minutes old on Free and 15 seconds old on Pro and Business. Check live.updated_at to see how recent the clock is. If a fresh answer can't be fetched, you may get an older copy marked X-Cache: STALE instead: see [When an answer is stale](/docs/concepts/rate-limits#when-an-answer-is-stale). Query parameters: - `competition_id` (string): Only return matches from this competition. - `season_id` (string): Only return matches from this season. If you also send competition_id, the season has to belong to it. - `team_id` (string): Only return matches this team plays in, at home or away. - `date_from` (date): Earliest kickoff day to include, as YYYY-MM-DD. The day is read in the utc_offset time zone. - `date_to` (date): Last kickoff day to include, as YYYY-MM-DD, up to the end of that day. It can't be before date_from. - `utc_offset` (string, default "+00:00"): Time zone for date_from and date_to, as +HH:MM or -HH:MM (up to 14 hours). Use it so a 'day' matches your users' local day. Kickoff times in the response stay in UTC. - `status` (string, one of scheduled, live, finished, postponed, cancelled): Only return matches in this state. Use live for matches being played now. - `matchday` (integer): League round number, from 1. Handy for showing one gameweek. - `stage` (string, default "all", one of regular, playoff, all): regular covers league and group games. playoff covers knockout rounds, cup ties and promotion or relegation play-offs. all returns both. - `group` (string, one of A, B, C, D, E, F, G, H, I, J, K, L): Only return matches from this tournament group. Not case-sensitive. Needs competition_id. - `sort` (string, default "-utc_date", one of -utc_date, utc_date): -utc_date puts the latest kickoff first, which suits results. utc_date puts the soonest first, which suits fixtures. Matches with the same kickoff are ordered by id. - `page` (integer, default 1): Page number, starting at 1. - `per_page` (integer, default 20): How many matches per page, from 1 to 100. Response fields: - `data[].id` (string): The match's id. Pass it to the other match endpoints for lineups, stats, events and more. - `data[].competition_id` (string): The competition this match belongs to. - `data[].season_id` (string): The season this match belongs to. - `data[].matchday` (integer|null): League round number. Null for cup fixtures. On play-off fixtures it is the play-off round number, not a league gameweek. - `data[].stage` (string): regular for league and group games, playoff for knockout, cup and play-off fixtures. - `data[].stage_name` (string|null): Name of the round for cup and knockout games, such as final, semi_final, quarter_final, round_of_16, group_stage or qualifying. Null for normal league games. - `data[].group_label` (string|null): Group letter (A to L) for group-stage games in tournaments that are split into groups. Null otherwise. - `data[].status` (string): Where the match stands: scheduled, live, finished, postponed or cancelled. - `data[].utc_date` (string): Kickoff time in UTC, as an ISO 8601 date-time. - `data[].home_team.id` (string): Home team's id. - `data[].home_team.name` (string): Home team's name. - `data[].away_team.id` (string): Away team's id. - `data[].away_team.name` (string): Away team's name. - `data[].score.home` (integer|null): Home goals in normal time. Null if the match hasn't been played. - `data[].score.away` (integer|null): Away goals in normal time. Null if the match hasn't been played. - `data[].score.final_score` (object|null): Aggregate score with home and away, included only when it differs from the normal-time score. - `data[].score.regulation` (object|null): Score after 90 minutes, with home and away. Null until the match is finished. - `data[].score.after_extra_time` (object|null): Score after 120 minutes, with home and away. Counts goals only, not shoot-out kicks. Null if there was no extra time. - `data[].score.penalty_shootout` (object|null): Penalties scored by each side in a shoot-out, with home and away. Null if the match wasn't settled on penalties. - `data[].score.went_to_extra_time` (boolean|null): Whether extra time was played. False for formats that go straight to penalties. Null until finished, or when we can't tell. - `data[].score.went_to_penalties` (boolean|null): Whether the match was decided by a shoot-out. Null until finished. - `data[].score.winner` (string|null): home, away or draw, taking extra time and penalties into account. Null until finished. - `data[].is_neutral` (boolean|null): True when the match is played at neither team's home ground. Null means we don't know, so don't treat it as false. - `data[].live` (object|null): The in-play clock. Null unless status is live. - `data[].live.match_status` (string|null): Detailed state of a live match: not_started, first_half, halftime, second_half, extra_time, penalties, finished, suspended or in_progress. - `data[].live.elapsed_minutes` (integer|null): Minutes since kickoff as a whole number. It keeps counting through the second half rather than starting again, and doesn't split out stoppage time. - `data[].live.period` (string|null): first_half, halftime, second_half or finished. Null when match_status is extra_time, penalties, suspended, not_started or in_progress. - `data[].live.updated_at` (string|null): When the clock was last updated. If it stops moving, live updates may have stalled, so don't assume the minute is current. - `data[].home_manager` (object|null): Home team's manager. Can be null. - `data[].home_manager.id` (string): Manager's id. - `data[].home_manager.name` (string): Manager's name. - `data[].away_manager` (object|null): Away team's manager. Can be null. - `data[].away_manager.id` (string): Manager's id. - `data[].away_manager.name` (string): Manager's name. - `data[].odds_available` (boolean): True when pre-match odds exist for this match. Always false for postponed or cancelled matches. - `data[].live_odds_available` (boolean): True when in-play odds are available right now. Always false for postponed or cancelled matches. - `data[].xg_available` (boolean): True when match-level expected goals (xG) exist. - `data[].shotmap_available` (boolean): True when a shot map with per-shot xG exists. - `data[].xg_quality` (string): full when per-shot xG values exist, none when they don't. Treat any value you don't recognise as none. - `meta.page` (integer): The page you're on. - `meta.per_page` (integer): Matches per page. - `meta.total` (integer): Matching results across all pages. - `meta.total_pages` (integer): Number of pages available. Example: curl "https://api.soccer-data.co/v1/matches?status=live" -H "X-Api-Key: $SOCCER_DATA_KEY"