Errors

Every error returns the standard envelope with errorpopulated and data set to null. The HTTP status code reflects the error class.

Error shape

jsonjson
{
  "error": {
    "code": "bad_request",
    "message": "invalid match id \"12345\""
  },
  "suggested_fix": "IDs are Big Balls UUIDs you get from the corresponding list endpoint, not from another source. Call GET /v1/matches?sport=football and copy the `id` field of any result into this path.",
  "example": "https://api.bigballsdata.com/v1/matches?sport=football&limit=3",
  "docs_url": "https://bigballsdata.com/docs/matches",
  "support": {
    "discord": "https://discord.gg/H2WJBQurbq",
    "email": "[email protected]"
  },
  "meta": {
    "request_id": "375110c3-c77a-4b26-b0d1-dfa8e8085611",
    "timestamp": "2026-06-16T15:08:07.218Z"
  }
}

The error object carries code and message, plus an optional validation_errors map on validation failures. Alongside it the envelope returns up to five top-level fields: suggested_fix (a concrete corrected request, included only when the fix is unambiguous, omitted otherwise), example (a full copy-paste URL that works today, on the errors where we can build one), docs_url, support, and meta. Always log meta.request_id. When you email [email protected], include it: with the request_id we pull the exact gateway trace instantly, no database search required. Without it, the same request needs a manual lookup.

Error codes

These are the eleven codes defined for error.code. They are part of the public contract and will not be renamed without a version bump. Every one of them is emitted by the gateway today.

StatusCodeCauseRetry?
400bad_requestMalformed or missing query / body parameterNo, fix the request
401unauthorizedMissing, invalid, or revoked API keyNo
403forbiddenKey's plan tier too low, or an IP-restricted routeNo, upgrade
404not_foundA real endpoint, but the resource id does not exist or is not yet ingestedNo
404route_not_foundThe path is not a route we serve (a bad URL or a guessed endpoint). The body names the nearest real endpoint in meta.did_you_mean and a corrected, copy-paste request in suggested_fix. Distinct from not_found, which means a real endpoint's id missed.No, fix the URL
403key_pausedKey was paused by an operator. Reversible — reactivate it from the dashboard, or email support. Not an upgrade prompt.No, reactivate
409conflictA concurrent request with the same Idempotency-Key is still resolvingYes, after a moment
422unprocessableValue rejected by the canonical schemaNo
429rate_limitedPer-minute or per-day quota exceededAfter Retry-After
500internalUnhandled exception. A bare 502/503 with no JSON body is the edge, not the API — retry it the same way.Yes, backoff
503upstream_unavailableA transient upstream or catalog read failed. Your request is fine — this one is on us.Yes, backoff

Common 400s

Two requests cause most 400s. Both return error.code: "bad_request" with a message that names the fix.

A non-UUID id on /v1/matches/:id

Single-resource routes take a Big Balls UUID that you read off the matching list endpoint. A numeric or third-party id is rejected immediately, before any upstream lookup, and the suggested_fix names the list call that hands you a real one. A bare /v1/matches with no filter is not an error — it returns live and upcoming matches across every sport.

bashbash
# 400 - 12345 is not a Big Balls UUID
curl "https://api.bigballsdata.com/v1/matches/12345?sport=football" -H "Authorization: Bearer $KEY"

# 200 - no filter at all: live + upcoming across every sport
curl "https://api.bigballsdata.com/v1/matches" -H "Authorization: Bearer $KEY"

# 200 - list first, then copy an id from the response
curl "https://api.bigballsdata.com/v1/matches?sport=football&limit=3" -H "Authorization: Bearer $KEY"

A provider id on /v1/players/:id (the id came from a different response)

The most common shape of this mistake isn't a made-up id, it's a real id from our own API — just from the wrong field. Match lineups and WC2026 squads return a provider id (bdl_player_id / af_player_id) next to the player's name, not our UUID. Pasting that provider id into /v1/players/:id 400s immediately, before any upstream lookup:

bashbash
# 400 - 12345 is some other system's id, not a Big Balls UUID
curl "https://api.bigballsdata.com/v1/players/12345?sport=basketball" -H "Authorization: Bearer $KEY"

# 200 - search by name, then use the id THAT response returns
curl "https://api.bigballsdata.com/v1/players?name=lebron" -H "Authorization: Bearer $KEY"
curl "https://api.bigballsdata.com/v1/players/{id}?sport=basketball" -H "Authorization: Bearer $KEY"

The real suggested_fix on this error names the exact mechanism: "This is often OUR OWN response: match lineups and WC2026 squads return a provider id next to the player's name, not our UUID. Use that name at /v1/players?name=<query> instead, then use the id it returns." If you got the bad id from a Big Balls response rather than a third party, this is why.

Bad value on /v1/stored/matches

Every filter on /v1/stored/matches is optional, so a 400 means a value is malformed, never a missing param. date is YYYY-MM-DD, status is one of scheduled, live, finished, or cancelled, and limit is 1 to 200.

bashbash
# 400 - "upcoming" is not a valid status
curl "https://api.bigballsdata.com/v1/stored/matches?status=upcoming" -H "Authorization: Bearer $KEY"

# 200 - valid status + date
curl "https://api.bigballsdata.com/v1/stored/matches?status=finished&date=2026-06-08" -H "Authorization: Bearer $KEY"

Retry strategy

  • 5xx with Retry?: Yes, exponential backoff starting at 1s, capped at 60s, with full jitter. Stop after 5 attempts.
  • 429, sleep exactly Retry-After seconds before retrying. Never sooner.
  • 4xx other than 429, do not retry. Fix the request first.
  • Idempotency keys are required on POST if you intend to retry, see webhooks.