Skip to content
أكاكوسAcacus
تواصل مع المبيعاتContact sales
Docs menu

API reference

Errors

When a request fails, the API answers with an HTTP status and a JSON body that says what went wrong. This page lists every error, what it means and what to do about it.

The error body

Errors use OpenAI's format: one error object with a message for people and a code for your program. This is a 402 for a request too large for the free tier:

Response
{
  "error": {
    "message": "This request is too large for the free tier: at most 80,000 prompt tokens (DeepSeek's count plus 10%, and 1,024 per image) and 320,000 bytes of JSON not counting image data. Send less input, or top up.",
    "type": "insufficient_quota",
    "code": "FREE_TIER_DAILY_LIMIT",
    "param": null,
    "reason": "request_too_large",
    "balance": 0
  }
}
Name
Type
Description
error.message
string
What went wrong, in a sentence. The wording can change: don't test for it in code.
error.type
string
The kind of error, set by the HTTP status (below).
error.code
string
The exact reason, such as MODEL_NOT_FOUND or FREE_TIER_THINKING. Test for this in code.
error.param
string or null
The request field at fault, such as model or max_tokens, else null.
error.reason
string
On some 402 errors: allowance, request_too_large or paused.
error.balance
number
On every 402: your API balance, in LYD.
error.requiredLyd
number
On a 402 when you have a balance that is too small for this request: the smallest balance that would run it as a paid request.
error.limit
integer
On TOO_MANY_IMAGES and on 429 rate_limit_error: the limit you reached.
error.suspension
object
On 403 ACCOUNT_SUSPENDED: until (a time, or null), reason (or null) and whatsapp, the number to contact.
error.upstreamStatus
integer
On UPSTREAM_ERROR: the HTTP status DeepSeek answered with.

Status and type

Each HTTP status has one type. The OpenAI libraries raise a different exception for each status:

StatustypePython exceptionNode.js error
400invalid_request_errorBadRequestErrorBadRequestError
401authentication_errorAuthenticationErrorAuthenticationError
402insufficient_quotaAPIStatusErrorAPIError
403permission_errorPermissionDeniedErrorPermissionDeniedError
404invalid_request_errorNotFoundErrorNotFoundError
405invalid_request_errorAPIStatusErrorAPIError
413none: an HTML pageAPIStatusErrorAPIError
422invalid_request_errorUnprocessableEntityErrorUnprocessableEntityError
429rate_limit_errorRateLimitErrorRateLimitError
500, 502, 503server_errorInternalServerErrorInternalServerError

In Python every one of them is an openai.APIStatusError; in Node.js, an OpenAI.APIError.

Errors from DeepSeek

The gateway checks a few fields itself. DeepSeek checks the rest, and when it refuses a request, its error comes back as DeepSeek sent it: the same format, with code set by DeepSeek (usually invalid_request_error) and DeepSeek's own request_id at the end of the message. This is DeepSeek's answer to "temperature": 5:

Response
{
  "error": {
    "message": "Invalid temperature value, the valid range of temperature is [0, 2] (request_id: 6cc042c7-9847-4c53-aed8-266da6985f06)",
    "type": "invalid_request_error",
    "param": null,
    "code": "invalid_request_error"
  }
}

DeepSeek answers 400 for a value it refuses, and 422 for a body it can't read at all, such as an unknown role. A streamed request that DeepSeek refuses gets the same JSON error, not a stream.

Errors that aren't JSON

A few errors come from the servers in front of the API, before your request reaches it. Their body is not JSON, so check the Content-Type before you parse it:

  • 413: the body is over 2 MB. nginx answers with an HTML page (below).
  • 403 with the text error code: 1010: Cloudflare refused the HTTP client. This happens with Python's urllib, for example. The OpenAI libraries, curl, requests, httpx and Node.js's fetch work. See Troubleshooting and FAQ.
  • A 5xx status with an HTML page, such as 502, 504 or one of Cloudflare's 52x: the servers in front of the API got no answer from it, or not in time. A 504 after 120 seconds is nginx's time limit: stream long replies, see Timeouts. Otherwise retry.
413 response (text/html)
<html>
<head><title>413 Request Entity Too Large</title></head>
<body>
<center><h1>413 Request Entity Too Large</h1></center>
<hr><center>nginx</center>
</body>
</html>

Handling errors in code

This request names a model the API doesn't serve, so it fails with 400. It costs nothing and needs no balance:

curl -sS -w '\nHTTP status: %{http_code}\n' https://acacus.ly/v1/chat/completions \
  -H "Authorization: Bearer $ACACUS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "deepseek-chat",
    "messages": [{"role": "user", "content": "Say OK."}],
    "thinking": {"type": "disabled"}
  }'

The curl example prints:

Terminal
{"error":{"message":"Unknown model: deepseek-chat. Model ids are exact. GET /v1/models, sent with this key, lists the models it can use: deepseek-v4-flash, deepseek-v4-pro and any others your account has.","type":"invalid_request_error","code":"MODEL_NOT_FOUND","param":"model"}}
HTTP status: 400

