go2llm API documentation

LLM API error codes: a reference with fixes

An LLM API error is an HTTP status plus a JSON error object carrying type, code, message and request_id. The status tells you who is at fault: 4xx is your request or your key, 5xx is the model provider. This page covers the nine codes that go2llm and the official Anthropic and OpenAI APIs return most often, and each one links to a page that reproduces it and fixes it.

Written 2026-09-12. The same table in machine-readable form lives in the API docs.

Every code in one table

HTTPerror.codeWhat to do
400invalid_requestCheck the JSON and the required fields model, messages, max_tokens
400unsupported_content_typeRemove image, audio, video and PDF blocks from a text request
400model_protocol_mismatchTake the right endpoint for the model from GET /v1/models
401no-keySend a valid gk_sk_… key in x-api-key or Authorization: Bearer
402insufficient_balanceTop up from $1, or lower max_tokens
403model / inactive / expired / ipCheck the key's model list, expiry and IP allow-list
404model_not_foundCopy the exact public model ID from GET /v1/models
404— (on /v1/responses)Switch clients: go2llm has no Responses API
409idempotency_conflictDo not reuse an Idempotency-Key for a different body
413request_too_largeShrink the body: the limit is 20 MiB
429rate_limitWait out Retry-After; the default is 20 RPM per key
429quotaRaise the key's dollar quota or create a second key
502upstream_unavailableRetry a safe text request with exponential backoff
503product_paused / no_eligible_routeCheck GET /status and retry later

A separate category is the errors returned not by go2llm but by the vendors themselves: authentication_error with the text invalid x-api-key from Anthropic, insufficient_quota and your credit balance is too low from OpenAI and Anthropic, unsupported_country_region_territory from OpenAI, and overloaded_error with status 529 from Anthropic. They get their own treatment below, because the literal string is what developers actually paste into a search box.

How to read an error response

go2llm returns one JSON object with four fields. Parse all four: the HTTP status alone is not enough, because one 400 and one 429 each cover several distinct causes with different fixes.

{
  "error": {
    "type": "insufficient_balance",
    "code": "insufficient_balance",
    "message": "not enough spendable balance",
    "request_id": "gw_4f1c8a2e9b7d40c3a15e6f2b8c9d0e1a"
  }
}

error.type is the class of the error, and it is what decides retry-or-not. Only upstream_unavailable, rate_limit and product_paused are safe to retry. Everything else returns the same answer on the second attempt.

error.code is the machine code you branch on. For gateway errors code equals type, but keep them separate anyway: the authorization codes (no-key, inactive, expired, ip, model) come from the key subsystem and distinguish four different reasons hiding under one 403.

error.message is the human-readable explanation — missing or unknown api key, model not allowed for this key, Only text input is supported; rejected image at messages.0.content.1.source. Do not regex the message; the wording changes. Branch on code.

error.request_id is the request identifier, prefixed gw_. It is the one thing worth attaching to a support email at support@go2llm.tech or a message to @go2llm_support. Detailed request logs are kept for 90 days; prompts and completions are not written to product logs, so a request_id reconstructs the route, the usage, the cost and the status — but not the content.

go2llm also sends a Retry-After header in seconds on 429 and 503. For product_paused it is 60 seconds.

A retry strategy that works

A correct retry for an LLM API rests on three rules. First: retry only 429, 502, 503 and network timeouts — 400, 401, 402, 403, 404 and 409 are not cured by repetition. Second: honour Retry-After when the header is present. Third: always add jitter, or ten parallel workers that all saw the same 429 will hit the gateway again in lockstep at exactly 1, 2 and 4 seconds.

import random, time
import httpx

RETRYABLE = {429, 502, 503}
BASE_URL = "https://go2llm.tech/v1/chat/completions"

def call_with_retry(payload, api_key, max_attempts=5, base=1.0, cap=32.0):
    with httpx.Client(timeout=120.0) as client:
        for attempt in range(max_attempts):
            r = client.post(
                BASE_URL,
                headers={"Authorization": f"Bearer {api_key}"},
                json=payload,
            )
            if r.status_code < 400:
                return r.json()
            if r.status_code not in RETRYABLE or attempt == max_attempts - 1:
                err = r.json().get("error", {})
                raise RuntimeError(
                    f"{r.status_code} {err.get('code')}: {err.get('message')} "
                    f"[request_id={err.get('request_id')}]"
                )
            server_hint = r.headers.get("retry-after")
            if server_hint and server_hint.isdigit():
                delay = float(server_hint)
            else:
                delay = min(cap, base * (2 ** attempt))
            time.sleep(delay * (0.5 + random.random() / 2))  # full-range jitter

Five attempts with a 1-second base and a 32-second cap add up to roughly 23 seconds of waiting in the worst case, which is a sane ceiling for an interactive request. For background jobs raise max_attempts to 8 and cap to 60 seconds.

Two exceptions that tutorials skip. First, a stream cannot be retried after the first valid SSE event: go2llm can swap in a compatible fallback route only before the first byte of the response, and a client-side retry after that produces a duplicate. Second, ambiguous_upstream_result on media requests must not be retried automatically: the provider may have accepted the request, and a repeat charges twice.

What an error costs

A failed request to go2llm does not charge you. The gateway reserves an estimated cost before the call, settles the actual cost after it, and releases the remainder of the reservation; the balance never goes below zero. The real cost of every successful request arrives in usage.cost_usd, including in the final usage block of an SSE stream. Prices for every published model are on the pricing page: claude-sonnet-5 is $0.33 per 1M input tokens against the official $2.00.

Every error code on this site

FAQ

What is the difference between error.type and error.code? type describes the class of error and drives the retry decision; code is the precise machine code you branch on. For gateway errors the two match. For authorization errors code is more specific and takes the values no-key, inactive, expired, ip, model, quota.

Which codes are safe to retry automatically? 429, 502, 503 and network timeouts. Retrying 400, 401, 402, 403, 404 or 409 is pointless: go2llm returns the same response until the request, the key or the balance changes.

Where do I find request_id if the body will not parse? It is in the response body at error.request_id and starts with gw_. If the body is empty and the status is 502 or 503, the connection dropped before the gateway formed a response; attach the request time to the second, plus the model, to your support message.

Why is the error message in English? error.message is English so that the text matches the official Anthropic and OpenAI APIs and does not break existing handlers during a migration.


Get a key and make your first request in 3 minutes → go2llm.tech. Registration credits $1 to your balance, which covers a first request without topping up. Full API reference: go2llm.tech/docs.