Fourteen seasons of fixtures, results, lineups, match statistics, grounds, referees and exchange prices at kickoff — one REST call, one JSON object, every field documented and counted from the archive rather than estimated.
1,689,164 matches, every one with its own fixture id, the same field names and the same club ids — from the Premier League down to the Namibian top flight.
Anyone can give you a scoreline. These are the fields that let you ask why — and they sit on the same row, in the same response, with no second call.
Exchange prices, not a bookmaker's book — so there's no margin baked into the number. Captured at kickoff, captured again at half-time, with the matched volume behind each one.
A three-way book at 106% has the margin built in before you start. At 102.3% the price is close enough to the crowd's real probability to model against — which is the only kind worth storing ten seasons of.
Ten seasons settled to yesterday, both snapshots on every priced match, matched volume, overround and the market ids — plus backtesting and CSV export over the lot.
Every country below is in the archive on the same schema, and the free key reaches all of them for yesterday, as one whole day. What separates the plans is how far back you can read and whether the prices come with it, never which league it came from.
Not a sample, not three showcase leagues. Every country on this list, on a real key, for yesterday, as one whole day.
Get a free key →Namibia’s top flight returns the same field names as the Premier League. Write the parser once and point it anywhere.
Field reference →28,325 club ids stable across every season a club appears in, and one match id per fixture — so what you store today still joins next year.
How ids work →No SDK to install, no schema to learn twice. Every one of these is a copy-paste snippet on the documentation page it belongs to.
Ten seasons of results with the price they went off at, plus positions and travel — cursor-paged, a thousand rows a call.
Archivecurl "…/v1/matches?season=2017-2025&has_prices=true &include=odds,context&limit=1000&cursor=$c" \ -H "X-API-Key: $FSAPI_KEY"
Today's card, in-play scores and current prices, asked once a minute; nothing comes back until something has changed.
Livelet since = ""; setInterval(async () => { const r = await fetch("https://api.…/v1/live", { headers: { "X-API-Key": key, "If-Modified-Since": since } }); if (r.status === 304) return; // nothing changed since = r.headers.get("Last-Modified"); (await r.json()).data.forEach(board.update); }, 60000);
A page per club and per league — form, standings, head-to-head — off one archive with stable ids.
Free key$form = fsapi("/v1/teams/{$id}/form?last=10"); $table = fsapi("/v1/standings/{$league}/2026"); // 28,325 clubs, one id each, stable across seasons
Point an agent at the MCP endpoint and it queries the archive itself — no glue code to write.
Archive// tools: fsapi.matches, fsapi.team_stats, fsapi.odds "How does Brighton do at home when they lead at half-time, and what did the market pay at kickoff in those games?"
Every endpoint page carries the same call in four languages and a Run it live box that fires against your own key — so you can check the shape of the response before you write a line.
Generated from the same registry that drives the router, the OpenAPI export, the Postman collection and the assistant tools — so this list can't drift from what the API actually serves.
The whole surface as a spec you can generate a client from, regenerated on every release.
Download the spec →Every endpoint with example parameters, ready to fire once you paste your key into the environment.
Import the collection →The same registry exposed as assistant tools, so an agent can query the archive without glue code.
Connect an assistant →Every popular football system run against the whole archive at kickoff prices, net of commission, with the losing ones shown losing. Rebuilt nightly. No sign-in to read any of it.
The whole Lab is open — no account, no email wall. When you want to test your rule rather than ours, the same engine is one endpoint away on Archive.
You can see the uptime, you can see how old the answer is, and you can see what changed and when. Nothing about this API should ever be a surprise on a Monday morning.
data-as-of: 2026-09-04T02:10:00Z x-request-id: 01J9QW3C7M4KX2VB x-ratelimit-limit: 120 x-ratelimit-remaining: 118 x-ratelimit-reset: 41
{ "error": { "code": "plan_required", "message": "Half-time prices need the Archive plan.", "your_plan": "free", "needs_plan": "archive", "upgrade_url": "/pricing?from=odds" }, "meta": { "request_id": "01J9QW3C7M4KX2VB" } }
Adding a field is routine and needs nothing from you. Removing one is a changelog event with a deprecation window, announced before it happens — never on the day.
Every release dated, with what changed and what it affects. Subscribe by email or RSS and you'll know before your code does.
Read the changelog →Tickets carry a reference and stay readable from your account. Quote a request_id and we can see the exact call you made.
Open a ticket →No seat pricing, no per-call surcharges, no coverage upsell. Cancel from your account and keep access to the end of the period you paid for.
Every league and every country, for yesterday, as one whole day, 50 calls a day. No card, no trial clock, and no sandbox rows for matches that were never played.
curl "https://api.footballsoccerapi.com/v1/countries" -H "X-API-Key: $FSAPI_KEY"
16 endpoints · 60 calls a minute · 500 a day · every league in the archive