Errors and status codes

Errors are JSON, always the same shape, and always carry a stable machine-readable code. Branch on the code, not on the message.

The shape

Every failure returns an object with a human-readable error and a stable code. The message is written for a person and may be reworded; the code is a contract and will not change under you.

{
  "error": "That file is larger than the 200 MB limit on your plan.",
  "code": "VALIDATION_FAILED"
}

Codes you should handle

CodeHTTPWhat it means
NOT_AUTHENTICATED401Missing or malformed bearer token
FORBIDDEN403The key is valid but not allowed to do this
NOT_ENTITLED402The account's plan does not include API access
VALIDATION_FAILED400 / 413Bad request, unsupported conversion, or file too large
NOT_FOUND404No such job, or it belongs to another account
RATE_LIMITED429Hourly limit reached. Wait and retry.
INTERNAL500Something broke on our side. Safe to retry.

What is worth retrying

429 and 500 are worth retrying, with a delay — back off rather than looping, because retries count against your hourly limit like any other request.

Everything else is permanent for that request. Retrying a 400, a 402 or a 404 produces the same answer and spends your rate limit finding out.

A 502 or 503 without a JSON body did not come from the application — that is the proxy in front of it, and it is always worth retrying.