Skip to content
Scores and results

Live football scores API over REST

Score widgets, match-day tickers and results pages run on polled REST requests. This page is about score state, not ball position: it covers what the scores feeds return, how often polling is sensible, and where the limits of a request-response score API sit.

Primary endpoint

GET/api/v1/football/live

No query parameters.

Access: All plans; Starter limited to your five saved leagues. Header: X-API-Key.

Illustration of a blank scoreboard and a refresh arrow, with no real score shown.
Scores arrive as polled REST snapshots of match state, not as ball or player tracking.
01

Score state, not a pitch tracker

A live scores API answers a narrow question well: what is the current state of this match? Who has scored, what period is it in, has it finished. It does not tell you where the ball is, which player is carrying it or how fast a counter-attack is developing. Those are positional tracking products, and nothing on this page offers them.

We say this up front because the two get confused in sales conversations. The score feeds described here are REST endpoints you poll. Each request returns a snapshot of match states as the source supplied them at that time. There is no push stream, no websocket and no coordinate data in these feeds. If a product idea depends on visualising ball movement, these feeds are the wrong foundation and you should talk to support before subscribing.

FootballAPI.net also runs a separate ball tracker demonstration on the site. It is its own area with its own guide, and nothing about buying a data plan activates it for your account. Treat it as a separate conversation.

02

The feeds behind a scores page

Five catalogue entries are relevant to scores. All are marked sample received: a response has come back through our gateway, and final freshness, coverage and licensing qualification is pending. None takes a league parameter except where noted, and on Starter each is restricted to your saved leagues.

  • live: matches the source currently reports as in play. The catalogue cache time is about fifteen seconds, the shortest of any feed.
  • scores: scores and results for the current window, cached for about thirty seconds.
  • scores-new: a newer scores service with the same cache time. Compare both against your needs before choosing one.
  • recent-results, with a required offset: finished matches for a recent window, cached for around five minutes.
  • inplay-mapping: a mapping feed you can use to relate in-play identifiers; check its contents against your use before relying on it.
Server-side score pollingFlow: your server polls the live feed about every fifteen seconds, compares with the previous snapshot, and republishes only changes to your readers.01GET /liveabout every 15 s02Compareby match ID03Changed?allow corrections04Your cachereaders read here
Server-side score polling. One server poller per account, diffing snapshots by match ID, fans changes out to readers without exposing the key.
03

Polling that respects the cache

Because the gateway caches each feed, asking for live every two seconds does not make scores arrive faster. It just spends your allowance on identical responses. A sensible default is to poll live at around the fifteen-second cache interval while at least one followed match is in play, drop to scores every minute or so around kick-off windows, and stop polling entirely when nothing relevant is happening.

Do the polling on your server, once, and fan the result out to your readers. A thousand browsers each calling the API directly would expose your key and multiply your request count by a thousand. One server process that fetches, diffs and publishes to your own cache or channel is cheaper and safer.

When a request fails, back off. Retrying a failed call immediately in a tight loop is the fastest way to hit the 100 requests per second account safety ceiling. Failed and denied requests are not billed, but they still do nothing for your readers.

Server-side poller that only republishes changesjavascript
const URL = "https://footballapi.net/api/v1/football/live";
let last = "";

async function tick() {
  const res = await fetch(URL, { headers: { "X-API-Key": process.env.FOOTBALLAPI_KEY } });
  if (!res.ok) return setTimeout(tick, 60_000); // back off on errors
  const body = await res.text();
  if (body !== last) {
    last = body;
    publishToReaders(body); // your own cache or channel
  }
  setTimeout(tick, 15_000);
}
tick();
04

Detecting a goal without inventing one

Most score products want an event: a goal alert, a final-whistle notification. The feeds give you states, so events are something you derive by comparing two snapshots of the same match identifier. If the previous snapshot had one score and the new one has another, something changed. Be careful about what you claim. A corrected score after a disallowed goal will also show up as a change, and a notification that says goal for a goal that was then ruled out is worse than a short delay.