The Python and Node.js examples print this, with a new request id each time:

Terminal
400 MODEL_NOT_FOUND invalid_request_error model
Unknown model: deepseek-chat. Model ids are exact. GET /v1/models, sent with this key, lists the models it can use: deepseek-v4-flash, deepseek-v4-pro and any others your account has.
x-request-id: a826fc60-8911-4518-be8d-2e960b80eb23

In Python, e.body holds the error object, so the details are e.body["balance"] and so on. In Node.js they are in err.error.

All errors

The code of each error, the start of its message, when it happens and what to do. Errors that come from the gateway's own checks cost nothing: see Billing and the free tier.

400 Bad request

CodeMessageWhenWhat to do
INVALID_JSONThe request body is not valid JSON.The body can't be read as JSON.Send a JSON object.
INVALID_JSONThe request body must be a JSON object.The body is JSON but not an object, such as null or a list.Send a JSON object.
INVALID_PARAMETERmax_tokens must be a whole number of at least 1 (got 0).model is not a string; max_tokens or max_completion_tokens is not a whole number of at least 1; stream is not true or false; or the request sends functions or function_call. param names the field.Fix that field. Use tools and tool_choice instead of functions.
MODEL_NOT_FOUNDUnknown model: deepseek-chat. Model ids are exact. GET /v1/models, sent with this key, lists the models it can use: deepseek-v4-flash, deepseek-v4-pro and any others your account has.The model id isn't one your key can use.Use an id from Models, or from GET /v1/models sent with your key. Ids are exact.
MODEL_NO_VISIONdeepseek-v4-pro can't read images. Send the message to a model that reads images (deepseek-v4-flash), or remove the images.An image sent to deepseek-v4-pro.Send images to deepseek-v4-flash, or remove them.
IMAGE_NOT_IN_USER_MESSAGEImages are accepted in user messages only.An image in a system, assistant or tool message.Move the image to a user message.
TOO_MANY_IMAGESToo many images in one request (at most 32).More than 32 images. limit is 32 images.Send fewer images per request.
DeepSeek's, usually invalid_request_errorInvalid n value (currently only n = 1 is supported) (request_id: ...)DeepSeek refuses a value: n other than 1, json_schema, 17 or more stop strings, a temperature or top_logprobs out of range, an empty messages list, or an image it can't download or read.Fix what the message names. See Chat completions.

401 Unauthorized

CodeMessageWhenWhat to do
MISSING_API_KEYMissing or malformed Authorization header. Send: Authorization: Bearer <your API key>No Authorization header, or not in the form Bearer <key>.Send Authorization: Bearer sk-shfr-....
INVALID_API_KEYInvalid API key: it is unknown or was revoked. Keys are managed at https://acacus.ly/platform/api-keysThe key doesn't exist, was revoked, or doesn't start with sk-.Check that you send the whole key, without quotes or spaces. If it was revoked or lost, create a new one on API keys.

402 Payment required

Your balance, or the free allowance, doesn't cover the request. Every 402 has balance; some have reason and requiredLyd. How the checks work is in Billing and the free tier.

CodeMessageWhenWhat to do
FREE_TIER_DAILY_LIMITYou have used today's free allowance. Top up, or try again after 00:00 UTC.You used today's free allowance.Top up, or wait until 00:00 UTC.
FREE_TIER_DAILY_LIMIT, reason allowanceThis request needs more than what is left of today's free allowance. The check counts the reply at its full max_tokens (N here), so a lower max_tokens or less input may fit. ...What is left of today's allowance is too small for this request.Lower max_tokens, send less input, or top up.
FREE_TIER_DAILY_LIMIT, reason request_too_largeThis request is too large for the free tier: at most 80,000 prompt tokens ... and 320,000 bytes of JSON not counting image data. ...The request is over the free size limits.Send less input, or top up.
FREE_TIER_EXHAUSTEDThe free tier is used up for today. Top up, or try again after 00:00 UTC.The free tier as a whole is used up for today.Top up, or wait until 00:00 UTC.
FREE_TIER_EXHAUSTED, reason pausedThe free tier is paused for now. Top up to continue, or try again later.Free requests are paused for now.Top up, or try again later.
FREE_TIER_EXHAUSTEDInsufficient balance. Top up to continue.The free tier is switched off, and your balance doesn't cover the request.Top up.
FREE_TIER_MODELThe free tier only includes deepseek-v4-flash. Top up to use ...A free request for a model other than deepseek-v4-flash.Use deepseek-v4-flash, or top up.
FREE_TIER_THINKINGThinking isn't included in the free tier. Top up to use it, or send the request without thinking: ...A free request that asks for thinking: a thinking field that isn't disabled, or a reasoning_effort other than none.Send "thinking": {"type": "disabled"}, or top up. See Reasoning.

