Blog/OpenRouter errors

OpenRouter errors: what 404, 429, 401 and “provider returned error” mean, and how to fix each

24 September 2026 · 7 min read

We call models through OpenRouter every day, and we have hit most of these errors ourselves. Each section below says what the error means, what causes it and how to fix it. Behaviour comes from OpenRouter's own docs, linked at the end. Where we say “when we tried it”, the quoted message is what the API sent back to us on 24 September 2026, sometimes cut short to the key part.

Quick fixes

CodeYou seeWhat it meansFirst thing to try
404"No endpoints found..." or a model that was removedThe model id is dead, or your settings filter out every providerLook the id up in the live model list; loosen provider filters
400"... is not a valid model ID"The model id is misspelled or never existedCopy the exact id from the model list
401"User not found."The key does not match an OpenRouter accountCheck the key with GET /api/v1/key; make a new key
402Payment requiredA credit or spending limit stopped the requestRead error.metadata.limit_source, then add credits, raise the key’s limit or wait
429"Rate limit exceeded"Too many requests, from OpenRouter’s limits or the provider’sWait (Retry-After, if sent), retry, add fallback models
Varies"Provider returned error"The company running the model rejected or failed the requestRead error.metadata.raw for the real message

One catch first: an error does not always come with an error status. If a provider has already accepted your request, OpenRouter has sent status 200, so the error arrives inside the response body (or as a streamed event). Always check the body for an error field, not only the status.

OpenRouter 404: “No endpoints found” and model not found

A 404 from OpenRouter means it found nothing that can serve your request. OpenRouter's docs describe 404 as “the requested resource (model, file, etc.) does not exist”. In practice there are two very different causes.

Cause 1: the model is gone

Models get retired, and free periods end. When we called one retired model id, the API returned 404 with this message:

The free Devstral 2 2512 period has ended. To continue using this model, please migrate to the paid slug: mistralai/devstral-2512

Look closely: the “paid slug” it suggests is the exact id we had called. Do not trust a replacement id from an error message until you have checked it yourself.

Fix: look the id up in the live model list. Each model there also has an expiration_date, which is empty unless the model is being retired, so you can see removals coming.
# List every live model id, with its end date if it has one
curl -s https://openrouter.ai/api/v1/models \
  | jq -r '.data[] | [.id, (.expiration_date // "")] | @tsv'

# Is my id still there?
curl -s https://openrouter.ai/api/v1/models \
  | jq -r '.data[].id' | grep -x "mistralai/devstral-2512"

A misspelled or made up id is a different error. When we tried one, OpenRouter returned 400 with openai/gpt-made-up-model is not a valid model ID, not 404.

Cause 2: your settings filter out every provider

A model can be served by more than one provider. Your request can rule all of them out. When we asked for features no provider of that model supports, with require_parameters on, we got 404 with:

No endpoints found that can handle the requested parameters.

And when we allowed only a provider that does not serve that model, we got 404 with No allowed providers are available for the selected model. The response also listed which providers do serve it, and which filter step removed them.

Things that shrink the provider list:

  • provider.only or provider.ignore lists.
  • provider.require_parameters: true, which keeps only providers that support every parameter you sent.
  • provider.data_collection: "deny", or the same choice in your account's privacy settings, which drops providers that may store your data.
  • provider.zdr: true, which keeps only zero data retention endpoints.
Fix: read error.metadata in the 404 response. It names the step that removed the last provider. Loosen that one setting, or pick a model whose providers meet it.

OpenRouter 401: “User not found”

OpenRouter's docs describe 401 as invalid credentials: a disabled or invalid API key, or an expired login session. When we sent a well formed key that belongs to no account, the reply was User not found. When we sent no key at all, it was No cookie auth credentials found.

