Errors
Every error response follows the same envelope shape — type, message, and a request id you can include in support tickets. No upstream branding leaks through; messages are sanitized.
Envelope shape
response · 400
{
"error": {
"type": "invalid_request",
"message": "Field 'prompt' is required.",
"request_id": "01JX..."
}
}request_id is also returned as the X-Request-ID response header. Quote it when filing a support ticket — we use it to look up the upstream call in our logs.
parameter
type
required
description
- parameter
error.typetypestringrequiredrequiredMachine-readable error code. See the table below. - parameter
error.messagetypestringrequiredrequiredHuman-readable message. Safe to surface to end-users. - parameter
error.request_idtypestringrequiredoptionalULID. Quote in support tickets and bug reports.
Common error types
parameter
type
required
description
- parameter
invalid_requesttype400requiredoptionalThe request body or query is malformed (missing required field, bad enum value, etc). - parameter
unsupported_media_typetype415requiredoptionalThe multipart upload used a MIME type we don't accept. See per-endpoint docs for allowed types. - parameter
payload_too_largetype413requiredoptionalThe uploaded file exceeded the cap. 10MB for images, 25MB for transcription audio. - parameter
insufficient_balancetype402requiredoptionalYour wallet doesn't have enough credit for the request. Top up at /billing/topup. - parameter
rate_limit_exceededtype429requiredoptionalEither your API key hit its usage limit (set in /keys), or the upstream rate-limited us. Back off and retry. - parameter
asset_expiredtype410requiredoptionalA signed asset URL (/v1/assets/<token>) older than 24h was fetched. Re-request the asset from the original endpoint. - parameter
asset_invalidtype401requiredoptionalA signed asset URL was malformed or had a bad signature. - parameter
upstream_errortype502requiredoptionalThe upstream model provider returned an error we couldn't recover from. Safe to retry once; if it persists, file a ticket with the request_id.
Handling errors in code
import requests
resp = requests.post(
"https://api.echotokens.me/v1/images/generations",
headers={"Authorization": "Bearer sk-echo-..."},
json={"model": "nano-banana-2", "prompt": "..."},
)
if not resp.ok:
err = resp.json().get("error", {})
print(f"{err.get('type')}: {err.get('message')}")
print(f"request_id: {err.get('request_id')}")
raise SystemExit(1)when in doubt, include the request_id
Support requests with a request_id are resolved roughly 10× faster than ones without. The id lets us find the exact request in our logs without playing detective.
What our errors hide
When the upstream rejects a request, we deliberately drop:
- The exact upstream provider's identity (you only see
upstream_error, not e.g.kling internal error 42). - Stack traces and internal paths.
- Headers from the upstream that could leak vendor info.
This keeps your error UX consistent regardless of which provider you're routed through. If you need the upstream's raw failure context for debugging, file a ticket with the request_id — we have it in our logs.