Error codes

Every error returns {"success": 0, "error": {…}} with the HTTP status that matches. The code is stable and safe to branch on; message is written for a human reading a log. param names the offending input where one applies, request_id is worth quoting if you write in, and doc_url points at the entry on this page.

{
  "success": 0,
  "error": {
    "type": "invalid_request",
    "code": "invalid_time_window",
    "message": "The window may span at most 24 hours.",
    "param": "end_time",
    "doc_url": "https://5dollarfootballapi.com/docs/errors#invalid_time_window",
    "request_id": "req_0xyuw9pv3baf"
  }
}

Authentication

HTTP 401
missing_api_key type: authentication_error

No key reached us.

Usually because 
The header was absent, spelled differently, or dropped by a proxy or a spreadsheet add-on that cannot send headers.
What to do 
Send Authorization: Bearer fb_live_…, or X-API-Key: fb_live_… if the client makes that easier. Tools that cannot set headers at all — Google Sheets' IMPORTDATA, for one — cannot call this API; use Apps Script instead.
invalid_api_key type: authentication_error

A key arrived but does not match a live key.

Usually because 
Revoked, mistyped, or a key from a different account. Keys are shown once at creation and only their hash is stored, so a truncated copy fails here rather than at creation.
What to do 
Check the prefix against the one in your dashboard. If it does not match, issue a new key — we cannot recover the old one.
expired_api_key type: authentication_error

The key was valid and has passed its end date.

Usually because 
Trial and evaluation keys are issued with an end date; the account behind them is unaffected. Keys without one never expire.
What to do 
The message names the date it ended. Create a key from your dashboard, or reply to the email the trial key came in and we will sort you out.

Plan limits

HTTP 403
insufficient_plan type: permission_error

The key is valid; this resource is outside its plan.

Usually because 
A competition your plan does not cover, include=odds on a fixture list below Pro, odds history below Ultra, a bookmaker other than Bet365 below Ultra, or a date older than your plan's history window.
What to do 
The message names which of those it was. We return this rather than an empty list on purpose — an empty array is indistinguishable from "no matches that day", and silently returning nothing is how an import ends up with holes nobody notices.

Request parameters

HTTP 400
invalid_time type: invalid_request

`start_time` or `end_time` is not a unix timestamp.

Usually because 
An ISO date string, milliseconds instead of seconds, or a formatted date from a spreadsheet cell.
What to do 
Send whole seconds since the epoch, UTC. Milliseconds are the common one — divide by 1000.
invalid_time_window type: invalid_request

The window is backwards, or spans more than 24 hours.

Usually because 
`end_time` at or before `start_time`, or a range wider than a day on `/v1/fixtures`.
What to do 
/v1/fixtures answers a bounded window — at most 24 hours — so that it can never turn into a range scan. For a longer stretch, walk it a day at a time: call again with start_time moved forward 86400 seconds. The window is [start_time, end_time), so consecutive days share no fixture and nothing is counted twice. end_time is optional and defaults to start_time + 24h, so paging usually means changing one number.
invalid_status type: invalid_request

Unrecognised `status`.

Usually because 
A value outside the four we accept.
What to do 
Use all, scheduled, live or finished. live ignores the time window and returns everything in play right now.
invalid_order type: invalid_request

Unrecognised `order` on a fixture list.

Usually because 
A value other than `asc` or `desc` — often a sort field name, or a direction spelled out.
What to do 
Use desc (the default, newest kickoff first) or asc. Only /v1/leagues/{id}/fixtures and /v1/teams/{id}/fixtures take it; the order is always by kickoff, never by another field.
invalid_include type: invalid_request

Unrecognised `include`.

Usually because 
A typo, or a name from another provider's API.
What to do 
Use any comma-separated mix of odds, events and stats. Adding an include caps per_page at 50 to bound the payload.
invalid_market type: invalid_request

Unrecognised `market` on an odds endpoint.

Usually because 
A market this bookmaker does not carry, or a name from elsewhere.
What to do 
Bet365 carries 1x2, asian, goalline, corner, their _half variants, plus corner_asian, cards, cards_asian and btts. Every other bookmaker carries 1x2, asian, goalline and corner.
invalid_bookmaker type: invalid_request

Unrecognised bookmaker slug.

Usually because 
A display name rather than the slug, or a book we do not carry.
What to do 
Call GET /v1/bookmakers for the current list of slugs. bookmaker takes exactly one.
invalid_bookmakers type: invalid_request

One or more slugs in a `bookmakers` list are unrecognised.

Usually because 
A typo in a comma-separated list; the message names the offender.
What to do 
Check the list against GET /v1/bookmakers. bookmakers (plural) takes several, comma-separated.
invalid_type type: invalid_request