Common causes:

  • The key was deleted or disabled on your OpenRouter keys page.
  • The key was copied with a missing character or an extra space or line break.
  • The key is from another service (an OpenAI or Anthropic key sent to OpenRouter).
  • Your app sends the key in the wrong place. OpenRouter wants the header Authorization: Bearer YOUR_KEY.
  • Your tool is still using an old key saved somewhere else, such as an environment variable.
Fix: test the key on its own. If this call works, the key is fine and the problem is how your app sends it. If it fails, create a new key.
curl -s https://openrouter.ai/api/v1/key \
  -H "Authorization: Bearer $OPENROUTER_API_KEY"

OpenRouter is a GitHub secret scanning partner. If it decides your key was exposed, it emails you, so check your inbox if a key that used to work suddenly stops.

OpenRouter 402: a credit or spending limit

402 means a credit or spending limit stopped the request. This can happen even with money in your account. According to the docs, a negative balance can cause 402 even on free models. There are three places the limit can come from, and error.metadata.limit_source tells you which:

  • openrouter_credits: your balance cannot cover the request, or this one request is too big for your spending budget. Add credits, or send a smaller request (less text, or a lower max_tokens).
  • openrouter_key_limit: this key's own spending cap is used up. Raise it or wait for it to reset.
  • openrouter_in_flight_budget: your running or recently finished paid requests have filled your spending budget for now. Wait for the Retry-After header and retry.
Fix: the GET /api/v1/key call above shows limit_remaining and your usage, so you can spot this before requests start failing.

OpenRouter error 429: rate limit exceeded

A 429 can come from two places:

  • OpenRouter itself, mostly on free models (ids ending in :free). The docs list 20 requests a minute, and 50 requests a day for accounts that have bought fewer than about 10 credits, or 1,000 a day above that. Your own limit is in free_model_daily_requests (see below).
  • The provider running the model, when it is busy. With fallback routing on (the default), OpenRouter retries other providers for the same model before the error reaches you. That stops once part of the answer has already been sent to you.
Fix: wait, then retry. If the reply has a Retry-After header, wait that many seconds. The official OpenAI, Anthropic, Vercel AI and OpenRouter code libraries already respect it. If you call the API yourself, do it by hand, back off a little more on each try, and check the body as well as the status:
import time, requests

URL = "https://openrouter.ai/api/v1/chat/completions"
RETRY_CODES = {429, 502, 503}

def call_openrouter(payload, key, tries=4):
    for attempt in range(tries):
        r = requests.post(URL, json=payload,
                          headers={"Authorization": f"Bearer {key}"}, timeout=120)
        body = r.json()
        # An error can arrive with status 200, so read the code from the body too.
        err = body.get("error")
        code = err.get("code") if err else r.status_code
        if code in RETRY_CODES and attempt < tries - 1:
            wait = r.headers.get("Retry-After")   # not always sent
            time.sleep(float(wait) if wait else 2 ** attempt)
            continue
        if err:
            meta = err.get("metadata") or {}
            raise RuntimeError(f'{code}: {err.get("message")} '
                               f'{meta.get("provider_name") or ""} {meta.get("raw") or ""}')
        return body
    raise RuntimeError("Still failing after retries")

For a free model, the free_model_daily_requests field in GET /api/v1/key shows how many of today's free requests are left.

“Provider returned error”

This one is vague on purpose: the real error came from the provider, the company that actually runs the model, and OpenRouter wraps it. When we sent a broken JSON schema (a description of the answer format we wanted), the reply was 400 with the message Provider returned error. The useful part was in error.metadata:

  • raw held the provider's own message, which said exactly what was wrong with the schema.
  • provider_name said which provider sent it.
  • A list of earlier errors showed OpenRouter had already tried a second provider, which failed the same way.

For the chat completions endpoint, OpenRouter's docs describe a stable error type in error.metadata.error_type, such as rate_limit_exceeded, provider_overloaded or context_length_exceeded. Other endpoints put it in a different place, and our own error above did not include it at all. Use it when it is there, and fall back to the status code when it is not.

