Errors

All error responses follow the same JSON format:

{
  "error": {
    "message": "Description of what went wrong"
  }
}

HTTP status codes

Status Meaning When
400 Bad Request Invalid request parameters (missing fields, wrong types), or a rejection forwarded from the provider
401 Unauthorized Missing, invalid, or expired API key / JWT token
402 Payment Required Valid authentication but insufficient credit balance
404 Not Found Requested resource does not exist (e.g., unknown model)
422 Unprocessable Entity Unsupported parameter or invalid field structure
503 Service Unavailable Upstream provider error (timeout, rate limit, connection failure)

Who validates the model

Quantized does not keep a gate in front of the providers. A model id we cannot resolve
in the catalog is forwarded to the provider exactly as you sent it, and the provider
decides whether it exists. The status code and the message you get back are the
provider’s, not ours.

This matters because the same mistake gets a different answer depending on who serves
the endpoint. On the two single-provider endpoints it is at least consistent:

POST /v1/aws-bedrock/embeddings    {"model": "amazon.titan-embed-text-v99:0"}
400  The provided model identifier is invalid.

POST /v1/gemini/embeddings         {"model": "gemini-embedding-9999"}
404  models/gemini-embedding-9999 is not found for API version v1beta, or is not
     supported for embedContent.

Both are the same class of error. AWS calls it a 400, Google calls it a 404.

Where an endpoint has several eligible providers it is not stable even across repeated
calls, because an unresolved model gives the router nothing to route on. Eight identical
requests to POST /v1/chat/completions with {"model": "nonexistent-model"} produced
four wordings and two status codes, one from each of the four chat providers:

404  nonexistent-model is not a valid model ID
404  The model `nonexistent-model` does not exist or you do not have access to it.
404  model: nonexistent-model
400  The provided model identifier is invalid.
Do not branch on this

Treat any 4xx naming the model as “this model is not usable”. Do not match on message
text, do not assume 404, and do not expect two identical requests to fail identically.

Checking a model before you use it

Call GET /v1/models and match on id. That is the list of ids the
catalog resolves; anything else depends on the provider accepting it.

The router still answers for itself in two cases, because no provider could give a
useful reply: when your X-Quantized-Provider pin leaves no usable provider, and when
the model resolves but cannot serve the modality you asked for. Both are covered below.

Error types

Bad Request (400)

Returned when the request payload is invalid. The message describes the specific issue.

{
  "error": {
    "message": "field required: 'model'"
  }
}

Model modality mismatch

A 400 is also returned when a chat-completion request contains a content part (image_url, input_audio, video_url) that no provider serving the model can accept. This check runs before the request reaches the provider.

{
  "error": {
    "message": "No available provider for model 'openai/gpt-4.1-nano' supports audio input"
  }
}

The message names providers rather than the model because a model’s hosts can differ: one may accept video where another does not. The router skips a provider that cannot serve the request and only fails when every reachable one refuses.

The same check runs on /v1/images/generations for output modality:

{
  "error": {
    "message": "No available provider for model 'openai/gpt-4.1-nano' supports image output"
  }
}

Pick a model that declares the required modality in input_modality / output_modality (see Models). file (PDF) parts are exempt from this check — OpenRouter’s universal PDF parser handles them on every model.

Provider pin with nothing behind it

Pinning X-Quantized-Provider to a provider that cannot serve the endpoint leaves the router with an empty pool, and it says so rather than silently ignoring your header:

{
  "error": {
    "message": "No provider available for endpoint 'chat_completions' under the current pins"
  }
}
Strict validation

All request bodies use strict validation — unknown or unsupported fields are rejected with 422. This prevents typos from being silently ignored and ensures you only use documented parameters.

Unprocessable Entity (422)

Returned when the request contains an unsupported parameter, an invalid field structure, or an unknown content type. The response includes details about which field was rejected.

{
  "detail": [
    {
      "type": "extra_forbidden",
      "loc": ["body", "logprobs"],
      "msg": "Extra inputs are not permitted",
      "input": true
    }
  ]
}

Common causes:

  • Using an unsupported parameter (e.g., top_k, modalities, audio)
  • Adding unknown fields to messages (e.g., images)
  • Using an unknown content part type value (supported types are text, image_url, input_audio, video_url, file)
  • Invalid structure for tools, response_format, or reasoning

Unauthorized (401)

Returned when the API key is missing, invalid, deactivated, or expired.

{
  "error": {
    "message": "Invalid API key"
  }
}

Payment Required (402)

Returned when your license has insufficient credits to process the request.

{
  "error": {
    "message": "Insufficient credits"
  }
}

Check your balance with GET /v1/license and contact your institution to top up.

Not Found (404)

Returned when a requested resource (e.g., a model ID) does not exist at the provider. The message comes from the provider, so it varies between providers and is not part of the contract:

{
  "error": {
    "message": "The model `nonexistent-model` does not exist or you do not have access to it."
  }
}

See Who validates the model — an unknown model is not always a 404, and on a multi-provider endpoint the same request can return either a 404 or a 400.

Service Unavailable (503)

Returned when the upstream provider encounters an error. The actual error details are masked for security:

{
  "error": {
    "message": "Service temporarily unavailable"
  }
}

This covers:

  • Timeouts — the provider did not respond in time
  • Rate limits — the provider is throttling requests
  • Connection errors — network issues reaching the provider
  • Authentication errors — issues with the upstream API key (not your Quantized key)
Masked messages

503 errors always return “Service temporarily unavailable” regardless of the underlying cause. This prevents leaking internal infrastructure details.

Handling errors

import httpx

response = httpx.post(
    "https://api.quantized.us/v1/chat/completions",
    headers={"Authorization": "Bearer sk-quantized-YOUR-KEY"},
    json={"model": "openai/gpt-4.1-mini", "messages": [...]},
)

if response.status_code == 200:
    data = response.json()
    print(data["choices"][0]["message"]["content"])
elif response.status_code == 402:
    print("Out of credits — contact your institution")
elif response.status_code == 422:
    details = response.json()["detail"]
    for d in details:
        print(f"Unsupported field: {'.'.join(str(x) for x in d['loc'])}")
elif response.status_code == 503:
    print("Provider unavailable — retry after a moment")
else:
    error = response.json()
    print(f"Error {response.status_code}: {error['error']['message']}")