Errors
Error shapes, status codes, key errors and retry rules.
Every JSON error body carries a code and a message. Branch on the HTTP status and code; the message text may
change.
OpenAI shape
POST /v1/chat/completions, GET /v1/models, POST /v1/audio/speech and POST /v1/audio/transcriptions return
errors in the OpenAI shape. The OpenAI SDKs raise AuthenticationError, PermissionDeniedError, NotFoundError,
RateLimitError and APIStatusError for these errors.
curl -s https://api.albastov.com/v1/models \
-H "Authorization: Bearer abk_not_a_key"{
"error": {
"message": "The API key is unknown or revoked",
"type": "invalid_request_error",
"code": "invalid_api_key"
}
}| Field | Value |
|---|---|
code | Error code |
type | OpenAI error type |
message | Human-readable text |
param | Audio routes: the parameter at fault, or null |
| Other fields | Per code: model, state and retry_after_s on model_loading; job_id on the audio routes when a job exists |
Albastar shape
Every other route, the docs routes included, returns errors in the Albastar shape.
curl -s https://api.albastov.com/v1/media/voices \
-H "Authorization: Bearer abk_not_a_key"{
"ok": false,
"error": "invalid-key",
"code": "invalid_key",
"message": "unknown or revoked api key"
}| Field | Value |
|---|---|
ok | false |
code | Error code |
message | Human-readable text |
error | code with hyphens for underscores (legacy; use code) |
param | The parameter at fault, when one is |
| Other fields | Per code, e.g. resets_at on budget-exhausted |
Codes appear exactly as each route returns them. Codes per route: the route's page under API reference.
Known issue, 2026-11-03: error codes mix snake_case and kebab-case, such as too_large on
POST /v1/chat/completions and too-large on the media routes, or not_found on the docs routes and not-found
on media jobs. Each route keeps its codes as listed on its page; a change to a code appears in the changelog first.
Status codes
| Status | Meaning |
|---|---|
| 400 | Invalid request: a missing, unknown or malformed parameter, or over the model's open requests per account |
| 401 | Missing, unknown or revoked key |
| 403 | Key without the scope the route needs, or account without access to the product |
| 404 | Unknown route, model, voice or job |
| 409 | A voice that is not ready, or an idempotency key used with another request |
| 413 | Request body or file over its limit |
| 415 | A body or file of a type the route does not take |
| 422 | A voice the model needs is missing, a voice the model does not speak, a missing consent, or the account's saved-voice limit |
| 429 | Call, token, open-request or daily limit reached |
| 500 | Internal error |
| 502 | No valid answer from the model, or a speech or transcription job that failed |
| 503 | Model loading, request queued, a job past its queue timeout, or service unavailable for now |
| 524 | Non-streamed request with no response within about 100 s |
Key errors
LLM routes are GET /v1/models and POST /v1/chat/completions; other routes are every other route that needs a key.
| Failure | LLM routes | Other routes |
|---|---|---|
| Unknown or revoked key | 401 invalid_api_key | 401 invalid_key |
| Key without the route's scope | 403 insufficient_scope | 403 scope_missing |
| Account without access to the product | none | 403 account_cannot |
| Key over its calls per minute or per day | 429 rate_limited | 429 rate_limited |
A request without an Authorization header fails with 401 and a code per route family:
| Routes | Code |
|---|---|
GET /v1/models, POST /v1/chat/completions | missing_api_key |
POST /v1/audio/speech, POST /v1/audio/transcriptions, /v1/media/jobs, /v1/media/models, /v1/media/uploads, /v1/media/voices | unauthorized |
POST /v1/events, POST /v1/events/batch, PATCH /v1/events/sources/self | publish_key_required |
GET /v1/events, GET /v1/events/sources, GET /v1/events/stream, POST and DELETE /v1/events/{id}/dismiss | unauthorized |
GET /v1/events/info | events_key_required |
The docs, status and samples routes and GET /v1/events/docs need no key. POST /v1/events/hooks/in/{token} takes
the token in its URL. A 401 on the LLM routes carries WWW-Authenticate: Bearer.
Plain-text errors
Two errors arrive as plain text, not JSON. Check Content-Type before parsing an error body as JSON.
| Status | Body | Cause |
|---|---|---|
| 403 | error code: 1010 | A request with the default Python-urllib User-Agent; send a User-Agent header (API reference) |
| 524 | error code: 524 | Non-streamed request with no response within about 100 s; send it with "stream": true |
Retries
| Status and code | Retry |
|---|---|
400 too_many_parallel, parallel_unsupported | After one of the account's open requests on that model ends |
409 voice_not_ready with status pending | After the voice's status is ready |
| 400, other codes; 401, 403, 404, 409, 413, 415, 422 | No; fix the request or the key |
429 daily_limit, daily_tokens, budget-exhausted | After 00:00 UTC |
429 rate_limited | Within 60 s for the per-minute limit; the per-day limit frees calls over a rolling 24 hours |
| 429, other codes | After Retry-After, or after an open request or job ends |
| 500 | Once; then report the X-Request-Id |
| 502 | After 5 to 10 s |
| 503 | After Retry-After; device_offline carries no Retry-After: after 5 to 10 s |
| 524 | No; stream the request |
Every limit and its code: Rate limits.