SOCCER DATA

How to choose a football data API: 10 things to check

Ten things to check before you pick a football data API, from coverage and live refresh to rate limits and AI docs, and how Soccer Data handles each.

· 7 min read

To choose a football data API, check ten things before you build on it: coverage per competition, historical depth, live refresh, stable ids, match detail such as xG, lineups and events, bookmaker odds, rate limits and pricing, the error format, pagination, and docs for AI tools. For each one, this checklist says what to look for and why, then how Soccer Data, a read-only REST API for football, handles it.

1. Coverage per competition and data type

What to check: whether the competitions you need have the data you need (fixtures, team and player stats, xG, lineups, tables, odds), and whether the API itself can tell you.

Why: coverage differs between competitions and seasons. A competition can be listed while only having results.

How Soccer Data handles it: /coverage/leagues lists every competition with the data types it has. Filter it with data_type (such as xg, player_stats, lineups or closing_odds) and search:

curl "https://api.soccer-data.co/v1/coverage/leagues?data_type=xg&search=Premier" \
  -H "X-Api-Key: YOUR_API_KEY"

/coverage/leagues/{competition_id} breaks one competition down season by season, with covered_events and coverage_pct for each data type.

2. Historical depth

What to check: how far back each data type goes in each competition, rather than one headline figure.

Why: backtests and season comparisons need the same data in every season you use, and data types often start at different points.

How Soccer Data handles it: depth depends on the competition and the data type, and the per-competition coverage endpoint lists it season by season. Some examples:

Competition Matches from
Premier League 2014/15
LaLiga, Bundesliga, Serie A, Ligue 1, UEFA Champions League 2020/21
MLS 2020

In the Premier League, fixtures, lineups, team and player stats and tables go back to 2014/15, closing odds to 2015/16, xG to 2022/23 and opening odds to 2026/27.

3. Live refresh and how updates arrive

What to check: how often live data refreshes, whether updates are pushed or pulled, and how you can tell an answer's age.

Why: refresh rate decides how live your scores feel, and with polling every call spends a request.

How Soccer Data handles it: the API works by pulling. There are no webhooks or streaming connections; you poll endpoints such as /matches?status=live and /matches/{match_id}/live-stats. Live data is cached briefly:

Plan Live data normally refreshed every
Free 5 minutes
Pro and Business 15 seconds

Asking again within that window returns the same answer with X-Cache: HIT and still counts. If a fresh answer can't be fetched, you may get an older copy marked X-Cache: STALE, with its age in X-Cache-Age. See Live matches.

4. Stable ids

What to check: whether ids are permanent, and how to find them by name.

Why: if ids change, the mappings and URLs you stored break.

How Soccer Data handles it: every id is a word prefix such as team_ or match_ followed by 12 lowercase letters and digits, and it never changes. The Premier League is competition_705uv6jm8myn and Arsenal is team_f0ibhxdw7hs5. Find ids with the search parameter on /competitions, /teams and /players. See Data model.

5. Match detail: xG, lineups and events

What to check: what each match has beyond the score, during the match and after it.

Why: match centres, fantasy games and models need stats, lineups and events, not only results.

How Soccer Data handles it:

Data Endpoint What you get
Team stats and xG /matches/{match_id}/stats Match and half stats, with expected_goals and np_expected_goals
Shots /matches/{match_id}/shotmap Every shot with expected_goals, x, y and result
Player stats /matches/{match_id}/player-stats Per player, filtered with player_ids
Lineups /matches/{match_id}/lineups type is predicted, then team_sheet, then played, with formation
Events /matches/{match_id}/timeline Goals, cards, substitutions, shots and VAR checks, in order

During a match, use the live-stats, live-timeline and live-player-stats endpoints; the full-match ones return 409 until full time. Check xg_available and shotmap_available before asking for xG.

6. Bookmaker odds

What to check: which bookmakers, pre-match and in-play, whether opening and closing prices are kept, and which plan includes them.

Why: price comparison and modelling need prices labelled by bookmaker, including closing lines for past matches.

