API errors
Always check both the HTTP code and the response body. For gateway errors the object carries error.type, error.code, error.message and error.request_id; an upstream may return the compatible error format of its own protocol.
| HTTP | Code or reason | What to do |
|---|---|---|
| 400 | invalid_request | check your JSON and the required fields |
| 400 | unsupported_content_type | remove the image, audio, video, PDF or binary block from a text request |
| 400 | model_protocol_mismatch | pick the endpoint listed in /v1/models |
| 400 | stateful_responses_unsupported | drop previous_response_id and store: true, send the history in input |
| 401 | no-key | send a valid API key |
| 402 | insufficient_balance | top up the balance or lower max_tokens |
| 403 | model, inactive, expired, ip | check the key's settings |
| 404 | model_not_found | refresh the catalog and copy the exact model ID |
| 404 | endpoint_not_found | there is no such route under /v1/; compare with the list in error.message |
| 405 | method_not_allowed | the route exists but takes a different method — it is named in the allow header |
| 409 | idempotency_conflict | do not reuse one Idempotency-Key for different image requests |
| 429 | quota, rate_limit | check the quota; respect Retry-After |
| 502 | upstream_unavailable | retry a safe text request after a delay |
| 503 | product_paused, no_eligible_route | check /status and retry later |
A miss on the route
Any unknown path under /v1/ answers with JSON in the same shape as every other error, not with plain text — an SDK can parse that and show a human the reason instead of garbage.
curl -i https://go2llm.tech/v1/embeddings
{
"error": {
"type": "invalid_request_error",
"code": "endpoint_not_found",
"message": "/v1/embeddings is not published by this gateway. Available: POST /v1/messages, POST /v1/chat/completions, POST /v1/responses, POST /v1/images/generations, POST /v1/messages/count_tokens, GET /v1/models, GET /v1/models/{id}, GET /v1/me. See https://go2llm.tech/docs/endpoints",
"request_id": "gw_..."
}
}
If the path is published but the method is wrong, you get 405 method_not_allowed with an allow header — that way a typo in the path is distinguishable from a wrong method.
curl -i -X GET https://go2llm.tech/v1/messages # 405, allow: POST
OPTIONS on the routes that take an API key also returns 405, with an explanation that browser-side calls are unsupported on purpose: a key in a browser bundle is readable by every visitor. CORS is open only on public reads of the catalog, prices and status; details are in API endpoints.
Stateful Responses
POST /v1/responses is implemented as a stateless adapter: the gateway keeps no response history. The fields previous_response_id, store: true and background: true are rejected with 400 stateful_responses_unsupported, and the message says the gateway stores no response state and the full history must be sent in input. How to restructure the loop is on the Responses API page.
Request ID
x-go2llm-request-id is now on every /v1/* response, including failed and streamed ones. On errors the same identifier is duplicated in the body as error.request_id; the prefix is gw_.
curl -sD - -o /dev/null https://go2llm.tech/v1/models | grep -i x-go2llm-request-id
Log that header on your side: with it a support request can be traced to a specific call, without it it cannot.
Everything else
Do not automatically retry a media request that came back with ambiguous_upstream_result: the provider may have accepted it. Keep the request_id for support, but never send your full API key.
The gateway does not emit X-RateLimit-Remaining or X-RateLimit-Reset. Retry-After arrives on 429, on 409 request_in_progress and on 503 product_paused; on 502 upstream_unavailable it does not — build your own retry strategy. To check the remaining budget before a long run, so you don't hit a 402 halfway through a task, call GET /v1/me — see Balance and spending.