> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.empiriolabs.ai/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.empiriolabs.ai/_mcp/server.

# Errors and Status Codes

Every EmpirioLabs API endpoint returns standard HTTP status codes with an OpenAI-compatible error body. This page explains the status codes you may see and how to handle each one.

## Error response shape

A failed request returns a JSON body with an `error` object:

```json
{
  "error": {
    "message": "A human-readable description of what went wrong.",
    "type": "invalid_request_error",
    "code": "insufficient_credits",
    "param": null
  }
}
```

Every response also includes an `x-request-id` header. Include that value when you contact support so we can locate the exact request.

## Status codes

| Status                     | Meaning                                                                                                                                                                                                 | What to do                                                                                                    |
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| `200 OK`                   | The request succeeded. For async media, a job is returned.                                                                                                                                              | Continue. For async jobs, poll the returned `job_id`.                                                         |
| `202 Accepted`             | An async job was accepted.                                                                                                                                                                              | Poll `GET /v1/jobs/{job_id}` until it reaches a terminal state.                                               |
| `400 Bad Request`          | The request was malformed or rejected before any work ran: invalid JSON, a missing or invalid parameter, an endpoint the model does not support, or a prompt blocked by the content policy. Not billed. | Fix the request and resend. Do not retry unchanged.                                                           |
| `401 Unauthorized`         | The API key is missing, malformed, or not found.                                                                                                                                                        | Verify the `Authorization: Bearer sk-empiriolabs-...` header and that the key exists in your dashboard.       |
| `402 Payment Required`     | The account cannot cover the pay-as-you-go amount required by the request.                                                                                                                              | Add credits on the dashboard Billing page or choose a request fully covered by your remaining plan allowance. |
| `404 Not Found`            | The resource does not exist, for example an unknown `job_id` or model id.                                                                                                                               | Check the id and the endpoint path.                                                                           |
| `422 Unprocessable Entity` | The request was well-formed but could not be fulfilled as sent: a parameter is out of range for the model, or generated media was produced and then blocked by the output content check.                | Adjust the parameter or prompt. See content moderation below.                                                 |
| `429 Too Many Requests`    | You exceeded your account rate limit (default 50 requests per minute).                                                                                                                                  | Retry with exponential backoff and jitter, and review your request volume.                                    |
| `500`, `502`, `503`, `504` | A transient server or model-service error.                                                                                                                                                              | Retry with exponential backoff. If it persists, contact support with the `x-request-id`.                      |

## Content moderation: 400 versus 422

Content-policy rejections return one of two status codes, and the difference is whether any generation actually ran:

* **400**: the prompt was blocked before generation. Nothing was produced, so the request is not billed. Revise the prompt and try again.
* **422**: media was generated and then blocked by the output content check. Because the generation ran, the request is billed at the normal rate for that model.

In both cases the message is generic ("This request violates our content policy and could not be completed. Please revise your prompt and try again.") and does not name a provider. Revise your input to continue.

## 402 responses with a subscription plan

An active subscription can run a request at a zero credit balance only when the entire request is covered by remaining plan allowance. If any pay-as-you-go amount is required, including the remainder of a partly covered request, your available balance must cover that full amount before work starts.

When the balance is insufficient, the API returns `402 insufficient_credits` before generation and consumes neither plan allowance nor pay-as-you-go credits. A past-due, canceled, or expired subscription does not provide plan access.

## Retrying safely

* Retry `429` and `5xx` responses with exponential backoff and jitter.
* Do not blindly retry `400`, `401`, `402`, or `422`. They will keep failing until you change the request, key, balance, or input.
* Async media jobs are durable. If a poll returns a transient error, keep polling the same `job_id` instead of resubmitting, so you are not charged twice.

For rate-limit details see [Limits and API Keys](/rate-limits-api-keys). For key format and bearer-token setup see [Authentication](/authentication).