How Soccer Data handles it: /matches/{match_id}/odds returns pre-match prices from Bet365, Paddy Power, BetMGM UK, Pinnacle and Betfair Exchange, each selection with an opening and a last_seen (closing, after kickoff) price, filtered with bookmaker. /matches/{match_id}/odds/live returns in-play prices from the same bookmakers except Pinnacle. Odds are in the Pro and Business plans; a key on a plan without odds gets 403 PLAN_UPGRADE_REQUIRED. See the Odds guide.

7. Rate limits, quota headers and pricing

What to check: each plan's per-minute and daily limits, whether responses tell you what's left, and whether plans are published.

Why: an unexpected limit breaks your app in production, and hidden pricing makes costs hard to plan.

How Soccer Data handles it: plans are monthly and published on the pricing page, each with its own limits:

Plan Requests / day Per minute Live data refreshed (normally) Odds
Free 50 10 5 min —
Pro 1,000 60 15 s Yes
Business 10,000 300 15 s Yes

Limits belong to the account, so all its keys share them, and daily limits reset at midnight UTC. Every response to a request with a valid key carries X-RateLimit-Remaining and X-Daily-Quota-Remaining, with matching -Limit and -Reset headers, and a 429 carries Retry-After. See Rate limits.

8. Error format

What to check: whether every error has the same JSON shape, with a stable code apart from the message.

Why: one handler can then cover every error, and the code tells you whether to retry or fix the request.

How Soccer Data handles it: every error looks like this:

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

Branch on code: RATE_LIMITED and USAGE_LIMIT_EXCEEDED are both 429, but only the first is worth retrying after Retry-After. A parameter the endpoint doesn't take returns 400 UNKNOWN_PARAMETER, and the message suggests the closest valid one. See Errors.

9. Pagination

What to check: how lists are paged, the largest page, whether you get a total, and whether the order is stable.

Why: without totals or a fixed order, syncing a list can miss or duplicate records.

How Soccer Data handles it: /competitions, /teams, /players and /matches take page and per_page (up to 100; up to 200 on /coverage/leagues). Paged responses include meta with page, per_page, total and total_pages. The order is fixed: /matches lists the latest kickoff first, or the soonest with sort=utc_date. See Pagination.

10. Docs for AI tools: llms.txt and MCP

What to check: whether AI coding tools can read the full reference, and whether AI assistants can query the data directly.

Why: an assistant that guesses endpoint and parameter names writes code that fails.

How Soccer Data handles it: the whole documentation, with every endpoint's parameters, fields and an example, is one plain-text file at https://soccer-data.co/llms.txt. The MCP server lets Claude, ChatGPT, Cursor, VS Code and other MCP apps query the data while you chat, signed in with OAuth or an API key. It's part of the Pro and Business plans and shares your plan's limits. See Build with AI.

Next steps

Frequently asked questions

What should I look for in a football data API?

Check coverage per competition, historical depth, live refresh, stable ids, xG, lineups and events, odds, rate limits and pricing, the error format, pagination, and docs for AI tools. Soccer Data's /coverage endpoints show which competitions and seasons have which data.

How do I check which leagues a football API covers?

With Soccer Data, call GET /coverage/leagues, optionally with data_type (such as xg or closing_odds) and search. GET /coverage/leagues/{competition_id} adds covered_events and coverage_pct per season.

Does Soccer Data have webhooks for live football scores?

No. Soccer Data works by pulling: you poll live endpoints such as /matches?status=live. On the Pro and Business plans, live data is normally refreshed every 15 seconds, so polling faster only spends requests.

Which football data API works with ChatGPT and Claude?

Soccer Data has an MCP server that Claude, ChatGPT, Cursor and other MCP apps can connect to, so they answer football questions from real data. It also publishes its full API reference at /llms.txt for AI coding tools.

Are Soccer Data ids stable?

Yes. Every Soccer Data id is a prefix such as team_ followed by 12 letters and digits, and it never changes, so you can store it. Look ids up by name with the search parameter on /competitions, /teams and /players.

How do I know how many API requests I have left?

Every Soccer Data response to a request with a valid key carries X-RateLimit-Remaining for the current minute and X-Daily-Quota-Remaining for the current UTC day. A 429 also carries Retry-After, the seconds to wait.

Try it with your own key

Fixtures, live scores, stats, xG, standings and odds for 1,100 competitions, in one REST API.

More guides