# Errors and rate limits

> HTTP status codes, the error object and how to handle HTTP 429 with Retry-After.

Source: https://docs.serenityedge.ai/docs/errors-and-rate-limits/

## Error object

Every error response has the OpenAI error shape, so SDK exception classes map directly.

```json
{
  "error": {
    "message": "Rate limit reached. Please retry after 12 seconds.",
    "type": "rate_limit_error",
    "param": null,
    "code": "rate_limit_exceeded"
  }
}
```

| Field | Meaning |
| --- | --- |
| `message` | Human-readable description. Safe to log; do not show it to end users verbatim. |
| `type` | Category: `invalid_request_error`, `authentication_error`, `permission_error`, `not_found_error`, `rate_limit_error` or `server_error`. |
| `code` | Machine-readable code: `invalid_request_error`, `invalid_api_key`, `permission_denied`, `model_not_found`, `not_found`, `context_length_exceeded`, `too_many_images`, `request_too_large`, `rate_limit_exceeded`, `upstream_unavailable`, `upstream_error` or `timeout`. May be `null`. |
| `param` | The request parameter the error refers to, when applicable. May be `null`. |

## HTTP status codes

| Status | `type` | When | What to do |
| --- | --- | --- | --- |
| 400 | `invalid_request_error` | Malformed JSON, unknown or out-of-range parameter, or prompt larger than the context window (`context_length_exceeded`). | Fix the request. Do not retry as is. |
| 400 | `invalid_request_error` | More images than the model reads, counted over the whole conversation: from 1 to 8 depending on the model (see [Image input](https://docs.serenityedge.ai/docs/models/#image-input)), for example `This model reads at most 1 image(s) per request.` on `paddleocr-vl-1.6`. `code` is `too_many_images` and `param` is `messages` (`input` on `POST /v1/responses`). The limit of each model is `supported_inputs.references.max` of its `image` input modality in [`GET /v1/models`](https://docs.serenityedge.ai/docs/api/edge/list-models/). | Send only the most recent images, up to the limit, and replace the older ones with a short text. Do not retry as is. |
| 400 | `invalid_request_error` | On [embeddings, rerank and redaction](https://docs.serenityedge.ai/docs/embeddings-rerank-and-redaction/#errors): a model sent to a route that does not serve it (`The model 'qwen3-reranker-0.6b' is not served on /v1/embeddings.`, `param: "model"`), or a request the model could not process as sent (`The model server rejected the request. Check the input sizes and parameters.`). | Use the model of the route, or shorten the inputs and check the parameters. Do not retry as is. |
| 401 | `authentication_error` | Missing, malformed or revoked API key. | Check the `Authorization` header and the key. |
| 403 | `permission_error` | The key is valid but not allowed to use this model or feature, for example because of its plan or credit balance, or because it cannot use the [decision endpoints](https://docs.serenityedge.ai/docs/decisions/#access) yet, as with keys from a Serenity Star account (`permission_denied`). The [embeddings, rerank and redaction](https://docs.serenityedge.ai/docs/embeddings-rerank-and-redaction/#access) routes name their API in the message: `This API key cannot use the embeddings API. Use a Serenity Edge API key.` (`rerank`, `redact`). | Check your plan, credits or contract; for decisions, embeddings, rerank and redaction, use a Serenity Edge API key. |
| 404 | `not_found_error` or `invalid_request_error` | Unknown model id or path. | Check `GET /v1/models`. |
| 413 | `invalid_request_error` | Request body larger than 4 MB, or than 192 KiB (196,608 bytes) on `POST /v1/redact` (`request_too_large`). | Reduce the request. Do not retry as is. |
| 422 | `invalid_request_error` | `POST /v1/systemone` only: the decision model refused a question or the state, as Jev's format does. The same refusal is a 400 on `POST /v1/decisions`. See [Decisions](https://docs.serenityedge.ai/docs/decisions/#errors). | Fix the request. Do not retry as is. |
| 429 | `rate_limit_error` | Requests or tokens per minute exceeded, credits exhausted, or the service temporarily at capacity for the model (`rate_limit_exceeded`). | Wait for `Retry-After`, then retry with backoff. |
| 500 | `server_error` | The model service returned an unexpected response (`upstream_error`), the request took longer than 600 seconds (`timeout`), or an unexpected error in the gateway. | Retry with backoff; for `timeout`, reduce `max_tokens`. |
| 503 | `server_error` | The model service is temporarily unavailable (`upstream_unavailable`). | Retry later; check the status page. |

A 500 or 503 that persists across retries usually means an incident or a capacity event rather than a problem with your request. The current and historical availability of the API is published on the status page at [status.serenityedge.ai](https://status.serenityedge.ai/); check it before opening a support request.

## Rate limits and HTTP 429

Each API key has a requests-per-minute and a tokens-per-minute allowance. Exceeding either returns HTTP 429 with:

-   a `Retry-After` header with the number of seconds to wait;
-   an error of type `rate_limit_error`.

A 429 with `code: "insufficient_credits"` means a prepaid balance has run out; top up in your [Serenity Star account](https://docs.serenityedge.ai/docs/access/serenity-star-account/) rather than retrying.

### Handling 429 correctly

1.  Read `Retry-After` and wait at least that long.
2.  Retry with exponential backoff and jitter; cap the number of attempts.
3.  Do not spin up more concurrency in response to 429. Rate limits are per key, so extra workers make it worse.
4.  Reduce the number of prompt tokens when you hit the token limit: shorter system prompts, fewer conversation turns, and [cached input](https://docs.serenityedge.ai/docs/pricing-and-limits/#cached-input) for repeated prefixes.

The official OpenAI SDKs retry 429, 500 and 503 automatically with backoff. Set `max_retries` to tune this:

```python
from openai import OpenAI

client = OpenAI(
    base_url="https://api.serenityedge.ai/v1",
    api_key="YOUR_SERENITY_EDGE_API_KEY",
    max_retries=5,
)
```

### Raising your limits

Limits scale with your access route. A [router or marketplace](https://docs.serenityedge.ai/docs/access/channels/) applies its own account limits. A Serenity Star account starts with a default allowance that grows with usage. An [enterprise contract](https://docs.serenityedge.ai/docs/access/enterprise/) sets limits and reserved capacity explicitly. If a default limit blocks you, write to [contact@substrate.ai](mailto:contact@substrate.ai) with your key prefix and the throughput you need.

## Timeouts

Long generations can take a while. Set the client timeout to at least 60 seconds for non-streaming requests, or use [streaming](https://docs.serenityedge.ai/docs/streaming/) so the connection stays active while tokens arrive.
