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
// Get started

Introduction

What Soccer Data covers, and the conventions every endpoint shares.

Soccer Data is a read-only REST API for football. You send GET requests over HTTPS with your API key, and every answer is JSON. This page covers what's inside and the few rules that apply everywhere.

What you can get

Area What's included
Competitions Leagues, cups and international tournaments, with their seasons, groups and league tables.
Teams Profiles, squads, season records, standings history, injuries and suspensions.
Players Profiles, season totals, injuries and suspensions, season heatmaps.
Matches Fixtures, results, live scores, lineups, event timelines, team and player stats, referees.
xG Shot maps with an xG value for every shot, plus per-match player heatmaps.
Odds Pre-match and in-play prices from Bet365, Paddy Power, BetMGM UK, Pinnacle and Betfair Exchange, where they exist.
Coverage Which competitions and seasons have which kinds of data.

Not every competition has every kind of data, and how far back the history goes depends on the competition. Competitions and matches carry flags such as xg_available and odds_available so you can check before you ask. The Coverage page explains how to see exactly what exists.

Base URL

Every path in these docs sits under this base URL:

https://api.soccer-data.co/v1

Authentication

Put your API key in the X-Api-Key header of each request. Keys belong on your server, never in code that runs in a browser or an app you ship to users. See Authentication for the details.

curl "https://api.soccer-data.co/v1/competitions/competition_705uv6jm8myn" \
  -H "X-Api-Key: YOUR_API_KEY"

Requests

All endpoints use GET. Filters go in the query string:

curl "https://api.soccer-data.co/v1/matches?team_id=team_f0ibhxdw7hs5&status=finished" \
  -H "X-Api-Key: YOUR_API_KEY"

If you send a parameter an endpoint doesn't know, including a typo, you get a 400 with the code UNKNOWN_PARAMETER. The message suggests the closest valid parameter, when one is close, and lists the ones the endpoint accepts, so mistakes are quick to fix.

IDs

Each thing in the API has an id that starts with a word saying what it is, such as competition_, season_, team_, player_ or match_, followed by 12 letters and digits. For example, the Premier League is competition_705uv6jm8myn and Arsenal is team_f0ibhxdw7hs5.

Ids never change, so you can store them in your own database. Treat them as plain strings and use them exactly as you received them. Data model lists every kind, and Find any ID shows how to look them up.

Dates and times

  • Date filters such as date_from and date_to take YYYY-MM-DD. There is no single date parameter; to get one day, set both to the same date.
  • Kickoff times in responses are in UTC, in ISO 8601 format (for example 2026-10-10T11:30:00.000Z). Convert them to local time in your app.

Responses

List endpoints return a data array plus a meta object for paging:

{
  "data": [
    {
      "id": "team_f0ibhxdw7hs5",
      "name": "Arsenal",
      "short_name": "Arsenal",
      "country": "England",
      "is_mens_team": true,
      "primary_competition": { "id": "competition_705uv6jm8myn", "name": "Premier League" }
    }
  ],
  "meta": { "page": 1, "per_page": 1, "total": 13, "total_pages": 13 }
}

Endpoints that return a single thing put one object in data:

{
  "data": {
    "id": "competition_705uv6jm8myn",
    "name": "Premier League",
    "country": "England",
    "type": "league",
    "xg_available": true,
    "current_season_id": "season_hr1ie7yrsdmt",
    "total_teams": 20
  }
}

Pagination explains meta and how to walk through every page.

Errors

Every error has the same shape, so one handler covers them all:

{
  "error": {
    "code": "NOT_FOUND",
    "message": "Match not found",
    "status_code": 404
  }
}

Write your logic against code, which is stable, rather than the message. Errors lists every code and what to do about it.

Limits and freshness

Each plan has a per-minute limit and a daily limit, and every response tells you how much you have left in its headers. Answers that change during a match are cached briefly: normally for 15 seconds on paid plans and 5 minutes on Free. If a fresh answer can't be fetched, you may get an older copy marked X-Cache: STALE. Rate limits has the numbers.

Getting new data

The API works by pulling: there are no webhooks or streaming connections. To follow a live match, call the live endpoints at a steady pace. Live matches shows how to do that without wasting requests.

Changes over time

We add endpoints, parameters and response fields as the API grows. Write your client so it ignores fields it doesn't recognise, and it won't break when something new appears.

Next steps