--- title: Errors and rate limits description: The single error shape every Luigi's Box AI endpoint returns, what each status code means, and how rate limiting is reported. slug: api-basics/errors-and-rate-limits docKind: reference hub: luigisbox-ai --- Every non-2xx response from every Luigi's Box AI endpoint has the same body. ## The error body ```json { "reason": "Invalid filter expression.", "exception_details": {}, "request_id": "01JQ8H3Z9K2F7M4N6P8R0S2T4V" } ``` | Field | Meaning | |---|---| | `reason` | A human-readable, actionable message. Show it to a developer, not to a shopper. | | `exception_details` | Machine-readable context. `{}` when there is nothing to add. | | `request_id` | Correlation ID, matching the `X-Request-Id` response header. | Log `request_id`. Luigi's Box support uses it to find the request. ### Validation failures When the problem is a specific value, `exception_details.validation_errors` lists every field-level problem in the request: ```json { "reason": "Request validation failed", "exception_details": { "validation_errors": [ { "loc": ["query", "size"], "msg": "Input should be less than or equal to 100", "type": "less_than_equal" }, { "loc": ["body", "objects", 0, "@id"], "msg": "Field required", "type": "missing" } ] }, "request_id": "01JQ8H3Z9K2F7M4N6P8R0S2T4V" } ``` - `loc` is the path to the offending value from the request root. - `type` is a stable machine code — safe to branch on. - `msg` explains the problem in words — safe to log, not to branch on. ## Status codes | Status | Meaning | What to do | |---|---|---| | `400` | Malformed request — unparseable body, bad encoding | Fix the request; do not retry | | `401` | Missing, expired or wrong-audience token | Get a fresh token and retry once | | `403` | Authenticated, but not permitted on this resource | Check the permission and the resource you named | | `404` | Not found — or not visible to your credentials | See the note below | | `409` | Conflict with the resource's current state | Re-read the resource and retry with what you find | | `422` | Validation failed | Read `validation_errors`; do not retry unchanged | | `429` | Rate limit exceeded | Back off — see below | | `502` | An upstream step failed | Retry with backoff | | `503` | Temporarily unavailable | Retry with backoff | :::note[`403` and `404` are both authorization answers] A `403` means your credentials hold *some* grant on the resource but not the one this call needs. A `404` on a single resource means they hold *no* grant on it at all — the API does not confirm that something exists to a caller who cannot see it. A `404` on a resource you expect to exist can therefore be a permissions problem rather than a wrong ID. Collection endpoints always answer `403` rather than `404`, because you named the scope yourself. On a listing that takes `organization_id`, that parameter *is* the scope: send it and credentials granted on that organization are enough, omit it and the listing spans every organization, which takes a grant that wide. ::: ## Which failures are worth retrying | Retry | Do not retry | |---|---| | `429`, after the interval it tells you | `400`, `422` — the request itself is wrong | | `502`, `503`, with exponential backoff | `403`, `404` — permissions do not change on retry | | `401`, exactly once, with a new token | `409` without re-reading the resource first | Add jitter to the backoff, so that a fleet of workers does not retry at the same instant. ## Rate limits Rate-limited endpoints report your budget on every response, not only on a `429`: | Header | Meaning | |---|---| | `RateLimit-Limit` | Requests allowed in the current window | | `RateLimit-Remaining` | Requests left in it | | `RateLimit-Reset` | Seconds until the window resets | Exceeding the limit gives you `429` with the standard error body and a `Retry-After` header in seconds: ```text HTTP/1.1 429 Too Many Requests Retry-After: 42 RateLimit-Limit: 100 RateLimit-Remaining: 0 RateLimit-Reset: 42 ``` Honour `Retry-After`. It is an upper bound on the wait; a client that respects it does not retry into the same window. `RateLimit-Remaining` on successful responses shows a job approaching its budget before it starts receiving `429`. If the limits do not fit your traffic, contact Luigi's Box — they are set per account and can be raised. ## Refused traffic A `403` that is not about permissions is usually [bot control](/api-basics/bot-control/) — most often a server-side integration attempting the browser token flow, or a page origin that was never registered for its publishable key. ## Debug output Some discovery responses can carry a `debug` object describing ranking decisions. It is gated on a permission granted only to Luigi's Box staff, and its contents carry no compatibility guarantee. Passing `debug=true` without that permission is ignored. ## See also - [Requests and responses](/api-basics/requests-and-responses/) - [Authentication overview](/authentication/overview/) — audiences and permissions - [Feed troubleshooting](/indexing/feeds/troubleshooting/) — ingestion-specific problems