---
title: "Errors and limits: 401, 402, 429 codes — AI Creator API"
description: "AI Creator API error codes, key and service limits, and what to do on 401, 402, 403 and 429."
url: https://llm.aicreator.app/en/docs/errors/
lang: en
updated: 2026-11-10
---

# Errors and limits

Errors come in two kinds. Ours, when we stopped the request: the key, the balance, limits, the model. Provider errors come through as is, in their protocol’s format: Claude Code and Codex recognise them and retry by themselves when it makes sense.

If you want a walk-through by symptom rather than a reference (“I got a 401, now what”), see [Troubleshooting](https://llm.aicreator.app/en/docs/troubleshooting/).

## Format

An error of ours is JSON in a shape both Anthropic and OpenAI clients parse. Look at `error.code`: different causes can share an HTTP status.

```json title="429"
{
  "type": "error",
  "error": {
    "type": "rate_limit_error",
    "code": "rate_limit_exceeded",
    "message": "key rate limit exceeded"
  }
}
```

The `x-should-retry` header says whether a retry makes sense: `true` or `false`. When there is a time to wait, `retry-after` gives it in seconds. When the header and the Retry column of the table below disagree, trust the header: one code can have several causes. The 401 `invalid_api_key` and 503 `authentication_unavailable` answers come without this header.

Two refusals come not in this shape but as short plain text, with no JSON and no `error.code`: the 429 of the per-address limit (with a `retry-after` header) and the 413 for a request whose body size declared in Content-Length is over 32 MB. Both are covered in [service limits](https://llm.aicreator.app/en/docs/errors/#service-limits).

## Codes

| HTTP | Code                         | What happened                                                                 | What to do                                                                                  | Retry |
| ---- | ---------------------------- | ----------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | ----- |
| 400  | `invalid_request_body`       | The request body is not a readable JSON object.                               | Check the JSON and the Content-Type header.                                                 | no    |
| 400  | `model_required`             | The request has no model field, or it is empty.                               | Name a model.                                                                               | no    |
| 400  | `duplicate_model`            | The model field appears at the top level of the body more than once.          | Keep one model field.                                                                       | no    |
| 400  | `model_not_available`        | The model is not on our list, or not on this protocol.                        | Take an id from GET /v1/models for your protocol.                                           | no    |
| 400  | `request_too_large`          | One request is estimated above the key’s whole per-minute token limit.        | Shorten the request or increase the key’s token limit in the dashboard; waiting won’t help. | no    |
| 400  | `invalid_surface`            | GET /v1/models got an unknown surface value.                                  | Use chat, responses or anthropic.                                                           | no    |
| 401  | `invalid_api_key`            | The key is missing, copied in part or revoked.                                | Check the key header or create a new key in the dashboard.                                  | no    |
| 402  | `insufficient_balance`       | The balance can’t cover the reserve for the request.                          | Top up or lower max_tokens; retrying before that won’t help.                                | no    |
| 403  | `model_not_allowed`          | The key is restricted to some models, and this one is not among them.         | Use an allowed model or add this one to the key’s model list in the dashboard.              | no    |
| 408  | `request_timeout`            | The request body took too long to read.                                       | Retry the request.                                                                          | yes   |
| 413  | `request_too_large`          | The request body is over 32 MB.                                               | Trim the history or the attachments.                                                        | no    |
| 429  | `rate_limit_exceeded`        | The key’s per-minute limit on requests or tokens ran out.                     | Wait as many seconds as retry-after says.                                                   | yes   |
| 429  | `concurrent_requests`        | The key already runs the most requests it may at once: 4.                     | Retry in a second, or spread the load across keys.                                          | yes   |
| 429  | `monthly_limit_exceeded`     | The key’s monthly spend cap ran out.                                          | Wait for the next month or increase the key’s monthly cap in the dashboard.                 | no    |
| 502  | `upstream_unavailable`       | The model’s provider did not answer.                                          | Retry later unless x-should-retry says false. Nothing was charged.                          | yes   |
| 502  | `upstream_auth_failed`       | The provider rejected our access. The failure is on our side, not your key’s. | Contact support. Nothing was charged.                                                       | no    |
| 503  | `at_capacity`                | The service is overloaded: too many requests at once.                         | Retry in a few seconds, following retry-after.                                              | yes   |
| 503  | `authentication_unavailable` | We could not check the key. The key itself is fine.                           | Retry the request; no new key is needed.                                                    | yes   |
| 503  | `billing_unavailable`        | We could not reserve money for the request.                                   | Retry the request unless x-should-retry says false. Nothing was charged.                    | yes   |
| 503  | `pricing_unavailable`        | The model has no price set on this protocol.                                  | Use another model and contact support.                                                      | no    |
| 503  | `global_limit_exceeded`      | The monthly budget of the whole service ran out.                              | Contact support; your balance is untouched.                                                 | no    |
| 504  | `upstream_timeout`           | The provider did not finish within 30 minutes.                                | Retry the request; ask for long answers as a stream.                                        | yes   |

The code `request_too_large` comes with two statuses: 413 when the body is over the size limit, 400 when the request is above the key’s per-minute token limit. The 413 arrives as JSON when the body size is not declared up front (chunked); an ordinary request with Content-Length gets the 413 as plain text.

524 comes not from our API but from Cloudflare in front of it: a request without streaming waited more than about two minutes. Ask for long answers as a stream; see the [API reference](https://llm.aicreator.app/en/docs/api/#long-answers).

## Codes in the request log

A request we refused before sending it to the provider (401, 402, 403, our 400 and 429) does not reach the dashboard’s log and costs nothing. Requests that reached the provider are all in the log, with the HTTP status and an error code. Besides the codes of the table above, the log has:

| Code                    | What happened                                                                                                                                                                                                                 |
| ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `upstream_error`        | The provider answered with an error. The client got it as is; the request is not charged.                                                                                                                                     |
| `upstream_error_body`   | The provider answered with status 200 but an error in the body. The client got it with status 400 or 502; the request is not charged.                                                                                         |
| `upstream_stream_error` | The stream started with status 200, but the provider sent an error in it before any text, or ended it with nothing at all. The response status can no longer change, so the code shows only here; the request is not charged. |
| `upstream_read_error`   | The provider’s non-streamed answer broke off while we read it. The client got 502; the request is not charged.                                                                                                                |
| `settle_failed`         | The answer was delivered but the charge failed, on our side. The reserve returned; the request is not charged.                                                                                                                |
| `client_closed_request` | The client closed the connection before the answer ended. The row counts as canceled; what the model had generated is charged.                                                                                                |

The stats count as an error only a failure on our side: a 5xx answer or a broken stream. Rows with a 4xx answer show in the log with their status but stay out of the error count.

## Key limits

A key can have:

- requests per minute (`rpm`); past the limit the key answers 429 `rate_limit_exceeded`;
- tokens per minute (`tpm`); these are input tokens by estimate: the request body size in bytes divided by 4. Past the limit it is the same 429; a request that alone exceeds the whole limit is 400 `request_too_large`;
- a monthly spend cap in dollars; when the reserve for a request no longer fits in it, the key answers 429 `monthly_limit_exceeded` until the next month;
- a list of allowed models; the rest get 403 `model_not_allowed` and are hidden from the key in `GET /v1/models`.

Limits are set when the key is created on the [Keys page](https://llm.aicreator.app/en/app/keys/) and can be changed there at any time: the key itself stays the same, and the new values apply from its next request. A request already running finishes under the old limits. A separate key per tool or project is a simple way to see who spends what. More in [Keys and limits](https://llm.aicreator.app/en/docs/keys/).

## Service limits

These apply to every key, whatever its own limits:

- at most 120 requests and 2,000,000 tokens per minute per key; the lower of this ceiling and the key’s own limit applies;
- from one IP address, at most 60 requests per minute, 300 per five minutes and 1,200 per hour to each API endpoint (`/v1/messages`, `/v1/chat/completions` and the others are counted apart), however many keys are in use. For one machine this limit is stricter than the key’s. Past it comes a 429 as plain text, without JSON, with a `retry-after` header;
- at most 4 requests at a time per key; the next one gets 429 `concurrent_requests`;
- a request body of up to 32 MB, or else 413: as plain text when the size is declared in Content-Length, and with the code `request_too_large` when it is not;
- one request lasts 30 minutes at most;
- at most 20 active keys per account; creating one more returns 409, and revoked keys don’t count.
