Docs/Reference/Errors

Errors

What each failure means, which are worth retrying, and the two statuses that do not mean what their name suggests.

Failures use the same envelope as successes. status becomes "error", data becomes null, and error carries one sentence written for a human:

json
{
  "status": "error",
  "error": "The 'email' parameter is required.",
  "data": null
}

Read the HTTP status to decide what kind of problem it is, and error to decide what to change. The sentence names the parameter; it is not a generic code you have to look up.

Statuses

StatusMeaningRetry?
200Success
400The request is wrong — bad parameter, missing field, or an upload over the plan's size limitNot until it changes
401Key is well-formed but not recognisedNo
403Key is valid but not allowed to make this callNo
404No such endpoint on this doorNo
429Rate limited or out of creditsSometimes — see below
500Something failed on our sideYes, with backoff
503Upstream source is unavailableYes, with backoff

Two of these do not mean what their names suggest, and both are worth reading carefully.

An HTML 403 means the request never arrived

Before the status table applies, the request has to reach the API at all. Two things stop it at the edge, and both look identical: an HTML 403 with no JSON body and no CORS headers.

  • No x-api-key header at all.
  • A key that is not in the expected format. Keys are UUIDs. A truncated key, a placeholder like YOUR_API_KEY, or an env var that resolved to empty is rejected on shape alone.
bash
# Malformed key -> Cloudflare's own HTML error page, not the API's JSON
curl -i 'https://api.apiverve.com/v1/dadjokes' -H 'x-api-key: av-test-123'
HTTP/1.1 403 Forbidden
Content-Type: text/html; charset=UTF-8

A well-formed but wrong key behaves completely differently — a real 401 with a JSON body:

json
{ "status": "error", "error": "unauthorized", "data": null }

This distinction is worth internalising because of how it surfaces in a browser: the HTML 403 carries no CORS headers, so fetch cannot read it and reports TypeError: Failed to fetch with no status at all. An undefined environment variable is the usual cause. See CORS.

403 is not an authentication failure

401 is the only status that means "we do not accept this key". If you get one, the credential is wrong — check it before anything else.

403 means the opposite: the key is real and accepted, but it is not permitted to make this particular call. There are three causes, and the error sentence tells you which:

error saysCauseFix
Access to <api> is blocked for this API keyKey scoping blocks this endpointAdjust the key's restrictions
IP address not allowed for this API keyThe caller's IP is not on the key's allow-listAdd the IP, or use a different key
API key has been revoked.The key was rotated or deletedUse the current key

Treating a 403 as "sign in again" sends someone to fix something that is not broken. See key scoping.

429 is two different problems

A 429 is either slow down or the month is spent, and they need opposite responses. The discriminator is the credit header, not the message:

js
if (res.status === 429) {
  if (Number(res.headers.get('x-api-remaining-credits')) === 0) {
    // Out of credits. Retrying cannot help.
  } else {
    // Rate limited. Wait retry-after and repeat.
  }
}

Full treatment, including backoff that does not make it worse, is on rate limits.

404 means "not on this door"

A 404 is returned both for an endpoint that does not exist and for one that exists but is not part of this product's catalog. That is deliberate — the response does not reveal that an endpoint you cannot call exists elsewhere.

If a call worked yesterday and 404s today, check you are calling the host that matches your account rather than assuming the endpoint was removed.

Validation errors

A 400 names what to change:

json
{
  "status": "error",
  "error": "The 'dob' parameter must be a valid date in YYYY-MM-DD format.",
  "data": null
}

Undeclared parameters are a special case worth knowing: a parameter the endpoint does not declare is dropped silently rather than rejected. The call succeeds and quietly ignores your input. If a parameter appears to have no effect, check its spelling against the reference page before assuming it is broken.

Handling errors in code

One handler covers every endpoint, because the envelope never changes:

js
async function call(endpoint, params, key) {
  const url = new URL(`https://api.apiverve.com/v1/${endpoint}`);
  Object.entries(params).forEach(([k, v]) => url.searchParams.set(k, v));

  const res = await fetch(url, { headers: { 'x-api-key': key } });
  const body = await res.json().catch(() => null);

  if (res.ok && body?.status === 'ok') return body.data;

  const message = body?.error || `HTTP ${res.status}`;
  const retryable =
    res.status >= 500 ||
    (res.status === 429 && Number(res.headers.get('x-api-remaining-credits')) !== 0);

  throw Object.assign(new Error(message), { status: res.status, retryable });
}
Check the body, not just the status

A 200 with status: "error" is possible on the multi-result paths — batch and GraphQL both return 200 for a response where some items failed. Anywhere you fan out, the per-item status is the one that matters.

Server errors

500 and 503 are ours, not yours. They are safe to retry, with backoff, and they are rare enough that a persistent one is worth reporting rather than working around.

Every response carries two identifiers that let support find your exact call instead of guessing. Quote either one:

HeaderIdentifies
cf-rayThe edge request. Present on every response, including the ones the edge answers itself.
x-cloud-trace-contextThe origin request, once it reached the API. Absent when the edge answered.
bash
curl -sS -D - -o /dev/null 'https://api.apiverve.com/v1/weather?city=London' \
  -H 'x-api-key: YOUR_API_KEY' | grep -iE 'cf-ray|cloud-trace'
Neither is readable from browser JavaScript

cf-ray and x-cloud-trace-context are not in the CORS exposed set, so headers.get() returns null for both in a browser. Capture them server-side, or read them from the network panel. See CORS.

What is worth retrying

  • Retry with backoff: 500, 503, and a 429 that still has credits left.
  • Never retry unchanged: 400, 401, 403, 404, and a 429 with zero credits. Nothing about the next attempt will be different, and repeated identical failures get their own throttle.

Next

Response shape and the premium-field rule are in response format. Ceilings, headers and backoff are in rate limits.

The client-side shape that acts on all of it — timeouts, retries, caching — is in making requests. Per-item failures inside a bulk call behave slightly differently; see batch requests.

If a failure here is not the one you expected, the FAQ covers the common surprises.

Was this page helpful?

Last updated