Understand the OpenDunes error format, status codes, and how to handle failures gracefully.
When a request fails, the OpenDunes API returns a non-2xx HTTP status with a JSON body. Parse the error code for machine-readable handling and the message for human-readable context.
The body has three fields at the top level — it is not nested under an error object the way some other APIs shape their errors. Do not access .error.code or .error.message; access .error and .message directly.
| Field | Type | Always present | Description |
|---|---|---|---|
error | string | Yes | Machine-readable error code |
message | string | Yes | Human-readable explanation |
retry_after | integer | null | No | Seconds to wait before retrying (present for 429 and 503) |
| HTTP | error | Meaning |
|---|---|---|
| 401 | invalid_api_key | Missing Authorization header, or the key does not exist / has been revoked |
| 402 | insufficient_credits | Balance too low — deposit more DA |
| 403 | account_suspended | The account has been suspended; contact support |
| 403 | model_not_allowed | Your account does not have access to this model |
| 403 | ip_not_allowed | The request IP is outside your key's allowed-IP list |
| 404 | model_not_found | The model slug is not recognized or inactive |
| 404 | no_provider | The model exists but no provider is currently active |
| 404 | not_found | The requested file does not exist, has expired, or belongs to another account |
| 413 | quota_exceeded | Your file storage quota is full — delete files to free space |
| 422 | (validation payload) | The request body is malformed or one or more parameters failed schema validation |
| 422 | guardrail_blocked | A configured guardrail rejected the request |
| 422 | unsupported_file_type | The uploaded file's detected type is not one of the supported formats |
| 422 | invalid_file_reference | A referenced file_id is unknown or expired, or the request exceeds the per-request reference limits |
| 429 | rate_limit_exceeded | Your per-key request rate was exceeded — see retry_after |
| 429 | rate_limited | An upstream provider rate limit was hit — see retry_after |
| 429 | too_many_uploads | Concurrent uploads collided — retry shortly (this response carries no retry_after field) |
| 502 | storage_error | The file could not be stored — retry the upload |
| 502 | upstream_error | The upstream provider returned an error |
| 502 | upstream_timeout | The upstream model did not respond in time |
| 503 | temporary_unavailable | Service temporarily unavailable — see retry_after |
For 429 and 503 responses, both the retry_after field in the JSON body and the Retry-After HTTP header are set to the number of seconds you should wait before retrying. Obey whichever your HTTP client exposes.
A 429 rate_limit_exceeded means you exceeded your per-key request rate. Back off by retry_after seconds, then retry with exponential jitter. See Rate Limits for the full header set and default thresholds.
A 402 insufficient_credits means your DA balance is zero or below the cost of the requested completion. Top up via your dashboard using a CIB or Edahabia card through SATIM. Once your balance is restored, retry the exact same request — no changes needed.
When an error occurs mid-stream (after tokens have already been sent), the connection stays open and the error arrives as a data event:
The HTTP status is still 200 because it was already committed. Check for an error field on the final chunk before [DONE]. Billing settles on the tokens received before the error.
Every response — success or error — carries an X-Request-Id header with a UUID-v7 trace identifier:
Include this value when contacting support. It ties the request to server-side logs and gateway traces, making diagnosis dramatically faster.