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
- Quickstart: your first requests in about five minutes.
- Coverage: check the competitions and seasons you need.
- Build with AI: hand the reference to your coding assistant.
- Create an account and get an API key.
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.