When your balance is too small for a paid request, the API tries it as a free request. That is why a 402 can carry a free-tier code while you have money in your API balance: then requiredLyd says how much balance the request needs.

403 Forbidden

CodeMessageWhenWhat to do
ACCOUNT_SUSPENDEDThis Acacus account is suspended. Contact us on WhatsApp at +218 94 380 1609.The account is suspended. suspension says until when, and the reason when one is given.Contact us on WhatsApp.
None: plain text from Cloudflareerror code: 1010Cloudflare refused the HTTP client.Use the OpenAI libraries, curl, requests, httpx or Node.js's fetch. See Troubleshooting and FAQ.

404 Not found and 405 Method not allowed

CodeMessageWhenWhat to do
NOT_FOUNDUnknown endpoint: POST /v1/embeddings. This API serves POST /v1/chat/completions, GET /v1/models, GET /v1/models/{id} and GET /v1/usage.A path this API doesn't have.Check the path. The endpoints are listed in the Overview.
MODEL_NOT_FOUNDUnknown model: deepseek-chat. Without an API key this API lists deepseek-v4-flash, deepseek-v4-pro; send Authorization: Bearer <your key> to see the other models your key may use.GET /v1/models/{id} for an id this API doesn't serve.Use an id from GET /v1/models.
METHOD_NOT_ALLOWED (405)GET is not allowed on /v1/chat/completions. Use POST.The wrong HTTP method on a known path. The Allow header names the right one.Use the method in Allow.

413 Request too large

CodeMessageWhenWhat to do
None: an HTML page from nginx413 Request Entity Too LargeThe request body is over 2 MB.Send less. Images count toward the size as base64: see Images.

422 Unprocessable

CodeMessageWhenWhat to do
DeepSeek's, usually invalid_request_errorFailed to deserialize the JSON body into the target type: ...DeepSeek can't read the body: for example no messages field, or an unknown role.Fix the structure the message names.

429 Too many requests

CodeMessageWhenWhat to do
rate_limit_errorRate limit exceeded: at most 30 requests a minute per account. Try again in 55 seconds.More than 30 requests in the current minute. limit and the Retry-After header are set.Wait Retry-After seconds, then retry.
concurrency_limitToo many requests in flight for this account: wait for a previous reply to finish.3 of your requests are already running.Retry when one of them has finished.
DeepSeek'sDeepSeek's textDeepSeek's own limit.Retry with a growing wait.

The limits are explained in Rate limits and other limits.

500, 502 and 503: server errors

CodeMessageWhenWhat to do
INTERNAL_ERROR (500)Internal server error. Please retry; if it keeps failing, contact support with the x-request-id.A fault in the API.Retry. If it keeps failing, contact support with the x-request-id.
UPSTREAM_ERROR (502, or DeepSeek's status)The model provider sent an empty answer. Please retry.DeepSeek's answer was empty or not JSON. upstreamStatus has DeepSeek's status.Retry.
UPSTREAM_INTERRUPTED (502)The upstream response was interrupted. Please retry.DeepSeek's reply broke off before it was complete. If it reported its usage, that is billed; if not, the prompt plus an estimate of the reply for the time the request ran (20 tokens per second, up to max_tokens). Nothing is billed when no part of the reply had arrived.Retry. The retry is billed again.
UPSTREAM_DOWN (503)The model provider is unavailable right now. Try again in a minute.DeepSeek can't be used right now.Retry after a minute.
DeepSeek's (500, 503)DeepSeek's textA DeepSeek server error, passed on.Retry with a growing wait.

Retrying

  • 429 rate_limit_error: wait the number of seconds in Retry-After, then retry.
  • 429 concurrency_limit: retry when one of your running requests has finished.
  • 500, 502, 503, and DeepSeek's own 429: retry with a growing wait. A retried UPSTREAM_INTERRUPTED is billed again, because the first try was billed for its prompt.
  • Every other 4xx: fix the request first. Sent again unchanged, it gets the same error.

The OpenAI libraries retry 408, 409, 429 and 5xx errors on their own, 2 times by default, and wait the Retry-After time when there is one (checked in openai-python 3.19.2 and openai-node 7.23.0). Each retry counts toward your requests per minute. Change it with max_retries in Python or maxRetries in Node.js.

Request ids and support

Every reply from the API has an x-request-id header. Send your own x-request-id with a request and the reply carries the same value, so you can match the two in your logs. The libraries read it for you: e.request_id in Python and err.requestID in Node.js.

Replies from the servers in front of the API have no x-request-id: a 413, a 403 with error code: 1010, or a 5xx HTML page (see Errors that aren't JSON). For those, note the cf-ray header, which Cloudflare puts on every reply, and the time.

If an error doesn't make sense, write to [email protected] or on WhatsApp with the x-request-id (or the cf-ray header when there is no x-request-id), the time, and the status and body you got. Leave your API key out.