# Rate limits and errors

## Rate limits

Limits apply per organization and per endpoint, measured in requests per minute.

| Plan | Requests per minute per endpoint |
|  --- | --- |
| Free | 10 |
| Pay-as-you-go | 60 |
| Enterprise | 1000, customizable per organization |


Above the limit the API returns `429 Too Many Requests`. Status endpoint calls count toward the limit, so poll at a sensible interval or use [webhooks](/getting-started/async-requests#webhooks). If you need more throughput, [contact us](https://bria.ai/contact-us) about an Enterprise plan.

### Handling 429

Retry with exponential backoff and jitter, honor `Retry-After` when present, and cap concurrency on your side so a burst does not turn into a wave of retries.

Python SDK
The SDK retries `429` and `5xx` responses automatically (3 attempts, exponential backoff). Tune it per client:

```python
from bria_client import BriaSyncClient
from httpx_retries import Retry

client = BriaSyncClient(retry=Retry(total=5, backoff_factor=2))
```

Keep your own concurrency bounded, for example with a thread pool of a few workers, so parallel jobs stay under the per-endpoint limit.

JavaScript / TypeScript
```javascript
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

async function postWithBackoff(url, body, attempts = 6) {
  let delay = 1000;
  for (let attempt = 0; attempt < attempts; attempt++) {
    const response = await fetch(url, {
      method: "POST",
      headers: { api_token: process.env.BRIA_API_TOKEN ?? "", "Content-Type": "application/json" },
      body: JSON.stringify(body),
    });
    if (response.status !== 429 && response.status < 500) return response;
    const retryAfter = Number(response.headers.get("Retry-After"));
    await sleep(retryAfter ? retryAfter * 1000 : delay + Math.random() * 500);
    delay = Math.min(delay * 2, 30000);
  }
  throw new Error("Bria API: too many retries");
}
```

## Error contract

Synchronous errors return a JSON body with an `error` object and the `request_id` to quote when contacting support:

```json
{
  "request_id": "9d4a1a2f5c7e4b6f8a0c2d3e4f5a6b7c",
  "error": {
    "code": 422,
    "message": "Unprocessable Entity",
    "details": "Prompt failed content moderation."
  }
}
```

Asynchronous jobs report failures through the [status endpoint](/status): `status` is `ERROR` and the same `error` object is included.

| HTTP status | When it happens |
|  --- | --- |
| `400 Bad Request` | Malformed JSON, an unknown parameter, an invalid value or an unsupported parameter combination. |
| `401 Unauthorized` | Missing or invalid `api_token`. See [Authentication](/getting-started/authentication). |
| `403 Forbidden` | The endpoint or option is not included in your plan. |
| `404 Not Found` | Unknown route, or an unknown or expired `request_id` on the status endpoint. |
| `413 Payload Too Large` | The image or request exceeds the endpoint's size limit. |
| `415 Unsupported Media Type` | The input file type or color mode is not supported. Use JPEG, PNG or WEBP. |
| `422 Unprocessable Entity` | The request is well formed but cannot be processed: [content moderation](/safety) failed, no object was detected for a prompt, or an async-only option was combined with `sync: true`. |
| `429 Too Many Requests` | Rate limit exceeded. Back off and retry. |
| `5xx` | A problem on Bria's side. Retry with backoff; check [status.bria.ai](https://status.bria.ai) if it persists. |


Each endpoint page lists its specific error responses and messages.