Errors & rate limits
HTTP status codes the Specala AI API returns (401, 403, 422, 429, 5xx), the JSON error shape, what triggers each one, and how to handle rate limits.
The API uses standard HTTP status codes. 2xx means success; 4xx means something about the
request needs fixing.
Status codes
| Status | Meaning | Typical cause |
|---|---|---|
200 | OK | Request succeeded. |
201 | Created | Key created (management). |
204 | No Content | Key revoked (management). |
401 | Unauthorized | Missing/invalid/expired key, or key passed in the URL. |
402 | Payment Required | No plan minutes left (uploads). |
403 | Forbidden | Plan without API access (api_access_denied), or key without upload permission (upload_scope_required). |
404 | Not Found | Resource doesn't exist in your workspace. |
408 | Request Timeout | Upload stalled or took too long. |
409 | Conflict | Same Idempotency-Key still in flight. |
411 | Length Required | Upload without Content-Length. |
413 | Payload Too Large | File over the 2 GB upload limit. |
422 | Unprocessable Entity | Invalid parameter (e.g. bad format, too many ids). |
429 | Too Many Requests | Rate limit or an upload cap exceeded. |
5xx | Server error | Transient — retry with backoff. |
Error shape
Most errors return a JSON body with a detail field. Plan/access errors include a machine-readable
code:
{
"detail": {
"code": "api_access_denied",
"message": "Your current plan does not include API access. Please upgrade to a plan with API & MCP support."
}
}{
"detail": "Invalid API key"
}Common cases
| What you see | Why | Fix |
|---|---|---|
401 on every call | Key revoked, expired, or has a trailing space | Create a fresh key, copy carefully |
401 with a key in the query string | Keys in URLs are rejected by design | Move the key to the Authorization header |
403 api_access_denied | Plan without API & MCP | Upgrade to Pro or Expert |
404 on a known ID | The transcription is in another workspace | Use a key for that workspace |
422 on export | format isn't md/txt, or ids is empty/over 100 | Fix the parameter |
Upload & delete errors
Upload a file and
Delete transcription return machine-readable codes in
detail.code. Branch your code on the code, not the message text:
| Code | Status | Meaning | What to do |
|---|---|---|---|
unsupported_file_format | 400 | Extension not supported | Check the format list |
file_too_small | 400 | Under 1 KB | Not a real recording |
filename_required | 400 | No Content-Disposition header | Add the header |
insufficient_balance | 402 | No minutes left | Top up, upload again |
upload_scope_required | 403 | Key has no upload permission | Create a key with Allow file uploads |
delete_scope_required | 403 | Key has no delete permission | Create a key with Allow deleting transcriptions |
upload_timeout | 408 | Stream stalled / over 90 min | Check the connection, retry |
idempotency_conflict | 409 | Same Idempotency-Key still uploading | Wait Retry-After, retry |
length_required | 411 | No Content-Length | Use a client that sets it |
file_size_limit_exceeded | 413 | Over 2 GB | Split or compress the file |
too_many_concurrent_uploads | 429 | Parallel-upload cap | Wait Retry-After, retry |
too_many_queued_files | 429 | 100+ files awaiting processing | Queue is draining — wait, retry |
upload_quota_exceeded | 429 | Daily anti-abuse cap | Retry after UTC midnight or contact support |
export_quota_exceeded | 429 | Daily export volume cap | Retry after UTC midnight or contact support |
uploads_temporarily_disabled | 503 | Uploads switched off | Temporary — retry later |
Transient (408, 429, 5xx) → retry with backoff. Everything else → fix the request; retrying
won't help. Never re-send a file after a 201 — that creates a second, billed transcription.
Rate limits
Limits are per key:
| Surface | Limit |
|---|---|
| Data endpoints (list / get / export / bulk) | 500 requests / minute |
| Export volume | 10 GB / day |
| File uploads | 60 requests / minute, 10 concurrent per key |
| Deletions | 30 requests / minute |
| Key management | 180 requests / minute |
When you exceed a limit you get 429. Handle it gracefully:
- Back off, don't retry immediately. Wait a few seconds and try again.
- Avoid tight loops. To export many transcriptions, use Bulk export (one request) instead of looping over single exports.
- Spread work out rather than firing hundreds of calls at once.
Don't auto-retry on 429 instantly
Immediate automatic retries make the limit worse. Use exponential backoff (e.g. 1s, 2s, 4s).