OpenRouter Free-Model Recovery Without Paid Fallbacks
This guide is for a strict zero-spend recovery boundary: recover an unusable OpenRouter request by retrying only an exact catalog variant that has been verified as free. It does not recommend a model, compare models, or introduce a paid fallback. If no verified free candidate completes, the workflow stops.
The zero-cost boundary
Append :free only to a model that has a free entry. OpenRouter treats that entry as a separate catalog record with its own pricing, context length, endpoints, and potentially different limits or availability from paid versions.
Treat the full model ID as the unit of verification:
- A base ID without
:freeis not verified as free. - A display name is not a substitute for an exact ID.
- A routing suffix is not a free variant. For example,
:nitroreturns the base model's entry rather than a separate catalog entry. - A 404 from the single-model lookup means the identifier does not exist or resolve as an alias, including when the requested catalog variant is absent.
- A successful catalog lookup establishes that metadata exists. It does not reserve capacity or predict provider availability.
The Models API exposes fields such as context_length, per_request_limits, supported_parameters, default_parameters, and pricing. Pricing can include conditional overrides, so a base entry with zero prices is not enough by itself: the applicable prices for the intended request must also be checked.
Implementation references are provided at the end. API endpoint addresses appear in implementation code, not as browser navigation links.
The decision framework
Classify the result before retrying. The safest first action is usually to preserve the original request and response metadata, then run the preflight checks.
| Observed result | What it indicates | Zero-spend action | Stop condition |
|---|---|---|---|
The exact :free ID is missing or the lookup returns 404 |
The identifier does not resolve as an alias or catalog variant | Recheck the complete ID. Do not replace it with a paid model | Stop the recovery path for that candidate |
| A relevant price or applicable override is not zero | The candidate is outside the zero-spend boundary | Reject it and inspect another preflighted free candidate | Stop if none remains |
| Context or a requested parameter does not fit | The request shape is incompatible | Remove or simplify only the incompatible option, then revalidate | Stop if the task cannot be represented within the free entry |
free_model_daily_requests.remaining is zero |
The daily free counter has no remaining request for the applicable account and endpoint | Do not buy credits or switch models | Stop the daily-free path |
| HTTP 429 with platform rate-limit headers | A platform limit was hit | Record the headers, wait for Retry-After when supplied, otherwise use increasing backoff, then retry the same verified free variant |
Stop if the limit remains after the documented retry window |
| Provider-side 429 metadata | The serving provider is limiting capacity or requests | Wait and retry the same verified free variant; OpenRouter may already have tried other providers for that model | Stop if repeated provider-side failures continue |
| Mid-stream 429-style SSE error | The response began with HTTP 200 and later failed during streaming | Preserve partial text as incomplete, stop reading that stream, and retry after the required delay | Stop if the retry also fails |
HTTP 402 with limit_source |
A credit or spending-budget condition is present | Branch on limit_source; for a transient in-flight-budget condition, wait for Retry-After and retry the same free variant |
Do not add credits or use a paid model; stop for an exhausted key cap or balance condition |
The key endpoint reports account and key credit information and, when the daily gate applies, free_model_daily_requests.used, .limit, and .remaining. It does not report the per-minute limit there. Accounts, endpoints, and BYOK requests may be exempt from the daily gate, so a displayed remaining value is not a live capacity reservation.
A 200 status is not enough to call a streamed request complete. A rate limit after streaming starts arrives as an SSE event with finish_reason: "error" because the HTTP status was already sent.
Preflight: verify before sending
- Preserve the original request. Record the full model ID, task or messages, requested parameters, stream setting, timestamp, and any text already received. This prevents a retry from silently changing the task.
- Resolve the exact ID. Use the single-model lookup for the complete identifier. Confirm that the returned
idmatches the full ID, including:free. - Check pricing. Confirm that the relevant
prompt,completion, andrequestprices are"0". Inspect applicable pricing overrides as well as the top-level prices. Do not infer zero pricing from a paid base entry. - Check context. Compare the input token count with
context_length. The remaining context iscontext_length - input_tokens. If that value is zero or negative, shorten the request or stop; do not add a paid model. - Check the output ceiling. If
max_tokensis requested, the effective ceiling ismin(max_tokens, context_length - input_tokens). This is a ceiling, not a guarantee that the model will use the full allowance. - Check parameters. Compare the requested options with
supported_parametersanddefault_parameters. Remove only an unsupported option, then revalidate the complete request. - Check the daily allowance when available. If
usedis 49 andlimitis 50, one request remains:50 - 49 - 1 = 0after that request is recorded. Ifremainingis zero, stop rather than bypassing the boundary. Exempt requests may not be gated by this counter. - Send one candidate. Use the official request examples for HTTP syntax. Do not send several candidates concurrently, and do not broaden the list after a failure.
Different models tokenize text differently, so the same input can produce different token counts. Tokenization matters to context fit even when the selected free entry has zero token prices.
Recovery workflow and failure handling
1. Preserve and classify. Save the original request and classify the result before retrying. Do not immediately resend a request merely because it failed.
2. Handle a mid-stream error first. If streaming started, inspect the SSE events for finish_reason: "error". A later error invalidates the stream even though the initial HTTP status was 200. Save any partial text, label it incomplete, and stop reading that stream. Retry from the original request rather than treating the partial text as a complete answer.
3. Handle a non-streaming error. Record the status, response headers, and error body. If the exact catalog entry is missing, stop for that candidate. If the entry exists, revalidate its pricing, context, and parameters before deciding whether the error is transient.
4. Handle platform 429. Preserve X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset. If Retry-After is present, wait for it. If it is absent, use increasing backoff delays rather than immediately resending. OpenRouter documents exponential backoff for 429 responses. Retry the same verified free variant after the delay.
5. Handle provider 429. Provider-side capacity or request limits can produce a 429. When provider_code is available, preserve it as evidence of the upstream provider error. OpenRouter may automatically try other providers for the same model before returning the error. Do not switch to a different model or paid variant to clear this condition. Wait and retry the same verified free variant.
6. Handle unsupported parameters. Compare the request with the returned entry's supported_parameters. Remove or simplify only the incompatible option. A parameter error is not evidence that the free variant has disappeared, and adding a parameter to make a fallback appear compatible is outside this workflow.
7. Handle 402 by metadata. Inspect error.metadata.limit_source, not the wording of remedy_hint. A negative account balance can produce 402 even for a free model. An exhausted key cap is not cleared by retrying. A transient openrouter_in_flight_budget condition can include Retry-After; wait and retry the same free variant. Do not add credits or switch to a paid variant. For a request-size problem, reduce the prompt or max_tokens only when doing so preserves the task.
8. Verify completion. For a non-streaming response, confirm the intended model ID and a complete answer. For a stream, confirm that the stream ended normally and that no later SSE error occurred. A 200 status alone is not completion evidence.
9. Stop when no free candidate remains usable. If all preflighted free candidates fail, preserve the evidence and stop. There is no guaranteed free fallback.
Worked scenarios
These scenarios are hypothetical examples, not predictions, model comparisons, or performance benchmarks.
Scenario 1: one daily request remains
Suppose the key endpoint reports used = 49 and limit = 50. The arithmetic leaves one request. A request then returns HTTP 429 with X-RateLimit-Remaining: 0 and X-RateLimit-Reset. The client records the headers, waits until the reset or the supplied retry time, rechecks the exact free entry and daily counter, and sends one request. A successful response ends the recovery attempt; another 429 returns to the wait-and-retry branch.
Scenario 2: a provider fails during a stream
Suppose the HTTP response begins with 200 and the stream emits partial text before an SSE event reports finish_reason: "error" with a 429-style error. The client saves the partial text but marks it incomplete, stops reading that stream, and retries the original request against the same verified free variant after the required delay. It does not concatenate the partial text with the retry as though the first stream had completed.
Scenario 3: an unsupported parameter
Suppose a free entry advertises max_tokens and temperature but not response_format. The original request includes all three. The client removes only response_format, preserves the rest of the task, revalidates the entry, and retries the same free variant. It does not add a paid model or add parameters to make another candidate appear compatible.
Scenario 4: a 402 appears
Suppose a free-model request returns 402 and limit_source identifies an exhausted key cap. Retrying will not clear that condition. For a zero-spend run, the client does not add credits or switch to a paid variant and stops. If the metadata instead identifies a transient in-flight-budget condition with Retry-After, the client waits and retries the same verified free variant.
Hypothetical local fallback list
The following is an example of how a local allowlist could be organized. It is not a recommendation or a claim about current OpenRouter availability. example/a:free, example/b:free, and example/c:free are placeholders for exact IDs; none is asserted to exist or be available.
| Order | Placeholder | Required preflight result | Retry rule |
|---|---|---|---|
| 1 | example/a:free |
Exact entry exists; applicable prices are zero; context and parameters fit | Try first |
| 2 | example/b:free |
Exact entry exists; applicable prices are zero; context and parameters fit | Try only after candidate 1 fails or times out |
| 3 | example/c:free |
Exact entry exists; applicable prices are zero; context and parameters fit | Try only after candidates 1 and 2 fail or time out |
For each candidate, perform the lookup before sending a request. Keep only entries whose applicable prices are zero and whose context and parameters fit. If a candidate is missing, has a nonzero applicable price, or rejects the requested parameters, reject it and move to the next candidate. Send candidates sequentially, stop at the first complete success, and never expand the list to a paid model.
Limitations
- Free availability is not guaranteed. A free entry can disappear or become unavailable while its catalog metadata remains visible.
- A catalog lookup is metadata evidence, not a live capacity reservation.
- Free variants can have different limits, context lengths, endpoints, and availability from paid versions.
- Platform limits and provider capacity can both produce 429. Headers and metadata distinguish the recovery paths.
- The daily counter does not expose the per-minute limit, and exempt requests may not be gated by it.
- A 402 can reflect account or key credit state even when the selected model is free. The in-flight spending budget itself does not apply to free-model requests, but account-level credit state can still affect them.
- No free fallback is guaranteed. A local allowlist can reject every candidate without producing a usable response.
- Provider routing preferences can change which providers are eligible, but they do not guarantee performance or availability.
Concrete verification checklist
Before each attempt:
- The full model ID is recorded, including
:free. - The single-model lookup returns the same ID.
- Relevant
prompt,completion,request, and applicable override prices are zero. - Context length is greater than the input token count.
- The requested output ceiling is nonnegative and compatible with the remaining context.
- Requested parameters are supported or have been removed.
-
free_model_daily_requestsis checked when available; exhausted allowance stops the path. - The request shape is checked against the official request examples.
- The candidate is sent only after all earlier checks pass.
After a failure:
- The exact status is recorded.
-
X-RateLimit-Limit,X-RateLimit-Remaining,X-RateLimit-Reset, andRetry-Afterare recorded when present. -
provider_codeis recorded when present. -
limit_sourceis recorded for a 402. - Stream state is recorded, including any
finish_reason: "error"event. - Partial text is labeled incomplete and is not treated as a complete answer.
- The next action remains inside the verified free-only boundary.
- If every candidate fails, the loop stops without adding credits or a paid model.
Official implementation references
- Free model variants
- Models API and model metadata
- Limits and quotas
- Provider routing
- Request examples
The endpoint addresses and request shapes belong in implementation references, not in browser navigation links.
Research and limitations
This guide was prepared with AI assistance using the official sources linked in the text, automated checks and a separate model review. It is not a claim of hands-on testing or human expert verification. Provider terms can change; check the linked documentation before making a commitment. Examples identified as hypothetical are not measured benchmarks. Tell us about an error.
