Errors, Rate Limits & Quotas
Every API has failure modes. This page covers how the AIOZ AI API tells you something went wrong, which errors are worth retrying and which aren't, and the rate limits and quotas you'll bump into at scale.
The error response shape
When a request fails, the API returns a non-2xx HTTP status code and a JSON body of the shape:
{
"status": "fail",
"message": "human-readable error description"
}The status field is "fail" and the message field is intended to be safe to surface to your own users or to log. The HTTP status code carries the category of failure; the message carries the specifics.
HTTP status code reference
The codes you'll commonly see, with their general meaning across endpoints. Endpoint-specific failures use the same codes; the message field tells you what specifically went wrong.
| Status | Category | When you'll see it |
|---|---|---|
200 OK | Success | The request succeeded; the response body's data field has your payload. |
400 Bad Request | Malformed request | The request itself is malformed, e.g. invalid JSON the server can't parse. Check message, fix the request before retrying. |
401 Unauthorized | Authentication | Your x-api-key header is missing, malformed, or no longer valid (e.g. the key was revoked). |
403 Forbidden | Authorization | The authenticated identity doesn't have permission to perform the action. |
404 Not Found | Missing resource | The requested resource doesn't exist or isn't accessible to this key. |
409 Conflict | State conflict | The request can't be applied because of the resource's current state. |
422 Unprocessable Entity | Validation | A body or query field is missing, has the wrong type, or fails an allowed-values (enum) check — e.g. an unknown sort or type value, a missing required field on a presigned-upload request, or size ≤ 0. The message names the offending field. Fix the request before retrying. |
429 Too Many Requests | Rate limit | You've exceeded a rate limit. Wait before retrying. See Rate limits below. |
500 Internal Server Error | Server-side | Something went wrong on our side. The request itself was well-formed; retrying after a short delay will often succeed. |
Rate limits
The API applies rate limits per API key to protect the platform from accidental overload and to keep latency predictable across all callers. When you exceed a limit, requests are rejected with a 429 Too Many Requests until the limit window resets.
Limits: the rate depends on the kind of endpoint you're calling, applied per API key:
- Public endpoints: 5 requests per second.
- Auth endpoints: 15 requests per second.
Response headers. Rate-limited responses, and successful responses on rate-limited endpoints, include the following headers so you can track your status without guessing:
| Header | Meaning |
|---|---|
RateLimit-Limit | The maximum number of requests allowed in the current window. |
RateLimit-Remaining | The number of requests left in the current window. |
RateLimit-Reset | Seconds until the current window resets and RateLimit-Remaining refills. |
X-Rate-Limit-Limit | The same limit, expressed as a decimal (e.g. 5.00). |
X-Rate-Limit-Duration | The length of the rate-limit window, in seconds. |
Design your client to handle 429 gracefully: back off and retry after the window resets (wait RateLimit-Reset seconds), and don't burst all your requests at once.
Quotas
In addition to per-window rate limits, your account has a balance-based quota: you can only run tasks until you've used your available credits. See How Billing Works for how credits are consumed and how to check your balance. There is no separate API call that "reserves" credits; creating a task that would push you past your balance fails at the time of creation.
Retry guidance
Some errors are worth retrying and some aren't. As a rule:
400,401,403,404,409,422: don't retry. These are deterministic failures; the same request will keep failing until you fix it.429: retry, but only after a delay. WaitRateLimit-Resetseconds; otherwise back off exponentially.500,502,503,504: retry with exponential backoff, capped at a small number of attempts. These are usually transient.
A reasonable pattern for retryable errors:
attempt = 0
max_attempts = 5
while attempt < max_attempts:
response = call()
if response is successful:
return response
if response status is non-retryable:
raise
wait = min(60, 2 ** attempt) seconds, with a little jitter
sleep(wait)
attempt += 1
raise last errorThe SDKs handle automatic retries for transient transport-level failures (connection resets, brief network blips) at the HTTP layer. Application-level retry decisions (whether to retry a 429, a 500, or a timeout) are left to your code, since the right strategy depends on what the request was doing.