Errors
Errors follow the format of the endpoint you called, so the OpenAI and Anthropic SDKs can read them. The HTTP status decides whether SDKs retry automatically.
Format
OpenAI (/v1/chat/completions, /v1/responses, /v1/models, images, and video):
{
"error": {
"message": "Insufficient balance for this request. Top up your balance and try again.",
"type": "insufficient_balance",
"param": null,
"code": "insufficient_balance"
}
}
Anthropic (/v1/messages):
{
"type": "error",
"error": {
"type": "insufficient_balance",
"message": "Insufficient balance for this request. Top up your balance and try again."
}
}
Error codes
| HTTP | code | Meaning | Retry? |
|---|---|---|---|
| 400 | invalid_json | The body is not valid JSON. | No |
| 400 | invalid_request | A wrong parameter; param names it. For example max_tokens above the model limit, n other than 1, or empty messages. | No, fix it first |
| 401 | invalid_api_key | The key is missing, malformed, revoked, or expired. | No |
| 402 | insufficient_balance | The balance is too low for this request's hold. | No, top up or lower max_tokens |
| 403 | account_suspended | The account is suspended. | No, contact support |
| 403 | key_spend_limit_exceeded | The key reached its spend limit. | No |
| 403 | model_not_allowed | The key may not use this model. | No |
| 404 | model_not_found | The model does not exist or is unavailable. | No, check /v1/models |
| 404 | not_found | Unknown path. | No |
| 405 | method_not_allowed | Wrong HTTP method. | No |
| 413 | request_too_large | The body is over the limit (8 MiB by default; 64 KiB for images and video). | No |
| 415 | unsupported_media_type | Content-Type is not application/json. | No |
| 429 | rate_limit_exceeded | A per-minute limit (key, account, or IP) was exceeded. | Yes, after Retry-After |
| 429 | too_many_concurrent_requests | The account's concurrent request queue is full. | Yes, after Retry-After |
| 500 | internal_error | Internal error. | Yes, with a delay |
| 502 | upstream_error | The model provider returned an error. Not billed. | Yes |
| 503 | overloaded | Temporarily at capacity. | Yes, after Retry-After |
| 503 | model_unavailable | The model is temporarily unavailable. | Yes, or use another model |
| 504 | upstream_timeout | The model provider took too long. Not billed. | Yes |
Conditions that retrying cannot fix (no balance, key spend limit) deliberately do not use 429, so SDKs do not retry in vain. Requests you cancel yourself are recorded as 499 client_closed_request in the dashboard.
Retry strategy
The OpenAI and Anthropic SDKs retry 408, 409, 429, and 5xx automatically. In your own client, use exponential backoff with jitter, honor Retry-After, and cap the number of attempts.
import os
import openai
from openai import OpenAI
client = OpenAI(base_url="https://api.markas.ai.id/v1", api_key=os.environ["MARKASAI_API_KEY"], max_retries=3)
try:
resp = client.chat.completions.create(
model="MODEL_ID",
messages=[{"role": "user", "content": "Halo"}],
max_tokens=100,
)
except openai.APIStatusError as e:
body = e.body if isinstance(e.body, dict) else {}
print(e.status_code, body.get("code"), e.response.headers.get("x-request-id"))
import OpenAI from "openai";
const client = new OpenAI({ baseURL: "https://api.markas.ai.id/v1", apiKey: process.env.MARKASAI_API_KEY, maxRetries: 3 });
try {
await client.chat.completions.create({
model: "MODEL_ID",
messages: [{ role: "user", content: "Halo" }],
max_tokens: 100,
});
} catch (err) {
if (err instanceof OpenAI.APIError) {
console.log(err.status, err.code, err.headers?.["x-request-id"]);
} else {
throw err;
}
}