Building with AI? Give your coding tool the whole reference in one file: https://soccer-data.co/llms.txt
SOCCER DATA Docs
Sign in Get API key
// Guides

Fixtures and results

Upcoming matches, recent scores, a day's games, matchdays and tournament rounds, all from one endpoint.

Fixtures and results both come from /matches. You choose what you get with filters: a team, a competition, a date range, a status. This guide walks through the questions people ask most.

Every example sends your key in the X-Api-Key header. Keep that key on your server.

A team's next match

Ask for scheduled matches, soonest first, one per page:

curl "https://api.soccer-data.co/v1/matches?team_id=team_f0ibhxdw7hs5&status=scheduled&sort=utc_date&per_page=1" \
  -H "X-Api-Key: YOUR_API_KEY"
{
  "data": [
    {
      "id": "match_yoeml0e6ae37",
      "competition_id": "competition_705uv6jm8myn",
      "season_id": "season_hr1ie7yrsdmt",
      "matchday": 6,
      "status": "scheduled",
      "utc_date": "2026-10-10T11:30:00.000Z",
      "home_team": { "id": "team_f0ibhxdw7hs5", "name": "Arsenal" },
      "away_team": { "id": "team_w63vzmngfyz9", "name": "Leeds United" },
      "score": { "home": null, "away": null, "winner": null },
      "odds_available": true,
      "live_odds_available": false
    }
  ],
  "meta": { "page": 1, "per_page": 1, "total": 9, "total_pages": 9 }
}

Change per_page=1 to per_page=5 for the next five. meta.total tells you how many scheduled matches the team has in the data.

A team's latest results

Ask for finished matches and leave sort out. The default order already puts the most recent kickoff first.

curl "https://api.soccer-data.co/v1/matches?team_id=team_f0ibhxdw7hs5&status=finished&per_page=1" \
  -H "X-Api-Key: YOUR_API_KEY"
{
  "data": [
    {
      "id": "match_nbqbxh7kv0q8",
      "status": "finished",
      "utc_date": "2026-09-19T14:00:00.000Z",
      "home_team": { "id": "team_v70dqyg1h8b0", "name": "Brighton & Hove Albion" },
      "away_team": { "id": "team_f0ibhxdw7hs5", "name": "Arsenal" },
      "score": {
        "home": 3,
        "away": 0,
        "regulation": { "home": 3, "away": 0 },
        "went_to_extra_time": false,
        "went_to_penalties": false,
        "winner": "home"
      },
      "xg_available": true,
      "shotmap_available": true
    }
  ],
  "meta": { "page": 1, "per_page": 1, "total": 561, "total_pages": 561 }
}

For the venue, referee and half-time score of a single match, call /matches/{match_id}.

One day's matches, in your time zone

Set date_from and date_to to the same day. Add utc_offset when "the day" should mean your local day rather than the UTC one:

curl "https://api.soccer-data.co/v1/matches?competition_id=competition_705uv6jm8myn&date_from=2026-10-10&date_to=2026-10-10&utc_offset=%2B10:00&sort=utc_date" \
  -H "X-Api-Key: YOUR_API_KEY"

The offset changes which matches fall inside the day. In UTC, 10 October 2026 has six Premier League matches. At +10:00, the local day runs from 14:00 UTC on 9 October to 14:00 UTC on 10 October, so only Arsenal v Leeds United (11:30 UTC) is included.

Warning In a URL, a bare + is read as a space, and the request fails with 400 BAD_REQUEST. Write + as %2B, as above, or build the query with URLSearchParams or your HTTP library's params option. Negative offsets like -05:00 need no encoding.

Kickoff times in the response stay in UTC either way. Convert them for display in your app.

A league matchday

curl "https://api.soccer-data.co/v1/matches?competition_id=competition_705uv6jm8myn&season_id=season_hr1ie7yrsdmt&matchday=6" \
  -H "X-Api-Key: YOUR_API_KEY"

This returns the ten matches of Premier League matchday 6.

Tournament groups and knockouts

For a group in a tournament, pass group along with the competition and season. This returns the six Group A matches of the 2022 World Cup:

curl "https://api.soccer-data.co/v1/matches?competition_id=competition_7vpwx91c7j7h&season_id=season_st2tyewgtq2j&group=A&sort=utc_date" \
  -H "X-Api-Key: YOUR_API_KEY"

Use stage to split a season into parts:

  • stage=playoff: knockout rounds and play-offs only (16 matches for the 2022 World Cup).
  • stage=regular: league and group matches only.

/competitions/{competition_id}/seasons/{season_id}/groups lists the group labels a season has.

How far ahead fixtures go

Later rounds of a season aren't always in the data yet. To see how far it reaches, ask for the season's scheduled matches without a sort:

curl "https://api.soccer-data.co/v1/matches?competition_id=competition_705uv6jm8myn&season_id=season_hr1ie7yrsdmt&status=scheduled&per_page=1" \
  -H "X-Api-Key: YOUR_API_KEY"

The first row is the furthest fixture we have, and meta.total is how many scheduled matches are loaded. Coverage gives the same picture for every season.

Getting every page

Lists come 20 to a page by default. To read everything, ask for per_page=100 and keep increasing page until it reaches meta.total_pages. Pagination has ready-made loops. Each page is one request, so filter as tightly as you can first.

Filters at a glance

Parameter What it does
team_id Matches the team plays in, at home or away.
competition_id, season_id One competition, one season. The season must belong to the competition.
status scheduled, live, finished, postponed or cancelled.
date_from, date_to Kickoff date range, YYYY-MM-DD, both ends included. There's no date parameter.
utc_offset The time zone the dates are read in, such as -05:00. Defaults to UTC.
sort -utc_date (latest first, the default) or utc_date (soonest first).
matchday A league round.
stage regular, playoff or all.
group A tournament group, such as A. Needs competition_id.
page, per_page Paging. 20 per page by default, 100 at most.

The /matches reference has the full details.

Reading the score

  • score.home and score.away are the goals scored in normal time. They're null before kickoff.
  • score.winner is home, away or draw once the match is over, and takes extra time and penalties into account.
  • score.regulation repeats the normal-time score; went_to_extra_time and went_to_penalties say how far the match went.
  • score.after_extra_time and score.penalty_shootout only appear when a match got that far.

Good to know

  • utc_date is always UTC.
  • Matches that kick off at the same moment always come back in the same order, so paging through them is stable.
  • Every request counts against your limits, including each page. Cache what doesn't need to be fresh, such as finished results.

Common mistakes

  • Leaving out sort=utc_date when you want the next match. You get the match furthest in the future instead.
  • A bare + in utc_offset. Encode it as %2B.
  • Sending date. It returns 400 UNKNOWN_PARAMETER. The message suggests the closest valid parameter and lists the ones the endpoint takes, date_from and date_to among them.
  • Sending group without competition_id, or per_page above 100. Both return 400.
  • Assuming the whole season is loaded. Check how far fixtures go before you promise them to users.