OpenRouter Rate Limits, Retries, and Backoff Strategies
Agents multiply HTTP calls: each turn can mean one or more model requests plus tools.
Search across all documentation pages
Agents multiply HTTP calls: each turn can mean one or more model requests plus tools.
Without disciplined retries, a single 429 storm becomes a thundering herd, a cost spike, or a stuck loop.
This page is a practical recipe for rate limits and transient errors when OpenRouter is your model gateway.
Retry idempotent model calls on 429 and selected 5xx with exponential backoff and jitter; cap attempts; prefer model/provider fallback after budgeted retries; never blindly re-execute side-effecting tools.
| Class | Examples | Default action |
|---|---|---|
| Rate limit | HTTP 429 | Backoff + retry; consider fallback model |
| Transient server | 500, 502, 503, 504 | Backoff + retry |
| Timeout | client timeout | Retry only if safe; else fail or fallback |
| Auth / config | 401, 403 | Do not retry; alert |
| Bad request | 400 on schema | Do not retry same payload |
| Insufficient credits | payment errors | Do not retry; page on-call |
Exact codes and bodies can vary - parse carefully and verify against current OpenRouter docs at build.
Example policy: max 4 attempts, max 20s total wait, deadline for the whole agent turn.
sleep = min(cap, base * 2**attempt) * Uniform(0.5, 1.5) (or full jitter variant).
Retry-After when presentIf the response includes a retry delay header, prefer it over your pure exponential guess (when sane and capped).
Tools that charge cards or send email need idempotency keys or "execute once" semantics.
Switch to a secondary OpenRouter model or provider preference instead of infinite retries on one slug.
Under global 429 rates, reduce concurrency, queue noncritical jobs, and return degraded responses.
import os
import random
import time
from openai import OpenAI, RateLimitError, APIStatusError, APITimeoutError
client = OpenAI(
base_url="https://openrouter.ai/api/v1",
api_key=os.environ["OPENROUTER_API_KEY"],
)
RETRY_STATUS = {429, 500, 502, 503, 504}
def complete_with_backoff(
*,
messages,
model: str,
fallback_models: list[str] | None = None,
tools=None,
max_attempts: int = 4,
base_delay: float = 0.5,
max_delay: float = 8.0,
):
models = [model] + (fallback_models or [])
last_err = None
for m in models:
delay = base_delay
for attempt in range(max_attempts):
try:
return client.chat.completions.create(
model=m,
messages=messages,
tools=tools,
)
except RateLimitError as e:
last_err = e
except APITimeoutError as e:
last_err = e
except APIStatusError as e:
last_err = e
if getattr(e, "status_code", None) not in RETRY_STATUS:
raise
if attempt == max_attempts - 1:
break # try next model
sleep_for = min(max_delay, delay) * random.uniform(0.5, 1.5)
time.sleep(sleep_for)
delay *= 2
raise RuntimeError(f"exhausted_retries: {last_err!r}")Tune limits per environment; production services should also bound total agent wall-clock time outside this helper.
A single user message might trigger 5-15 model calls (plan, act, repair, summarize).
Concurrency across tenants multiplies that further.
Design limits at three layers:
OpenRouter aggregates many providers; limits can come from your OpenRouter account/key, free-model tiers, or upstream provider capacity.
A 429 might mean "slow down this key" or "that model/provider is hot."
Falling back to another model slug often recovers faster than hammering the same one.
Free and heavily discounted models typically have stricter rate limits - do not put them on your hottest agent path without caps.
Spend limits and key credits are related but not identical to RPM limits: a paid key can still 429 under burst traffic.
Verify current limit behavior and headers in OpenRouter documentation at build time.
| Strategy | Behavior | Use |
|---|---|---|
| Fixed delay | Always wait N seconds | Simple scripts only |
| Exponential | 0.5, 1, 2, 4... | Default server-side friendliness |
| Exponential + jitter | Randomized | Multi-instance prod (recommended) |
| Immediate fallback | Switch model first | When alternate capacity exists |
Request-level model lists (when configured) can fail over inside one HTTP call path.
You still want client-side retries for connection drops and some 429s.
Avoid stacking unbounded retries on top of unbounded fallbacks - product of both is your true worst-case cost.
Log: status code, attempt, sleep_ms, model, fallback index, run_id.
Alert on: rising 429 rate, retry budget exhaustion, fallback share.
Dashboard cost separately for retries so "invisible" duplicate calls show up.
A thread pool of 100 agents each retrying 4 times will amplify outages.
Use bounded executors, bulkheads per tenant, and optional token buckets before you call OpenRouter.
| Approach | Strength | Weakness | Best fit |
|---|---|---|---|
| Client backoff only | Simple | Single model still saturates | Low QPS apps |
| Backoff + model fallback | Higher uptime | Quality variance | Production agents |
| Queue + workers | Smooths bursts | Latency added | Batch / async agents |
| Provider direct with SLA | Contractual limits | Multi-provider complexity | Enterprise single-vendor |
| Local Ollama shed | Absorbs some load | Quality/ops trade-off | Hybrid deployments |
No.
Retry transport/rate-limit/transient classes only. Fail fast on auth, validation, and policy errors.
Often 3-5 attempts with capped backoff. Beyond that, fallback or fail and let the user retry.
It is the standard friendly default under 429. Fixed short retries can worsen herd effects at scale.
Randomization of sleep duration so concurrent clients do not align their retries on the same clock.
Sometimes for multi-model setups with spare capacity. A common pattern is 1-2 retries on primary, then fallback, then limited retries on secondary.
A dropped stream may need a full retry. Design UX for partial output and ensure you do not double-apply tools already executed from partial tool calls.
No.
Bigger models may even be tighter on capacity. Fix concurrency, caching, cheaper tiers for easy steps, and backoff.
In the gateway/client layer shared by all agents, not copy-pasted into each tool node.
Some have hooks; do not assume they match your policy. Explicit client policy beats unknown defaults.
Lower concurrency, enable stricter shed, switch default model or provider order, verify credits, and communicate degraded mode.
Stack versions: Pins from the category manifest (verify at build): OpenRouter (~315+ models, July 2026 pricing/fees); LangGraph 1.0+; CrewAI 1.14+; Microsoft Agent Framework 1.0; Vercel AI SDK 6; Pydantic AI (latest); LlamaIndex (latest); OpenAI Agents SDK (latest + MCP); MCP (Linux Foundation governance); A2A (HTTP+SSE+JSON-RPC 2.0); Solana
@solana/web3.js+@solana/spl-token.
Reviewed by Chris St. John·Last updated Jul 16, 2026