---
title: Errors
description: Clipia API's unified error format, the table of HTTP statuses and errorCodes, and which ones are safe to retry.
---

Every error is returned in a single JSON shape with `type`, `code`, and a human-readable `message`. The HTTP status sets the error class, while the machine-readable `code` gives the exact reason. Only transient errors (`429`, `503`) are safe to retry with backoff; request errors (`4xx`) will return the same result unless you change the request.

<Callout type="info" title="One unified format">
Every error response shares the same shape: an `error` object with `type`, `code`, and `message`. Branch your logic on the machine-readable `code`, and use the HTTP status as the error class.
</Callout>

```json
{
  "error": {
    "type": "invalid_request_error",
    "code": "insufficient_credits",
    "message": "Not enough credits for this generation. Please top up your balance."
  }
}
```

## Error codes

| HTTP | `code` | When | Retry-able |
|------|--------|------|------------|
| `400` | `invalid_request` | Invalid body or parameters | No |
| `401` | `invalid_api_key` | Key missing, invalid, or revoked | No |
| `402` | `insufficient_credits` | Not enough credits on the balance | No — top up first |
| `403` | `insufficient_scope` | The key lacks the required scope | No |
| `404` | `not_found` | Unknown `request_id` or `model` | No |
| `409` | `idempotency_key_reuse` | Same `Idempotency-Key` with different parameters | No — use a new key |
| `409` | `request_in_progress` | Retry while the first request is still processing | Yes — after the first finishes |
| `422` | `model_input_invalid` | Parameters don't fit the model | No — fix the `input` |
| `429` | `rate_limit_exceeded` | Rate limit exceeded | Yes — after `Retry-After` |
| `500` | `internal_error` | Internal error | Yes — with backoff |
| `503` | `service_unavailable` | Temporarily unavailable | Yes — with backoff |

<Callout type="info" title="What to retry">
It is safe to retry `429`, `500`, and `503` with exponential backoff. For `4xx` request errors (except `request_in_progress`), retrying unchanged returns the same error — fix the request first.
</Callout>

<Callout type="warn" title="Messages are sanitised">
The `message` field never exposes internal infrastructure details. Branch on the machine-readable `code`, not on the `message` text, which may change.
</Callout>
