Every error response from the Overten API follows a consistent JSON shape. The HTTP status code tells you the broad category of the problem, the error field gives you the exact machine-readable reason, and message is a human-readable string that is safe to surface directly to your end users — it never contains stack traces or internal implementation details.
{
"success": false,
"error": "<error_code>",
"message": "<human_readable>"
}
HTTP status codes
The table below maps every status code the API returns to its category and the most common scenario that triggers it.
| Code | Category | Typical scenario |
|---|
200 | Success | Sync result returned |
201 | Resource created | File uploaded, key minted, webhook subscribed |
202 | Accepted (async) | Task queued; poll /tasks/{id} |
204 | No content | Image, key, or webhook deleted |
302 | Redirect | GET /runs/{id}/download |
400 | Bad request | Validation or preflight error |
401 | Unauthorized | Bad or missing API key |
402 | Payment required | Out of credits |
403 | Forbidden | Key valid but not permitted |
404 | Not found | Run, file, or image doesn’t exist or belongs to another org |
408 | Timeout | Sync ceiling exceeded; response body tells you to retry async |
409 | Conflict | Idempotency key collision or invalid resource state |
410 | Gone | Resource existed but was deleted or expired |
413 | Payload too large | Upload exceeds tier limit |
422 | Validation | Field-level rejection on the request body |
429 | Rate limited | See the Retry-After header |
500 | Server error | Unhandled exception on our side — gets paged automatically |
501 | Not implemented | Endpoint exists but the feature isn’t live yet |
502 | Upstream error | A dependency (model provider, storage, or rendering pipeline) failed |
503 | Service unavailable | Capacity exhausted or scheduled maintenance |
Error codes
Authentication
| Code | When it occurs | How to fix it |
|---|
invalid_api_key | Missing, malformed, or revoked sk_live_* key | Check your Authorization header; rotate the key if it may be compromised |
forbidden | Key is valid but lacks permission — suspended org or wrong tier | Check org status via GET /verify |
Billing
| Code | When it occurs | How to fix it |
|---|
insufficient_credits | Org balance is below the minimum required for the requested format | Top up via the dashboard or contact sales |
Validation
| Code | When it occurs | How to fix it |
|---|
invalid_request | Missing required field or bad enum value | Fix the payload according to the error message |
validation_error | Field-level rejection on the request body | The message contains a JSON pointer to the exact failing field |
preflight_failed | LLM validator rejected the request due to missing assets | Attach the missing assets or rephrase the prompt |
payload_too_large | Uploaded file exceeds the tier limit | Compress the file or upgrade your tier |
Resources
| Code | When it occurs | How to fix it |
|---|
not_found | Run, file, image, or webhook ID doesn’t exist or belongs to another org | Check the ID spelling; confirm it was created under the same org |
gone | Resource existed but passed the 30-day retention window | Start a new run |
conflict | Idempotency key reused with a different request body, or resource is in an invalid state | Use a different idempotency key, or wait for the resource to reach a terminal state |
Runtime
| Code | When it occurs | How to fix it |
|---|
sync_timeout_upgrade_async | Sync request hit the 300-second ceiling | Resubmit with async: true and poll /tasks/{id} |
rate_limit_exceeded | Exceeded the per-key, per-minute limit for your tier | Retry after the number of seconds in the Retry-After header |
internal_error | Unhandled server exception | Include the X-Request-Id header value in your support ticket |
upstream_error | LLM provider, storage, or rendering pipeline outage | Retry with exponential backoff |
service_unavailable | Worker pool saturated or scheduled maintenance window | Retry after 30–60 seconds |
not_implemented | Endpoint exists for shape compatibility but the feature isn’t available on your tier or deployment | Contact support@overten.ai |
Handling errors in code
Use the error field to branch your error-handling logic. Treat 5xx responses as transient and retry with exponential backoff. Treat 4xx responses (other than 408 and 429) as permanent — retrying without changing the request will not help.
import time
import requests
resp = requests.post(f"{API}/excel/generate", headers=headers, json=body)
if not resp.ok:
err = resp.json()
code = err.get("error", "unknown")
msg = err.get("message", "")
if code == "preflight_failed":
# Fix the prompt or attach the missing assets before retrying
print(f"Rejected: {msg}")
elif code == "insufficient_credits":
# Surface a billing prompt to the user
redirect_to_billing()
elif code == "rate_limit_exceeded":
# Honor the Retry-After header
retry_after = int(resp.headers.get("Retry-After", "30"))
time.sleep(retry_after)
# then retry the request
elif resp.status_code >= 500:
# Transient — retry with exponential backoff
retry_with_backoff()
else:
# Permanent client error — do not retry without fixing the request
raise RuntimeError(f"{code}: {msg}")
Debugging failed runs
Always capture the run_id and task_id from every response — whether the request succeeded or failed. Include both values in any support ticket you open; they let the Overten team look up the exact ledger and trace rows on our side without asking you for more information.
For async tasks that reach a terminal failed state, the error field on GET /tasks/{task_id} carries the full structured failure:
{
"task_id": "task_...",
"status": "failed",
"error": {
"code": "internal_error",
"message": "AgentLoopError: max_tool_calls=30 exceeded"
},
"credits_used": 0,
"duration_ms": 45123
}
If you see internal_error in the code field, copy the X-Request-Id response header and include it in your support ticket. It maps directly to the server-side trace for that request.
Rate limits
Every response includes rate-limit headers so you can track your remaining budget without waiting for a 429.
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 42
X-RateLimit-Reset: 1713312345
When the API returns 429, it also includes a Retry-After header with the number of seconds to wait before retrying:
Rate limits are enforced per API key, per minute, at the gateway layer — before any agent or pool work starts. Hitting a rate limit does not deduct any credits.
Limits by tier:
| Tier | Requests per minute | Concurrent runs |
|---|
free | 10 | 2 |
pro | 60 | 10 |
enterprise | 600 | 50 |
The X-RateLimit-Reset value is a Unix timestamp in seconds (UTC). Convert it to a sleep duration by subtracting the current time before passing it to your retry logic, or simply use the Retry-After header directly.