Key every comparison on the source match identifier, stored as a string. Keep attribute names, including those that carry an at-sign prefix, exactly as they arrive. Your diff logic should compare the source fields, not a normalised shape you invented, so that when the source adds or changes an attribute you can see it rather than silently drop it.

05

What we do not promise

We do not publish a latency figure for score changes, and we do not promise that every match in every competition appears in live. Coverage varies by competition and date. A lower-tier match may appear only in results after it finishes, or not at all. Build your interface so that a match with no live state looks deliberate, for example by showing the scheduled time from the fixtures feed, rather than broken.

Nor do we grant broadcast or redistribution rights by selling access. If you plan to show scores publicly at scale, read the data licensing page and ask support about your specific use.

06

Designing the score interface around snapshots

Because each response is a snapshot, your interface should be honest about time. Show when your server last received an update, not a ticking clock that implies continuous data. If a poll fails, keep the previous state on screen with a quiet note that the score may be out of date, rather than blanking the panel or showing an error to every reader.

Group matches by competition and order them by scheduled time from the fixtures feed, so that a reader scanning the page sees a stable layout even when individual matches change state. Finished matches can move into a results section using recent-results, which refreshes less often and costs fewer requests.

Accessibility deserves care on score pages. Screen reader users should not hear every refresh announced. Announce only meaningful changes in a polite live region, and keep scores as text rather than images so they can be read and zoomed.

07

Abandoned, suspended and delayed matches

Real match days include states that a simple scheduled, live, finished model does not cover. A match can be delayed before kick-off, suspended for weather or crowd trouble, abandoned and replayed later, or awarded by a competition authority without being completed. Whatever state the source reports, pass it through to your interface as a distinct state rather than collapsing it into finished or not started.

These cases are exactly where invented logic causes harm. If a match stops appearing in live while it is suspended, do not mark it finished. Keep the last known state on screen, label it clearly, and wait for the scores or recent-results feeds to show a definitive outcome. When an abandoned match is later replayed under a new identifier, treat it as a new event and keep the original record for history.

Write tests for these states with saved responses from your own polling, so that your interface handles them deliberately before the first wet evening in November forces the issue.

08

What a match day costs in requests

A worked example helps. Suppose a site follows three leagues on a Saturday with matches spread over eight hours. Polling live every fifteen seconds for those eight hours is 1,920 requests. Add scores every minute for the remaining sixteen hours, another 960, and a few dozen recent-results calls. That is roughly 2,900 successful requests, within the Starter inclusion of 5,000 per UTC day.

Starter is EUR 29 a month for five selected leagues; the in-play mapping service is not part of Starter. Growth at EUR 99 includes all available package leagues and 25,000 requests a day, and Pro at EUR 249 includes 250,000. Successful requests beyond the daily inclusion are EUR 0.002 each, billed monthly on the EUR subscription. There is no free trial and no VAT is currently charged.

Questions developers ask

Is this a websocket or streaming feed?
No. The score feeds are REST endpoints you poll. Each response is a snapshot of match states.
Does the live feed include ball position or player tracking?
No. It reports score and match state only. Positional tracking is not part of these feeds or of a standard data plan.
Will subscribing activate the ball tracker?
No. The ball tracker is a separate area of the site, and a data subscription does not activate it.
How often should I poll live?
Around the fifteen-second cache interval while followed matches are in play, and less often or not at all otherwise.
Which should I use, scores or scores-new?
Both are published and sampled. Request each for the competitions you follow and choose the one whose structure fits your product.
Are goals pushed as events?
No. Derive changes by comparing snapshots of the same match identifier, and allow for corrections.

Check the plan before you write the integration.

Starter EUR 29, Growth EUR 99, Pro EUR 249 per month. No free trial. No VAT currently charged.