Error Codes
API error responses and solutions
The Onysoft API returns standard HTTP status codes and detailed error messages.
HTTP Status Codes
| Code | Meaning | Description |
|---|---|---|
200 | OK | Request completed successfully |
202 | Accepted | Asynchronous task created (video/image/music) |
400 | Bad Request | Invalid request format or missing parameter |
401 | Unauthorized | Invalid or missing API key |
402 | Payment Required | Insufficient balance: the balance does not cover the estimated maximum cost of the request (see Balance pre-check) |
403 | Forbidden | Project suspended, model blocked or access denied |
404 | Not Found | Endpoint or record not found (wrong path, unknown model detail, usage record or task id) |
413 | Payload Too Large | Request size too large (max 10MB — for base64 images) |
422 | Unprocessable Entity | Missing or invalid parameters (validation_error — e.g. model/messages missing) |
429 | Too Many Requests | Rate limit or token limit exceeded |
500 | Internal Server Error | Server error |
502 | Bad Gateway | Invalid response from the upstream provider or from your device (upstream_error, node_error); retry shortly |
503 | Service Unavailable | Service temporarily unavailable: the model is busy (service_unavailable) or no device is ready (node_unavailable, node_busy). Follow the Retry-After and X-Should-Retry headers |
Error Response Format
{
"success": false,
"error": {
"code": "insufficient_balance",
"message": "Insufficient balance. Add funds at https://api.onysoft.com/dashboard/billing",
"type": "insufficient_quota",
"param": null
}
}
The error body is compatible with the OpenAI error format: SDKs read error.message, error.code, error.type and error.param. type follows the HTTP status: 401 authentication_error, 402 insufficient_quota, 403 permission_error, 404 not_found_error, 429 rate_limit_error, 5xx server_error, otherwise invalid_request_error. param names the field the error refers to (for validation errors the first invalid field; all of them are listed in fields); it is null or empty when no specific field applies. For backward compatibility "success": false and endpoint-specific extra fields (e.g. retry_after, fields, required) are kept. This format applies to /v1/chat/completions and the other endpoints; the formats of /v1/responses and the Anthropic endpoints are in the table below.
Error Format by Endpoint
| Endpoint | Error body |
|---|---|
/v1/chat/completions, other endpoints | The format above: {"success": false, "error": {"code", "message", "type", "param"}} |
/v1/responses | OpenAI format: {"error": {"message", "type", "param", "code"}}; no success field |
/v1/messages, /v1/messages/count_tokens, /v1/models with the anthropic-version header | Anthropic format: {"type": "error", "error": {"type", "message"}}; types and mapping in the Anthropic Messages API section |
Balance Pre-check (402)
402 is not returned only when the balance reaches zero. Before every paid request an estimated maximum cost is computed: input estimate + output reservation; the output reservation is max_tokens (or max_completion_tokens / max_output_tokens on Responses) if sent, otherwise the model's maximum output tokens. A 20% safety buffer is added; if your balance is below that, the request returns 402 insufficient_balance without ever reaching the model. The required and available fields of the error body show the required and the available amount. You are always charged for actual usage; the pre-check is only a sufficiency check. Free models are not pre-checked. The threshold depends on the model price and the current exchange rate.
For coding assistants this threshold can be high: Codex and Cursor send no maximum output limit and Claude Code asks for 64,000–128,000 output tokens. If you get 402 although your balance looks sufficient, lower max_tokens / max_output_tokens; in Claude Code use CLAUDE_CODE_MAX_OUTPUT_TOKENS.
Error Message Language
The message field of an error body is produced in English or Turkish; code, type, param, the HTTP status and the extra fields (retry_after, fields …) are language-independent and never change. Order of precedence: 1) the ?lang=tr / ?lang=en query parameter. 2) the Accept-Language header — when sent, it always wins (Turkish if the highest-priority language starts with tr, otherwise English). 3) the language setting of your API key: Dashboard → API Keys → the settings button next to the key → “API error language”. 4) English when none of the above applies. Every key that already exists starts with tr (so nothing changes for current integrations); keys created from now on start with en. Exception: errors raised before authentication are always in English, because the key has not been resolved yet — 401 invalid_auth, empty_api_key, invalid_api_key_format, invalid_api_key and 429 too_many_attempts. For those responses you can still send Accept-Language: tr to get Turkish.
Common Errors and Solutions
Your API key is invalid or has been deactivated. Create a new key from the Dashboard. If the Authorization header is missing you get invalid_auth; if the key does not start with sk-ony- you get invalid_api_key_format.
Your balance is insufficient for this request. The required and available fields of the body show the required and the available amount. Top up or lower max_tokens (see Balance pre-check).
The requested endpoint or record was not found: a wrong path, a model that is not in the catalog in GET /v1/models/{model_id}, or a usage/transaction record that does not exist. In a chat request an unknown model returns 400 unsupported_model instead.
You have exceeded the per-minute request limit. Wait a moment and try again.
You have reached the daily token limit defined for this model. Ask your administrator (partner) for a new limit or wait for the daily reset.
You have reached the monthly token limit defined for this model. It resets on the first day of the month.
You have reached the daily request-count limit defined for this model.
This model is blocked for the project/customer. Contact your partner or admin for access.
You have exceeded the free model usage limit. At most 5 requests can be sent within 24 hours.
The request size is too large. If you are uploading a base64 image, shrink the image (max 10 MB) or send it directly via a public URL.
Video/image/music generation failed. This is usually a temporary issue with the upstream generation service — try another model or try again in a few minutes.
The model id is not in the catalog or the model is currently inactive. For the current model list, call GET /v1/models.
403: your key has a model lock and the requested model is not in the allowed list (also returned when the model chosen by OnyRouter is not in the lock). On the Anthropic endpoints this is returned as 404 not_found_error.
403: on personal keys, free models (:tr, :free) are only available to verified accounts: sign in with Google or GitHub, a verified e-mail or a real balance top-up is required. Project and partner keys are not subject to this requirement. It does not count toward the quota; X-Should-Retry: false is returned.
429: the key's daily request limit (10,000 by default) has been reached. Retry-After gives the time until the end of the day; if that is longer than 60 seconds, X-Should-Retry: false is returned too and the SDKs do not retry.
429: the limit of :tr requests that can be processed at the same time has been reached (4 by default; per account for personal keys, per key for project and partner keys). Extra requests wait in a queue for a short time; if no slot frees up, this error is returned with Retry-After. It is not deducted from your daily quota.
429: the daily request quota of :tr models is used up (shared across all :tr models). Retry-After gives the time until the quota renews.
400: an unsupported field or tool type was sent to /v1/responses (e.g. previous_response_id, a file_id); code is unsupported_parameter or unsupported_tool and the param field names the field. On /v1/messages the same case is returned in the Anthropic format as 400 invalid_request_error; the field or tool name (e.g. file_id, code_execution) is stated in the error message.
504: the model response did not complete within the time limit. Use stream: true for long generations; if a stream is cut off, the part already generated is charged.