Skip to content

Errors & error codes

The unified error JSON shape, the complete error code list, and retry strategy.

Errors & error codes

Every error returns a non-2xx HTTP status with this body shape:

{
  "error": {
    "code": "insufficient_credit",
    "message": "Your credit balance is insufficient for this request."
  }
}

code is a stable, machine-readable string — branch on it. message is human-readable and its wording may change at any time, so never match on it.

Error code reference

HTTPcodeMeaningWhat to do
400invalid_requestMalformed request: missing required fields, unparseable JSON, wrong parameter type or range.Fix the request. Retrying will not help.
401unauthorizedNo Authorization header, or not in Bearer <key> form.Send the correct header.
401invalid_api_keyThe key does not exist, was deleted, or is malformed.Check the key is complete and free of stray whitespace, or create a new one in the Dashboard.
403api_key_disabledThe key has been disabled.Re-enable it in the Dashboard, or use a different key.
403model_not_allowedThe model exists but is not on this key's allowlist.Adjust the key's model allowlist, or use a permitted model.
404model_not_foundNo such model ID on the platform.Confirm the ID with GET /v1/models. Remember the provider/model prefix.
402insufficient_creditThe account balance cannot cover this request.Top up in the Dashboard.
402spend_limit_reachedThe key's monthly spend limit has been reached.Raise the limit, or wait for the automatic monthly reset.
429rate_limitedRequest rate exceeded.Back off per Retry-After and retry. See Rate limits.
502upstream_errorThe upstream provider returned an error, timed out, or is temporarily unavailable.Safe to retry with exponential backoff, or fail over to another model.

Examples

HTTP/1.1 402 Payment Required
Content-Type: application/json

{
  "error": {
    "code": "spend_limit_reached",
    "message": "This API key has reached its monthly spend limit of $50.00."
  }
}
HTTP/1.1 404 Not Found
Content-Type: application/json

{
  "error": {
    "code": "model_not_found",
    "message": "Model 'gpt-4o' does not exist. Did you mean 'openai/gpt-4o'?"
  }
}

Should you retry?

CategoryCodesRetry?
Client errorsinvalid_request, model_not_foundNo. Fix the code first.
Auth problemsunauthorized, invalid_api_key, api_key_disabled, model_not_allowedNo. Needs human action.
Billing problemsinsufficient_credit, spend_limit_reachedNo. Needs a top-up or a limit change.
Transient problemsrate_limited, upstream_errorYes. Exponential backoff with jitter.

Handling errors (Python)

The openai SDK raises on non-2xx responses; the error body is available via err.response.json().

import os
import time

import httpx
from openai import OpenAI, APIStatusError

client = OpenAI(
    base_url="https://api.alphacurve.io/v1",
    api_key=os.environ["INFERENCE_API_KEY"],
    timeout=httpx.Timeout(60.0),
)

RETRYABLE = {"rate_limited", "upstream_error"}

def complete(**kwargs):
    for attempt in range(5):
        try:
            return client.chat.completions.create(**kwargs)
        except APIStatusError as err:
            try:
                code = err.response.json()["error"]["code"]
            except Exception:
                code = "unknown"

            if code not in RETRYABLE:
                raise

            retry_after = err.response.headers.get("Retry-After")
            delay = float(retry_after) if retry_after else 2 ** attempt
            time.sleep(delay)

    raise RuntimeError("retries exhausted")

Handling errors (Node / TypeScript)

import OpenAI, { APIError } from "openai";

const client = new OpenAI({
  baseURL: "https://api.alphacurve.io/v1",
  apiKey: process.env.INFERENCE_API_KEY!,
});

const RETRYABLE = new Set(["rate_limited", "upstream_error"]);

async function complete(
  params: OpenAI.Chat.ChatCompletionCreateParamsNonStreaming,
) {
  for (let attempt = 0; attempt < 5; attempt++) {
    try {
      return await client.chat.completions.create(params);
    } catch (err) {
      if (!(err instanceof APIError)) throw err;

      const code = (err.error as { code?: string } | undefined)?.code ?? "unknown";
      if (!RETRYABLE.has(code)) throw err;

      const wait = 2 ** attempt * 1000 + Math.random() * 250;
      await new Promise((r) => setTimeout(r, wait));
    }
  }
  throw new Error("retries exhausted");
}

Debugging with curl

-i prints the status line, headers and body together:

curl -i https://api.alphacurve.io/v1/chat/completions \
  -H "Authorization: Bearer $INFERENCE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "model": "does-not-exist", "messages": [] }'

When reporting a problem, include the response id (if present), the code, the timestamp and the model — that is enough for us to locate the request.

Errors during streaming

Once a stream starts the HTTP status is already 200. A later failure is delivered as an SSE event:

data: {"error":{"code":"upstream_error","message":"Upstream provider timed out."}}

data: [DONE]

So your stream parser must check each chunk for an error field in addition to choices.