Skip to main content
Errors are JSON, with a stable machine-readable error code and a human message. Branch on the code — the message is written for a person and may be reworded.

The shape

The x-request-id header is present on errors too, including on a 500. Capture it — without it a report of an intermittent failure cannot be traced back to the request that produced it.

Codes

Individual services add codes of their own for a failure that is specific to them — scrape_failed and fetch_failed are both 502 and both mean the page could not be retrieved. They are documented on each service’s page. A 400 carries a details object naming what was wrong: {"error":"validation_error","message":"Invalid request body","details":{"issues":["prompt"]}} means the prompt field failed. Only validation errors carry it, and only field names, allowed values and limits — never the values you sent, and never anything about your organization.

What a failure costs

Only work that produced a result is billed. A validation error, an auth failure, a rate limit, a timeout, or an upstream failure costs nothing.
Failed requests are still recorded, so they appear in your usage dashboard with their status — you can see a spike of 429s without being charged for it. The quantity billed is what the handler actually consumed, which is why a partially successful call (an extraction that needed a retry) reports exactly what it used in the response body.

Retrying

  • Retry 429, 500, 502 and 503 with exponential backoff and jitter.
  • Do not retry 400, 401 or 403 — the same request will fail identically until you change something.
  • Do not retry 402 in a loop. It means spending is blocked until a human adds a card, fixes one, or raises a budget.
  • Retries are billed like any other request: a successful retry of a failed call is one billable unit, because the failed attempt was free.