Errors & rate limits
Error shape
Every non-2xx response is JSON of the form:
{
"error": {
"code": "insufficient_scope",
"message": "This endpoint requires the `generation:run` scope.",
"required_scope": "generation:run",
"granted_scopes": ["books:read"]
}
}Common codes
| HTTP | error.code | Meaning |
|---|---|---|
| 400 | invalid_request | The body or query failed schema validation. |
| 400 | invalid_state | The book/chapter exists but isn’t in a state that supports the action (e.g. publishing a book with empty chapters). |
| 401 | unauthenticated | Missing or malformed Authorization header. |
| 401 | invalid_token | Token is unknown, expired, or revoked. |
| 403 | insufficient_scope | The token is valid but missing the scope the endpoint requires — mint a new key with the listed scope. |
| 404 | not_found | The book or chapter doesn’t exist (or isn’t owned by you). |
| 502 | generation_failed, narration_failed | The downstream provider (Anthropic, ElevenLabs) failed. Safe to retry. |
| 503 | feature_disabled | The Anthra instance doesn’t have the required upstream key configured (e.g. ELEVENLABS_API_KEY for narration). |
Rate limits
Anthra applies per-account quotas to expensive operations (chapter generation, narration). When you hit a limit, you’ll receive an HTTP 429 with retry-after set to the number of seconds to back off. The 429 body uses the same error shape.
Generation jobs are also bounded by maxDuration = 300s on Vercel Functions — chapter writes that legitimately need longer should be re-issued (the partial content is already saved before the timeout).
Idempotency
Mutating endpoints are not idempotent today. Use a unique key per request (e.g. POST /v1/books always creates a new book). If you need at-most-once semantics, deduplicate on the client before retrying.