Handling live matches
A live match moves through a small set of statuses. Knowing them lets you poll only when it matters and react the moment a score changes.
Samples use the small get() helper from Build a live score app.
Match lifecycle
Every fixture carries a status.short code. A normal match goes NS → LIVE → HT → LIVE → FT; a few never start.
| Status | Meaning | What to do |
|---|---|---|
| NS | Not started. | Show the kick-off time. No need to poll yet. |
| LIVE | In play; status.minute holds the match minute. | Poll the live endpoint every 60 seconds. |
| HT | Half-time. | Keep polling — play resumes shortly. |
| FT | Full-time; the score is final. | Stop polling this fixture and refresh standings. |
| PST | Postponed. | Show it as postponed and look out for a new date. |
| CANC | Cancelled. | Remove it from schedules. |
When to poll
- Before kick-off — load the schedule, then sleep until the first kick-off time.
- In play — poll
/football/fixtures/liveevery 60 seconds. One request returns every live fixture matching your filters, so never poll fixture by fixture. - After full time — stop polling and fetch the final result once.
60 seconds is the sweet spot
Data freshness is about 30 seconds. Polling faster than the recommended 60 seconds spends requests without showing your users anything newer.
A small scheduler that follows those rules:
const IN_PLAY = ["LIVE", "HT"];// Milliseconds until the next poll, or null when every fixture is donefunction nextPollDelay(fixtures, now = Date.now()) { if (fixtures.some((f) => IN_PLAY.includes(f.status.short))) { return 60 * 1000; // in play: every 60 seconds } const untilKickOff = fixtures .filter((f) => f.status.short === "NS") .map((f) => Date.parse(f.date) - now); if (untilKickOff.length === 0) return null; // nothing left today return Math.max(Math.min(...untilKickOff), 60 * 1000); // sleep until the next kick-off}Detecting changes
The API returns the current state of each match, not a list of events. To send goal alerts or update a timeline, compare each poll with the previous one:
const previous = new Map();function diff(live) { const events = []; for (const f of live) { const before = previous.get(f.id); if (!before) { events.push({ type: "kickoff", fixture: f }); } else { for (const side of ["home", "away"]) { if (f[side].score !== before[side].score) { events.push({ type: "score", side, fixture: f }); } } if (f.status.short !== before.status.short) { events.push({ type: "status", fixture: f }); } } previous.set(f.id, f); } return events;}- Compare scores with
!==, not>: a disallowed goal makes a score go down. - If your process restarts mid-match, seed
previousfrom the first poll without emitting events, or every live match will look like a new kick-off.
When a match ends
A finished fixture drops out of /fixtures/live. When a fixture you were tracking disappears, fetch it once more to confirm its final score and status:
const liveIds = new Set(live.map((f) => f.id));for (const [id, tracked] of previous) { if (liveIds.has(id)) continue; // No longer live: fetch it once more to confirm the final state const { data } = await get("/football/fixtures", { league_id: tracked.league.id, date: tracked.date.slice(0, 10), // kick-off date, UTC }); const final = data.find((f) => f.id === id); if (final) onFinished(final); // FT, PST or CANC previous.delete(id);}This is the moment to refresh anything that depends on results — standings, form, player statistics — and to invalidate their caches. See Caching.
Other sports
Every sport with live data has a live endpoint that works the same way. Only the fields that describe the match clock differ:
| Sport | Live endpoint | Clock fields |
|---|---|---|
| Football | /football/fixtures/live | status.minute |
| Basketball | /basketball/livescores | status.period, status.clock |
| Tennis | /tennis/livescores | status.set, status.game, sets |
| Cricket | /cricket/livescores | runs, wickets, overs, target |
| Baseball | /baseball/livescores | status.inning, status.half, status.outs |
| Hockey | /hockey/livescores | status.period, status.clock |
| American Football | /american-football/livescores | status.quarter, status.clock |
| Motorsport | /motorsport/live-timing | lap, positions, fastest_lap |
| Rugby | /rugby/livescores | status.minute |
| Volleyball | /volleyball/livescores | status.set, set_scores |
| Handball | /handball/livescores | status.minute |