Unrecognised `type` on standings.

Usually because 
A value outside the three tables we build.
What to do 
Use total (the points table, the default), corner or card.
invalid_esports type: invalid_request

`esports` is not a boolean.

Usually because 
Something other than true/false.
What to do 
Send true or false. It defaults to false, which is real football; true returns esoccer instead, never both.
invalid_lang type: invalid_request

Unrecognised `lang` code.

Usually because 
A locale we do not translate into, or a full locale where a short code is expected.
What to do 
The message lists every supported code. Omit lang, or pass en, for English. A name with no translation falls back to English on that row rather than coming back empty, so a response is never partly blank.
invalid_country type: invalid_request

Unrecognised `country` code on /v1/leagues.

Usually because 
A code that is not one /v1/countries lists — a country name, a three-letter code, or a confederation acronym such as UEFA.
What to do 
Pass the code from /v1/countries: ISO 3166-1 alpha-2 (DE, BR), GB-ENG / GB-SCT / GB-WLS / GB-NIR for the home nations, or a region code (EUROPE, ASIA, AFRICA, AMERICAS, OCEANIA, WORLD) for continental and international competitions. Case does not matter.
missing_parameter type: invalid_request

A required parameter is absent.

Usually because 
Usually `league` on `/v1/standings`, or `market` on the odds history endpoint.
What to do 
The message names it. Required parameters are marked in the endpoint reference.
unknown_parameter type: invalid_request

A parameter we do not support was sent.

Usually because 
Habits from another API — `date`, `from`, `to`, `team` and `live` are the usual ones.
What to do 
The message suggests the equivalent. We reject unknown parameters rather than ignoring them: a filter that is silently dropped returns a full result set that looks exactly like a filtered one, and that quietly corrupts whatever you are building.

Moved

HTTP 301
league_moved type: moved

That league id was retired when two duplicate rows were merged; the survivor is in `error.canonical_id` and in the `Location` header.

Usually because 
The feed occasionally opens a second row for a competition (a rename, a split season). We merge them, moving every fixture to the surviving id, so the retired id no longer owns anything.
What to do 
Most HTTP clients follow the 301 automatically and you get the canonical league's data with its real id in the payload. Update the id you store; the redirect is permanent and costs a request each time.

Not found

HTTP 404
fixture_not_found type: invalid_request

No fixture with that id.

Usually because 
An id from another provider, or one outside our coverage.
What to do 
Fixture ids come from /v1/fixtures and are ours — ids from other football APIs will not resolve here.
league_not_found type: invalid_request

No league with that id.

Usually because 
Same as above, or a guessed id.
What to do 
Look ids up with GET /v1/leagues, or in the league coverage reference.
team_not_found type: invalid_request

No team with that id.

Usually because 
An id from elsewhere, or a team outside our coverage.
What to do 
Team ids appear on every fixture row under teams.home.id and teams.away.id.
season_not_found type: invalid_request

That league has no such season.

Usually because 
A season the feed does not define for this competition — coverage varies by league, and not every one has a season record.
What to do 
GET /v1/leagues/{id} lists the seasons that exist, newest first. Omit season to get the newest with fixtures.
standings_not_available type: invalid_request

That competition has no league table.

Usually because 
Cups, play-offs and some smaller competitions have no table to build — and where the feed's data is incomplete enough that a computed table could be wrong, we decline rather than publish a table that looks authoritative and is not.
What to do 
Nothing to change in the request. Fixtures, results, statistics and odds for the competition are unaffected; only the table is missing. GET /v1/leagues/{id} reports whether a competition has standings at all.
resource_not_found type: invalid_request

The addressed resource does not exist.

Usually because 
The generic 404, used where a more specific code does not apply.
What to do 
Check the path and the id against the endpoint reference.

Rate limits

HTTP 429
too_many_requests type: rate_limit_error

The window's allowance is spent.

Usually because 
More calls than the plan permits inside one window — 60 an hour on Free, 10 a minute on Pro, 40 a minute on Ultra.
What to do 
Read Retry-After on the 429 and wait that many seconds; every successful response also carries X-RateLimit-Remaining and X-RateLimit-Reset, so a client can slow down before it hits this rather than after. Finished matches never change, so caching what you already pulled removes most repeat traffic.

Server

HTTP 500
internal_error type: api_error

The failure is ours, not yours.

Usually because 
A bug or an upstream problem. Nothing about the request needs changing.
What to do 
Retry with a short backoff. If it persists, send us the request_id from the response — it points straight at the log line.

Still stuck?

Send the request_id from the response to support. It points at the exact log line, which is faster than describing what happened.