One day of results per request
Recent results takes one required parameter, offset, which is a whole number from 1 to 7 sent as a string. For this feed it counts days before today: offset 1 is yesterday and offset 7 is a week ago. It mirrors the upcoming feed, which uses the same values to count forward. There is no offset for today here; the current day belongs to the scores and live feeds, where matches may still be in progress.
Values outside 1 to 7, or values written as dates, are rejected with a 400 response and the invalid_parameter code. That rejection is not billed, but it leaves a hole in whatever page you were building, so validate the value on your side before sending it.
The cache time is about five minutes, which reflects the nature of the data. Results for yesterday change rarely, so frequent polling buys nothing.
Finished matches, as supplied
Responses use the standard envelope: a data object with the source structure and a meta object with the feed, UTC fetch time, cache time and qualification. The data is passed through without a normalised schema of our own. Identifiers are strings, and attributes that came from source attributes keep their at-sign prefix. Inspect a real response before mapping it, and expect the attributes present to vary between competitions.
Do not assume every match in the response finished normally. A day's results can include matches that were abandoned, postponed on the day or awarded by a competition authority. Display whatever state the source supplies with each match. A results page that shows an abandoned match as a nil-nil draw is making a claim the data does not support.
The morning-after page
A classic use is a page that shows yesterday's results grouped by competition, refreshed early each morning. One request with offset 1, made after the last late kick-off has finished in every timezone you cover, produces the whole page. Add offsets 2 to 7 if you want a week of results navigable by day.
Remember that offsets slide at midnight, so store each response with the date you derived from its fetch time and render headings from that stored date. A cache keyed only by offset will show the wrong day's results under a heading the moment the date changes.
- Refresh offset 1 after late kick-offs have finished everywhere you cover.
- Store each day under a computed date, not just its offset.
- Group by competition and show each match state beside its score.
- Link each result to deeper match detail where your plan includes it.
Rolling a week of results into storage
The example fetches all seven offsets once a day and upserts each match by its string identifier. Because the same match appears under offset 1 today and offset 2 tomorrow, upserting rather than inserting avoids duplicates and lets a correction overwrite the earlier value. Run it once each morning and once in the evening and you will catch most amendments made in the days after a match.
After seven days, a match leaves this feed. If you need it longer, keep your stored copy, or request the season through historical fixtures when you build a permanent archive.
const base = "https://footballapi.net/api/v1/football/recent-results";
const headers = { "X-API-Key": process.env.FOOTBALLAPI_KEY };
for (let n = 1; n <= 7; n++) {
const res = await fetch(`${base}?offset=${n}`, { headers });
const body = await res.json();
if (!res.ok) { console.warn(n, body.code, body.requestId); continue; }
for (const match of readMatches(body.data)) { // your mapping from a real sample
await upsertResult(String(match.id), match, body.meta.fetchedAt);
}
}Corrections after the final whistle
Results are not always final when the match ends. A goal may be reassigned to a different scorer, a match result may be changed after a disciplinary decision, or a data entry error may be fixed the next day. The seven-day window is useful precisely because it lets you see those corrections. If you only ever store the first version of a result you saw, you will publish the uncorrected one indefinitely.
Keep a small history of changes for each match: the previous values and when you saw the new ones. If a correction changes a scoreline, your editors may want to know, especially if a round-up has already been published. A simple daily report of changed results is cheap to build from that history and saves embarrassment.
Results for gameweek tools
Fantasy and prediction-league tools, the kind where friends compete to forecast scores for fun rather than money, depend on reliable results and on knowing when a result is settled. The feed tells you what the source reports; it does not certify that a result will never change. Build a settlement rule into your product, such as treating a result as final for scoring purposes a set time after it first appears, and tell your users what the rule is.
These tools also tend to run their scoring at the same moment for every user, which produces a burst of reads. Read from your stored copy, not the API. A single scheduled fetch feeds every user's score calculation.
Corrections after publication
Results are occasionally corrected after first publication: a goal reassigned, a score amended after review, a match awarded. Your refresh should compare each result with your stored copy by match identifier and update it when the source changes, recording the time of the change. If your product publishes results to readers through notifications or social posts, decide in advance how you will handle a correction, and avoid sending a second alert unless the score itself changed.
Keep match states as supplied. A postponed or abandoned match is not a result, and it should not be displayed with a score of nil-nil.
Choosing the offset window
The offset parameter takes a value from 1 to 7 and returns results for that many days before today. A one-day window suits a frequent refresh of yesterday's matches; a seven-day window suits a weekly round-up or a recovery job after downtime. Most products run a short window often and a long window once a day, which catches late corrections without requesting the same week repeatedly.
Errors during refresh
An offset outside 1 to 7 returns a 400 invalid_parameter response. Upstream 502 or 504 errors are temporary; keep serving stored results, retry later and log the requestId. A run of failures should alert you rather than leave a results page silently stale.
Plan scope
Recent results is league filtered. On Starter at EUR 29 a month, responses are filtered to your five saved leagues. Growth at EUR 99 and Pro at EUR 249 receive the available package competitions. Coverage varies by competition and date on every plan, and the feed qualification is sample received.
Seven requests twice a day is fourteen successful requests, a negligible share of any daily inclusion. Successful requests above the inclusion cost EUR 0.002 each on the monthly EUR invoice, and failed or denied requests are not billed. No free trial is offered and no VAT is currently charged.
Questions developers ask
- What does offset mean for recent results?
- Days before today, as a whole number from 1 to 7 sent as a string. Offset 1 is yesterday.
- Can I get today's finished matches here?
- No. Use the scores feed for today. Recent results starts from yesterday.
- How far back does recent results go?
- Seven days. For older matches, use your stored copy or the historical fixtures feed.
- Can a result change after it appears?
- Yes. Sources can amend results. Refresh the week and upsert by match identifier so corrections replace earlier values.
- Is the feed filtered on Starter?
- Yes. Starter responses contain only your saved leagues.
- Will an abandoned match show as a result?
- It appears with whatever state the source supplies. Display that state rather than treating the score as final.
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.