Errors
One error body for every failure, a code to branch on, safe retries with an Idempotency-Key, and the rate-limit headers.
The error body
Every status other than 200 returns the same JSON shape. Branch on error.b11_code: each code has exactly one HTTP status and one error.type. error.message is for people and may change. request_id is the same value as the request-id header.
{
"type": "error",
"error": {
"type": "invalid_request_error",
"message": "\"tools\" is not a field of this request.",
"b11_code": "invalid_field"
},
"request_id": "req_01M3VNBWG00W3GE1R70W3GE1R7"
}A refused call, and a call that fails before any output, is never charged. A streamed call that fails after it began sends its error as the stream’s error event instead; see streaming.
Every code
| HTTP | error.type | b11_code | Meaning |
|---|---|---|---|
| 400 | invalid_request_error | invalid_field | A field is missing, unknown or out of range. The message names it. |
| 400 | invalid_request_error | max_tokens_exceeded | max_tokens is above the model’s max_output_tokens. |
| 400 | invalid_request_error | input_too_large | Input tokens plus max_tokens exceed the model’s context_tokens. |
| 401 | authentication_error | missing_key | No key in x-api-key or Authorization. |
| 401 | authentication_error | invalid_key | The key is not one this API knows. |
| 401 | authentication_error | key_revoked | The key was revoked on the keys page. |
| 402 | billing_error | credits_exhausted | A trial’s remaining credits do not cover this call’s worst case. |
| 402 | billing_error | spend_cap_reached | This call could take a pay-as-you-go workspace past its monthly spend cap. |
| 402 | billing_error | key_budget_exhausted | Reserved for per-key budgets, which are not offered yet. |
| 403 | permission_error | workspace_suspended | The workspace is suspended. Check the app. |
| 403 | permission_error | trial_expired | The trial has ended. Add a card or talk to sales. |
| 403 | permission_error | workspace_not_active | The workspace is not active yet (a quote or sales request is pending). |
| 404 | not_found_error | unknown_model | The model is not one of this API’s. GET /models lists them. |
| 409 | invalid_request_error | idempotency_replayed | This Idempotency-Key was used by a request that has finished. |
| 409 | invalid_request_error | idempotency_in_progress | This Idempotency-Key belongs to a request that is still running. |
| 413 | request_too_large | body_too_large | The body is over 1 MiB. |
| 429 | rate_limit_error | rate_limited | Over the tier’s requests a minute. Wait retry-after seconds. |
| 429 | rate_limit_error | concurrency_limited | Over the tier’s concurrent calls. Wait retry-after seconds. |
| 500 | api_error | internal | The API failed. Nothing was charged. |
| 502 | api_error | backend_error | The model failed before any output. Nothing was charged. |
| 503 | unavailable_error | paused | The service is paused; the status page says why. Wait retry-after seconds. |
Idempotency-Key
Send an Idempotency-Key header, any string of 1 to 255 characters, and a retry with the same key never calls the model twice. Keys are scoped to the workspace and kept for 24 hours.
curl https://api.baseeleven.dev/v1/messages \
-H "x-api-key: $B11_API_KEY" \
-H "content-type: application/json" \
-H "Idempotency-Key: order-1042-first-reply" \
-d '{
"model": "eleven-lite",
"max_tokens": 256,
"messages": [
{
"role": "user",
"content": "Which of your models should I start with?"
},
{
"role": "assistant",
"content": "Start with the smaller model and move up when you need a longer context."
},
{
"role": "user",
"content": "How many tokens of context does the larger one take?"
}
]
}'- While the first request runs, a second one with the key gets 409
idempotency_in_progress. - Once the first request has finished, the same key gets 409
idempotency_replayedfor 24 hours, withoriginal.request_idand the tokens it used. The reply itself is never stored, so it is not returned. - A key is kept once the call has run, even if the call ended in a 502 or the connection dropped. A refused call (a 4xx before the model was called) frees its key, so you can fix the request and send it again with the same key.
{
"type": "error",
"error": {
"type": "invalid_request_error",
"message": "This Idempotency-Key was used by a request that has finished.",
"b11_code": "idempotency_replayed"
},
"request_id": "req_01M3VNBWG00W3GE1R70W3GE1R8",
"original": {
"request_id": "req_01M3VNBWG00W3GE1R70W3GE1R7",
"usage": {
"input_tokens": 3,
"output_tokens": 16
}
}
}Rate-limit headers and 429
Every response but a 401 and a 503 paused carries the workspace’s request budget in three headers. The limits themselves are the workspace’s tier.
| Header | Value |
|---|---|
| request-id | This request’s id, req_ and a ULID. |
| b11-ratelimit-requests-limit | Requests a minute the tier allows. |
| b11-ratelimit-requests-remaining | Requests you can make now before a 429. |
| b11-ratelimit-requests-reset | When the budget is full again, UTC with milliseconds. |
| retry-after | On every 429 and 503: whole seconds to wait. |
Over the limit, the call is refused with 429 rate_limited (requests a minute) or concurrency_limited (calls at once). Wait retry-after seconds and send it again. Refusals are not charged.
HTTP/1.1 429 Too Many Requests
b11-ratelimit-requests-limit: 20
b11-ratelimit-requests-remaining: 0
b11-ratelimit-requests-reset: 2026-10-01T12:01:00.000Z
request-id: req_01M3VNBWG00W3GE1R70W3GE1RV
retry-after: 3
content-type: application/json
{
"type": "error",
"error": {
"type": "rate_limit_error",
"message": "The workspace is over its tier's requests per minute.",
"b11_code": "rate_limited"
},
"request_id": "req_01M3VNBWG00W3GE1R70W3GE1RV"
}Next: Tiers