Fix: log error.metadata in full. If raw points at your request (a bad parameter, too much text for the model), fix the request; retrying will not help. If it says the provider is down or overloaded, retry or fall back to another model.

Fallback models and provider order

Two settings make many of these errors go away before you see them. They do different jobs.

Fallback models: the models array

Send a list of model ids in the order you want them tried. If the first model returns an error, OpenRouter tries the next one. By default any error can trigger this, including rate limits, downtime, context length errors and moderation flags. You pay for the model that actually answered, and the response names it in its model field.

curl -s https://openrouter.ai/api/v1/chat/completions \
  -H "Authorization: Bearer $OPENROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "models": ["first/model-id", "second/model-id", "third/model-id"],
    "messages": [{"role": "user", "content": "Hello"}]
  }'

With OpenAI's code library (SDK), pass models inside extra_body. Check every id in the list against the live model list: a dead id in your fallback list is a fallback that fails.

Provider order and allow_fallbacks

This is about providers of the same model, not other models. provider.order lists the providers to try first. allow_fallbacks is on by default, which lets OpenRouter use other providers when your chosen ones are down. Set it to false only if you must stay on your list, and expect more errors when those providers have trouble.

{
  "model": "some/model-id",
  "messages": [{"role": "user", "content": "Hello"}],
  "provider": {
    "order": ["provider-a", "provider-b"],
    "allow_fallbacks": true
  }
}

What happened to us on 23 September

A model we used was removed from OpenRouter, and calls to it started returning 404. The error told us to move to the id we were already calling. The fix went live almost 14 hours after the last good call, because our alerts missed it: that call ran outside the part of our system we logged. We now alert on “model not found” the moment it happens and check published end dates every day.

The full story, with what we changed, is in our research on AI model speed, timeouts and a withdrawn model.

  • Log every model call, including the ones outside your main flow.
  • Alert on “model not found” on its own. It is not the same as a slow or busy provider.
  • Read expiration_date from the model list every day.
  • Do not trust a replacement id from an error message until you have called it yourself.

MegaLens reviews code with AI models from different companies, and it accepts your own OpenRouter key. See how to set it up.

Questions people ask

What does "No endpoints found" mean on OpenRouter?

OpenRouter could not find any provider that can serve your request. Either the model is gone, or your settings rule out every provider for it: an allow list, a data policy, or a request for features no provider supports. Check the model in the live model list, then loosen your provider settings.

Why does OpenRouter say "User not found"?

The API key you sent does not match an OpenRouter account. The key may be deleted, disabled, copied wrong, or from another service. Test it with a GET request to https://openrouter.ai/api/v1/key. If that fails too, make a new key.

How do I fix OpenRouter error 429?

You are being rate limited, either by OpenRouter or by the provider. Wait, then retry. If the reply has a Retry-After header, wait that many seconds. Free models have their own limits: the docs list 20 requests a minute, and 50 or 1,000 a day depending on how many credits you have bought. GET /api/v1/key shows your own daily limit and what is left. A fallback model list helps too.

What does "Provider returned error" mean?

The company that actually runs the model sent back an error, and OpenRouter passed it on. The useful part is inside error.metadata: raw holds the provider’s own message and provider_name says who sent it.

How do I set fallback models on OpenRouter?

Send a models array instead of one model, in the order you want them tried. If the first model returns an error, OpenRouter tries the next one. You are charged for the model that actually answered, which the response names in its model field.

How do I know if an OpenRouter model is being removed?

Call GET https://openrouter.ai/api/v1/models and read each model’s expiration_date. It is empty for a model with no end date, and set to a date for one that is being retired.

Sources

OpenRouter's own documentation, read on 24 September 2026. Messages we describe as “when we tried it” are from our own calls to the live API that day, some cut short. Other quoted text is from the docs. OpenRouter changes over time, so check the docs if something here no longer matches.