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

# Errors and Rate Limits

> The error envelope, what each status code means, and what to retry

Errors use OpenAI's envelope on both endpoints, so error handling written against
the OpenAI SDK works unchanged:

```json
{
  "error": {
    "message": "The rate limit for your plan has been exceeded.",
    "type": "rate_limit_error",
    "code": "rate_limit_exceeded",
    "status": 429
  }
}
```

`type` is one of `invalid_request_error`, `authentication_error`,
`permission_error`, `rate_limit_error` or `server_error`. `code` is the stable
machine-readable value to branch on; `message` is for humans and may change.

A `402` for a blocked account uses its own shape, carrying `message`, `code` and
`data.reason` at the top level rather than inside `error`. Branch on the status
code before reading `error`, so a blocked account surfaces as the billing problem
it is rather than as a parsing failure.

## What to do with each status

| Status | Meaning                                                               | Retry?                   |
| ------ | --------------------------------------------------------------------- | ------------------------ |
| `400`  | The request is malformed, or a parameter is invalid for this endpoint | No. Correct the request. |
| `401`  | The API key is missing or not valid                                   | No.                      |
| `402`  | The account cannot currently be billed                                | No.                      |
| `403`  | The key is valid but not permitted to do this                         | No.                      |
| `404`  | No such response id, or it is no longer stored                        | No.                      |
| `429`  | Your plan's rate limit is exhausted                                   | Yes, with backoff.       |
| `500`  | Something failed on our side                                          | Yes, with backoff.       |
| `502`  | A transient upstream failure                                          | Yes, with backoff.       |

Retry `429`, `500` and `502` with exponential backoff and jitter, and give up
after a small number of attempts rather than retrying indefinitely. A `4xx` other
than `429` will return the same result however many times you send it.

> **Warning**
>
> Creating a response is not idempotent. If a `POST /v1/responses` times out on
> your side, the run may still have started, so retrying can produce a second
> run. Prefer `"background": true` for long work: it returns an id immediately,
> and you poll for the outcome instead of holding a request open.

## Errors worth knowing about

These come from specific combinations rather than from a malformed body, so they
are easy to hit while everything looks correct:

| Condition                                                                                           | Status                                                                    |
| --------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- |
| A top-level field the endpoint does not recognize, including a misspelling like `max_token`         | `400`, `code: "unknown_parameter"`, with the field named in `param`       |
| `"background": true` with `"store": false`                                                          | `400`                                                                     |
| `?stream=true` retrieving a response that was not created with `"background": true`                 | `400`                                                                     |
| A `reasoning.effort` outside `none`, `minimal`, `low`, `medium`, `high`, `xhigh`                    | `400`                                                                     |
| A tool whose `type` is not `function`, on `/v1/responses`                                           | `400`                                                                     |
| An image content part with no URL, base64 that does not decode, or an image over 20 MB once decoded | `400`, `code: "unsupported_content_part"`, with the part named in `param` |
| More than ten images in one request                                                                 | `400`, `code: "unsupported_content_part"`                                 |
| An image URL on a host ASI:One does not fetch from. Send the image inline instead                   | `400`, `code: "unsupported_content_part"`                                 |
| A content part that carries neither text nor an image, such as `input_audio`                        | `400`, `code: "unsupported_content_part"`                                 |
| Chaining `previous_response_id` onto a run that has not finished                                    | `409`                                                                     |
| Retrieving or chaining a response older than the storage window                                     | `404`                                                                     |

The image rules are the same on both endpoints. Only `param` differs, because it
names the field you sent: `messages[i].content[j]` on
[`/v1/chat/completions`](/documentation/build-with-asi-one/chat-completions#image-input),
and `input[i].content[j]` on
[`/v1/responses`](/documentation/build-with-asi-one/responses#image-input).

## Failures inside a stream

A stream can fail after it has started, so a `200` on the initial request is not
the whole story.

On `/v1/chat/completions`, read `finish_reason` on every chunk rather than
assuming the stream ended cleanly, and treat a connection that drops before
`data: [DONE]` as an incomplete reply. See [Chat Completions API](/documentation/build-with-asi-one/chat-completions#streaming).

On `/v1/responses`, a failed run ends with a `response.failed` event and a run
that stopped early ends with `response.incomplete`. Both are terminal, so
receiving either means no more events are coming. See [Responses API](/documentation/build-with-asi-one/responses#streaming).

[Planner mode](/documentation/build-with-asi-one/planner) has its own partial
failures: an agent can be slow, refuse, or not reply. The planner treats those as
normal and works around them, so a run that lost an agent still returns an
answer.

## Rate limits

A `429` means your plan's limit is exhausted rather than that anything is wrong
with the request. Back off and retry.

Limits are scoped to your plan and are visible in your [ASI:One account](https://asi1.ai/account-and-settings/wallet).

## Next steps

1. **[Chat Completions API](/documentation/build-with-asi-one/chat-completions)** - The default endpoint, including streaming
2. **[Responses API](/documentation/build-with-asi-one/responses)** - Retrieve, chain and cancel by id
3. **[OpenAI Compatibility](/documentation/build-with-asi-one/openai-compatibility)** - Which parameters are supported on each endpoint
4. **[API Reference](/api-reference/chat-completions/create-a-chat-completion)** - Every status code each endpoint returns