> Section "Errors" of the AthanLab Developer API documentation (API version 1.0.0). On the web: https://athanlab.com/docs#errors. Whole documentation as Markdown: https://athanlab.com/docs.md # Errors Every error the API sends has the same JSON shape and an `X-Request-Id` header. Handle errors by four rules: 1. Branch on `error.code`, never on the message. 2. Retry only when `error.retryable` is `true`. 3. If the response has a `Retry-After` header, wait that many seconds before you retry. 4. If `error.retryable` is `false`, change the request before you send it again. Error response: ```json { "error": { "type": "rate_limit_error", "code": "concurrency_limit", "message": "This account already has the maximum number of jobs running. Retry after one finishes (see Retry-After).", "param": null, "retryable": true, "request_id": "req_4f1c2a9e0b7d6c5a3e8f1b2c", "doc_url": "https://athanlab.com/docs#errors-concurrency_limit", "details": { "active_jobs": 2, "max_concurrent_jobs": 2 } } } ``` > [!NOTE] > **A 503 can arrive as an HTML 504** > > The API runs on DigitalOcean App Platform, which replaces every 503 response with its own HTML 504 page. Only `Retry-After`, `X-Request-Id` and an `x-do-orig-status` of 503 survive. Treat such a 504 as retryable: nothing was charged, so wait the `Retry-After` and retry. ## Error fields - `error.type` (`"authentication_error" | "permission_error" | "billing_error" | "rate_limit_error" | "service_unavailable" | "invalid_request_error" | "api_error"`): Broad category of the error. - `error.code` (`string`): Machine-readable error code. New codes may be added; handle an unknown one by its HTTP status and `retryable`. - `error.message` (`string`): A human-readable explanation. Do not parse it. - `error.param` (`string | null`): The request field or header at fault, when there is one. - `error.retryable` (`boolean`): True when the same request may succeed later without changes (honor Retry-After). - `error.request_id` (`string`): Also sent as the X-Request-Id header. Quote it when you contact support. Constraints: pattern `^req_[0-9a-f]{24}$` - `error.doc_url` (`string`): This error's entry in the documentation. - `error.details` (`object`, may be absent): Structured context some errors carry, such as `notice` (a maintenance message), `replacement` (a removed endpoint's successor), `required_scope`, `job_id` (idempotency_conflict), `active_jobs` / `max_concurrent_jobs` (concurrency_limit), `required` / `spendable` (insufficient_characters) or `required` / `monthly_char_budget` / `used_this_month` / `resets_at` (key_budget_exceeded). On a delivery: `delivery_id` (idempotency_conflict) and `active_deliveries` / `max_active_deliveries` (concurrency_limit). New keys may be added. - `error.upgrade_url` (`string`, may be absent): Where to fix a billing error. ## Request IDs Every response carries an `X-Request-Id` header, and an error repeats it as `error.request_id`. Quote it when you contact [support](https://athanlab.com/docs#support). ## Error codes The codes are grouped by `error.type`. For a code not listed here, see [Versioning](https://athanlab.com/docs#versioning). **Authentication** (`authentication_error`) | Code | HTTP | Retryable | Meaning | | --- | --- | --- | --- | | missing_api_key | 401 | No | No API key provided. Send it in the X-API-Key header or as Authorization: Bearer \. | | invalid_api_key | 401 | No | The API key is invalid, revoked or expired. | **Access** (`permission_error`) | Code | HTTP | Retryable | Meaning | | --- | --- | --- | --- | | developer_pending | 403 | No | Your developer application is still under review. Keys start working once it is approved. | | developer_rejected | 403 | No | Your developer application was not approved. See the Developer API page in your dashboard. | | developer_suspended | 403 | No | Developer API access for this account is suspended. Contact support@athanlab.com. | | developer_not_approved | 403 | No | This account has no approved developer application. Apply from the Developer API page in your dashboard. | | account_suspended | 403 | No | This account is suspended or scheduled for deletion. Contact support@athanlab.com. | | scope_missing | 403 | No | This API key does not have the scope this endpoint requires. | **Billing** (`billing_error`) | Code | HTTP | Retryable | Meaning | | --- | --- | --- | --- | | plan_required | 402 | No | Creating jobs through the API requires an active Max plan. Existing jobs, downloads and usage stay readable. | | insufficient_characters | 402 | No | The account does not have enough characters for this job. Nothing was charged. Top up Athan Tokens or wait for the monthly allowance to reset. | **Rate limits** (`rate_limit_error`) | Code | HTTP | Retryable | Meaning | | --- | --- | --- | --- | | rate_limited | 429 | Yes | Too many requests. Retry after the number of seconds in the Retry-After header. | | auth_blocked | 429 | Yes | Too many failed authentication attempts from this network. Retry after the number of seconds in the Retry-After header. | | concurrency_limit | 429 | Yes | This account already has the maximum number of jobs running. Retry after one finishes (see Retry-After). | | key_budget_exceeded | 429 | No | This API key has reached its monthly character budget. Nothing was charged. The owner can raise the budget from the Developer API page. | **Availability** (`service_unavailable`) | Code | HTTP | Retryable | Meaning | | --- | --- | --- | --- | | api_disabled | 503 | Yes | The AthanLab API is currently unavailable. | | api_read_only | 503 | Yes | The AthanLab API is in read-only mode: new jobs are paused, while existing jobs, downloads and usage stay available. | | capacity_unavailable | 503 | Yes | The speech service is at capacity. Nothing was charged. Retry after the number of seconds in the Retry-After header. | | server_restarting | 503 | Yes | The server is restarting. Nothing was charged. Retry in a few seconds. | | server_busy | 503 | Yes | Too many downloads are in progress. Retry after the number of seconds in the Retry-After header. | **Request** (`invalid_request_error`) | Code | HTTP | Retryable | Meaning | | --- | --- | --- | --- | | validation_error | 400 | No | The request is invalid. | | invalid_cursor | 400 | No | The pagination cursor is invalid. Use the next_cursor value from a previous list response, unchanged. | | not_found | 404 | No | No such endpoint or resource. | | voice_not_found | 404 | No | No voice with this id is available to this account. | | idempotency_conflict | 409 | No | This Idempotency-Key was already used with a different request. Use a new key for a new request. | | job_not_finished | 409 | Yes | The job has not finished yet. Poll GET /speech/{id} until its status is "succeeded". | | job_failed | 409 | No | The job failed or was cancelled, so it has no audio. See the job's error for details. | | job_finished | 409 | No | The job has already finished and can no longer be cancelled. | | endpoint_removed | 410 | No | This endpoint has been removed. | | audio_expired | 410 | No | The audio for this job is no longer available: it is deleted after the retention period. | | payload_too_large | 413 | No | The request body is too large. | | unsupported_media_type | 415 | No | Request bodies must be sent as Content-Type: application/json. | | unspeakable_text | 422 | No | The text has no speakable content. Send words to read aloud, not only emoji, punctuation or whitespace. | | text_too_long | 422 | No | The text is too long once numbers are written out as words. Send a shorter text or a different number_mode. | **Server** (`api_error`) | Code | HTTP | Retryable | Meaning | | --- | --- | --- | --- | | internal_error | 500 | Yes | Something went wrong on our side. Retry the request; if it keeps failing, contact support@athanlab.com with the request_id. |