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.
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.
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.
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();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.
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.
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.
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.
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.
Guides and documentation
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.