Last updated: July 2026
A successful API response during development does not prove that an AI application is ready for production.
Once real users arrive, an application must handle traffic spikes, large prompts, slow generations, provider rate limits, temporary outages, malformed responses and model-specific failures. Even a reliable model API can return a timeout, a 429 Too Many Requests response or a temporary 5xx server error.
The difference between a prototype and a production AI system is not whether failures happen.
It is whether the application can recover without creating duplicate requests, uncontrolled retry loops, long user waits or silent data loss.
A resilient LLM integration should:
- Set explicit connection and response timeouts.
- Retry only transient failures.
- Use exponential backoff with random jitter.
- Respect provider retry instructions when available.
- Limit the total number of attempts.
- Prevent duplicate business actions.
- Maintain a tested fallback route.
- Validate model output after a successful HTTP response.
- Stop sending traffic to persistently unhealthy routes.
- Monitor reliability by model and task type.
OpenAI, Anthropic and Gemini expose different error formats and rate-limit rules, but the production principles are largely the same. Anthropic's official SDKs, for example, automatically retry certain connection errors, rate limits and server errors with exponential backoff, while Gemini recommends waiting and retrying after 429 RESOURCE_EXHAUSTED errors. ([Claude Platform Docs][1])
LumeAPI can simplify a multi-model architecture by exposing supported GPT, Claude and Gemini models through one OpenAI-compatible endpoint and one API key. This makes it easier to implement application-level retries, model fallbacks and controlled failover without maintaining a completely different client for each model family. ([LumeAPI Docs][2])
Quick Answer
Use this general policy for LLM API failures:
| Failure | Retry? | Recommended action |
|---|---|---|
| Connection error | Usually | Retry with exponential backoff |
| Request timeout | Sometimes | Retry only when the operation is safe |
408 Request Timeout | Usually | Retry with backoff |
429 Too Many Requests | Yes | Respect Retry-After, reduce concurrency |
500 Internal Server Error | Usually | Retry a limited number of times |
502 Bad Gateway | Usually | Retry or use a tested fallback |
503 Service Unavailable | Usually | Retry, then open circuit if persistent |
504 Gateway Timeout | Usually | Retry carefully |
400 Bad Request | Usually not | Correct request parameters |
401 Unauthorized | No | Fix the API key |
403 Forbidden | No | Fix permissions or account access |
404 Model Not Found | No | Verify the model ID |
| Context-length error | No | Reduce or summarize the prompt |
| Invalid model output | Not as transport retry | Validate, repair or escalate |
A good starting configuration is:
Connection timeout: 5–10 seconds
Overall request timeout: workload dependent
Maximum attempts: 3
Initial retry delay: 0.5–1 second
Maximum retry delay: 15–30 seconds
Backoff: exponential with jitterThese are starting points, not universal settings. A real-time chatbot, coding agent and asynchronous report generator should not share the same timeout policy.
Why LLM APIs Fail in Production
An LLM request depends on more than the model.
A typical request travels through:
Application
→ Network
→ DNS
→ Load balancer
→ API gateway
→ Provider infrastructure
→ Model-serving capacity
→ Streaming connection
→ Application parserA failure can occur at any layer.
Common causes include:
- Temporary network interruption
- DNS failure
- TLS connection error
- Client-side timeout
- Provider overload
- Requests-per-minute limits
- Tokens-per-minute limits
- Daily or spend-based quotas
- Excessive prompt length
- Unsupported parameters
- Invalid model IDs
- Model deprecation
- Streaming connection interruption
- Invalid JSON or tool-call output
- Downstream database or tool failure
Gemini documents 429 RESOURCE_EXHAUSTED for exceeded request, token, daily or spending limits. Its troubleshooting guide recommends verifying the active limits, waiting before retrying, reducing the request rate or size and requesting higher limits when required. ([Google AI for Developers][3])
Anthropic similarly returns a 429 response when an organization exceeds a rate limit and provides a retry-after header indicating how long the client should wait. ([Claude Platform Docs][4])
The application should therefore avoid treating every failure as the same generic exception.
Transport Success Is Not Task Success
An API can return HTTP 200 while the task still fails.
Examples include:
- Empty content
- Truncated output
- Invalid JSON
- Missing required fields
- Incorrect tool arguments
- Unsupported citations
- A refusal
- A response in the wrong language
- Code that does not compile
- An answer that contradicts retrieved evidence
Production reliability should be divided into two categories.
Transport Reliability
Did the API request complete successfully?
Measure:
- Connection-error rate
- Timeout rate
429rate5xxrate- Streaming interruption rate
Semantic Reliability
Did the returned response complete the intended task?
Measure:
- Schema-validity rate
- Tool-call success
- Task-completion rate
- Factual-validation rate
- Code-test pass rate
- Human acceptance rate
- Escalation rate
A robust application needs both.
Understanding Timeouts
A timeout is not one single event.
Different timeout types occur at different stages of a request.
Connection Timeout
The client could not establish a connection within the allowed period.
Possible causes:
- Network failure
- DNS issue
- Provider endpoint unavailable
- Firewall or proxy problem
Connection timeouts are commonly safe to retry because the request may never have reached the provider.
Read Timeout
A connection was established, but the application did not receive data quickly enough.
This can happen when:
- The model is slow
- The prompt is large
- The requested output is long
- A reasoning model takes longer
- The provider is overloaded
- The streaming connection stops producing data
A read timeout is more complicated because the provider may already be processing the request.
Overall Deadline
A production application should have a total task deadline in addition to an individual request timeout.
For example:
Total user deadline: 30 seconds
Attempt 1:
Maximum 15 seconds
Retry delay:
1 second
Attempt 2:
Maximum 10 seconds
Fallback:
Remaining 4 secondsWithout a total deadline, several individually reasonable retries can create an unacceptable wait.
Set Timeouts by Workload
Real-Time Chat
The user expects a response quickly.
Possible targets:
Connection timeout: 5 seconds
Time to first token target: 2–5 seconds
Overall deadline: 20–40 secondsCoding or Document Analysis
The task may need longer processing.
Connection timeout: 5–10 seconds
Overall deadline: 60–180 secondsBackground Processing
A worker can tolerate a longer request, but it still needs a deadline to prevent stuck jobs.
Per-attempt timeout: 2–5 minutes
Job deadline: 10–30 minutesDo not set a very large timeout merely to avoid handling errors. Long timeouts can keep application workers occupied and allow queues to grow during provider incidents.
Understanding 429 Rate-Limit Errors
A 429 response means the client has exceeded a provider or gateway limit.
The limit may apply to:
- Requests per minute
- Tokens per minute
- Requests per day
- Input tokens
- Output tokens
- Concurrent requests
- Spending over a time window
- A specific model
- An organization or project
- A usage tier
A 429 does not always mean the application sent too many HTTP requests.
One large prompt can consume enough tokens to exceed a token-based limit even when request volume is low.
Gemini also documents spending-based limits for applicable usage tiers. When exceeded, the API returns 429 RESOURCE_EXHAUSTED; Google recommends waiting, reducing expensive request volume or requesting an increase. ([Google AI for Developers][3])
Anthropic's rate-limit documentation notes that a 429 response identifies the exceeded limit and includes a retry delay. ([Claude Platform Docs][4])
How to Respond to 429 Errors
A production client should:
- Read the provider's retry delay when available.
- Stop immediate retries.
- Reduce request concurrency.
- Apply exponential backoff.
- Add random jitter.
- Avoid retrying the same request from multiple workers.
- Monitor which model or account limit was exceeded.
- Route to a tested alternative only when appropriate.
A bad retry pattern looks like this:
while True:
try:
return call_model()
except Exception:
continueThis creates a retry storm.
If the provider is already overloaded, immediate unlimited retries make the incident worse.
Exponential Backoff
Exponential backoff increases the delay after each failure.
A basic schedule might be:
Attempt 1 fails
→ Wait about 1 second
Attempt 2 fails
→ Wait about 2 seconds
Attempt 3 fails
→ Wait about 4 seconds
Attempt 4 fails
→ Wait about 8 secondsThe simplified formula is:
delay = initial_delay × 2^retry_numberAlways place an upper limit on the delay.
Why Jitter Matters
If thousands of requests fail at the same time and every client retries after exactly two seconds, they will all return together.
This is called the thundering-herd problem.
Jitter adds randomness:
Calculated delay: 4 seconds
Actual delay: random value between 2 and 6 secondsThis spreads requests over time and gives the provider more opportunity to recover.
Respect Retry-After
When an API provides Retry-After or equivalent structured retry information, use it as the minimum waiting period.
Conceptually:
delay = max(
provider_retry_after,
calculated_backoff_with_jitter,
)Do not ignore an explicit provider delay and retry immediately.
Which Errors Should Be Retried?
Retrying is useful only when the failure may be temporary.
Usually Retryable
Common transient failures include:
408 Request Timeout
429 Too Many Requests
500 Internal Server Error
502 Bad Gateway
503 Service Unavailable
504 Gateway Timeout
Connection reset
Temporary DNS failure
Temporary network failureAnthropic's official SDKs automatically retry certain connection errors, 429 responses and 5xx failures using exponential backoff, illustrating the kinds of errors generally treated as transient. ([Claude Platform Docs][1])
Usually Not Retryable
Common permanent request failures include:
400 Invalid Request
401 Invalid API Key
403 Permission Denied
404 Unknown Endpoint or Model
Unsupported parameter
Context length exceeded
Malformed JSON request
Invalid image or file format
Insufficient account balanceA retry with the same request will probably fail again.
The application should correct the request, reduce its size, use a supported model or notify the responsible operator.
Context-Length Failures
A prompt that exceeds a model's context window should not be retried unchanged.
Instead:
- Remove irrelevant conversation history
- Summarize older turns
- Retrieve fewer documents
- Shorten tool output
- Split a large task
- Select a model with an appropriate context window
- Limit expected output tokens
The application should estimate token usage before sending very large requests.
Do Not Retry Every 400 Error Blindly
Most 400 errors indicate a client problem.
However, real provider implementations can occasionally classify transient problems differently. The safest architecture is:
- Follow the provider's documented error semantics.
- Log the complete error code and message.
- Analyze repeated production cases.
- Add narrow exceptions only when evidence supports them.
- Never treat all
400responses as retryable.
Broad exception rules can hide genuine request bugs.
Build an Explicit Retry Policy
A retry policy should define:
- Retryable status codes
- Retryable exception types
- Maximum attempts
- Initial delay
- Maximum delay
- Total task deadline
- Whether the operation is idempotent
- Whether fallback is allowed
- Which model should receive the fallback
- What happens after final failure
Example:
RETRYABLE_STATUS_CODES = {
408,
429,
500,
502,
503,
504,
}
MAX_ATTEMPTS = 3
INITIAL_DELAY_SECONDS = 0.75
MAX_DELAY_SECONDS = 12Do not scatter retry logic across application features. Create one tested request layer.
Python Retry Example With LumeAPI
LumeAPI publishes an OpenAI-compatible base URL for supported models:
https://api.lumeapi.site/v1Developers can use the OpenAI Python client with a LumeAPI key and an exact catalog model ID. ([LumeAPI Docs][2])
The following example implements:
- Explicit timeouts
- Limited retries
- Exponential backoff
- Jitter
- Retryable status detection
- A total deadline
- Clear final failure
import os
import random
import time
from dataclasses import dataclass
from typing import Any
from openai import (
APIConnectionError,
APIStatusError,
APITimeoutError,
OpenAI,
)
RETRYABLE_STATUS_CODES = {
408,
429,
500,
502,
503,
504,
}
@dataclass(frozen=True)
class RetryPolicy:
max_attempts: int = 3
initial_delay_seconds: float = 0.75
maximum_delay_seconds: float = 12.0
total_deadline_seconds: float = 45.0
client = OpenAI(
api_key=os.environ["LUMEAPI_KEY"],
base_url="https://api.lumeapi.site/v1",
timeout=20.0,
max_retries=0, # Avoid stacking SDK and application retries.
)
def calculate_delay(
retry_number: int,
policy: RetryPolicy,
) -> float:
exponential_delay = (
policy.initial_delay_seconds * (2 ** retry_number)
)
capped_delay = min(
exponential_delay,
policy.maximum_delay_seconds,
)
# Full jitter: choose a random delay from zero to the cap.
return random.uniform(0, capped_delay)
def is_retryable_status(error: APIStatusError) -> bool:
return error.status_code in RETRYABLE_STATUS_CODES
def create_completion(
*,
model: str,
messages: list[dict[str, Any]],
policy: RetryPolicy = RetryPolicy(),
) -> str:
started_at = time.monotonic()
last_error: Exception | None = None
for attempt in range(policy.max_attempts):
elapsed = time.monotonic() - started_at
if elapsed >= policy.total_deadline_seconds:
raise TimeoutError(
"The total LLM request deadline was exceeded."
) from last_error
try:
response = client.chat.completions.create(
model=model,
messages=messages,
temperature=0.2,
max_tokens=1_500,
)
content = response.choices[0].message.content
if not content:
raise ValueError(
"The model returned an empty response."
)
return content
except APIStatusError as error:
last_error = error
if not is_retryable_status(error):
raise
except (APIConnectionError, APITimeoutError) as error:
last_error = error
if attempt == policy.max_attempts - 1:
break
delay = calculate_delay(attempt, policy)
remaining = (
policy.total_deadline_seconds
- (time.monotonic() - started_at)
)
if remaining <= 0:
break
time.sleep(min(delay, remaining))
raise RuntimeError(
f"LLM request failed after {policy.max_attempts} attempts."
) from last_errorSetting max_retries=0 on the SDK is intentional in this example.
Some SDKs already retry transient errors. Adding another application retry layer without understanding SDK behavior can multiply the number of requests.
For example:
SDK attempts: 3
Application attempts: 3
Possible total attempts:
3 × 3 = 9Choose one retry owner or calculate the combined behavior deliberately.
Add Retry-After Support
When the error response includes a retry delay, use it.
The exact method for reading headers depends on the SDK version and response object, but the policy should follow this structure:
def choose_retry_delay(
*,
provider_delay: float | None,
calculated_delay: float,
) -> float:
if provider_delay is None:
return calculated_delay
return max(provider_delay, calculated_delay)Cap the final delay when necessary to remain inside the task's overall deadline.
Idempotency and Duplicate Requests
Retries can create duplicate work.
Imagine this sequence:
Application sends request
→ Provider completes generation
→ Network connection fails before response reaches application
→ Application retries
→ Provider processes the same request againFor a text answer, duplicate generation mainly wastes money.
For a workflow that triggers an action, the risk is larger:
- Sending the same email twice
- Creating two support tickets
- Charging a customer twice
- Publishing duplicate content
- Executing the same database update twice
- Creating multiple video-generation tasks
The safest architecture separates inference from side effects:
Model request
→ Validate result
→ Store result
→ Check operation state
→ Execute business action onceEvery business operation should have a stable idempotency key.
Example:
task_id = order_4815_refund_reviewBefore performing the side effect:
Has task_id already been completed?
Yes
→ Return existing result
No
→ Apply action and mark completedDo not assume a timed-out model request definitely failed.
Retrying Streaming Requests
Streaming improves perceived latency, but failure handling becomes harder.
A stream can fail:
- Before any tokens arrive
- After a few tokens
- Near the end
- After a tool call is partially generated
If failure happens before any user-visible output, retrying may be straightforward.
If partial output has already been displayed, retrying can create:
- Repeated text
- Contradictory continuation
- Broken markdown
- Duplicate tool calls
- Confusing user experience
Possible strategies include:
#### Restart the Entire Response
Clear the partial response and start again.
Best when the interface can visibly indicate that generation is restarting.
#### Continue From Saved Partial Output
Send the accepted partial output back to a model and request continuation.
This is more complex and may produce style inconsistencies.
#### Keep the Partial Output
Show a recovery action:
The response was interrupted.
[Continue] [Try again]For tool-using agents, never execute a partially streamed tool call. Wait until the complete structured arguments pass validation.
Fallback Routing
Retries and fallbacks solve different problems.
Retry
Try the same route again because the failure may be temporary.
Claude Sonnet
→ temporary 503
→ retry Claude SonnetFallback
Send the request to a different tested route.
Primary route unavailable
→ fallback modelA fallback may use:
- A different model from the same family
- A different model family
- A different provider
- A lower-capability emergency route
- A stronger escalation route
A fallback is not automatically equivalent to the primary model.
Different models may behave differently in:
- Tool calling
- JSON generation
- System-prompt adherence
- Safety policies
- Output length
- Citation behavior
- Multilingual responses
- Reasoning controls
- Context handling
Every fallback must pass the same application-level evaluation as the primary route.
For routing strategy depth, see the [Model routing guide](/research/route-requests-gpt-claude-gemini-quality-speed-reliability-lumeapi).
Why a Unified Gateway Helps
Direct multi-provider failover often requires:
OpenAI client
+ Anthropic client
+ Gemini client
+ separate authentication
+ separate billing
+ response normalization
+ model-name translationLumeAPI provides supported GPT, Claude and Gemini models through one OpenAI-compatible gateway, with one API key and wallet-based billing. Its documentation also includes text, image and asynchronous video endpoints. ([LumeAPI Docs][2])
This can simplify fallback code:
MODEL_ROUTES = [
"gpt-5.6-terra",
"claude-sonnet-4-6",
"gemini-3.5-flash",
]The client configuration remains the same while the model ID changes.
This reduces integration work, but it does not create complete infrastructure independence. Models accessed through one gateway still share parts of the gateway path.
For maximum resilience, a high-value production system may use:
Primary:
LumeAPI unified route
Emergency fallback:
Direct official provider integrationor the reverse:
Primary:
Official provider
Cost-efficient or secondary route:
LumeAPIThe correct architecture depends on the application's reliability, compliance and feature requirements.
Fallback Example
The following code retries a primary model and then moves to one tested fallback.
from dataclasses import dataclass
from typing import Any
@dataclass(frozen=True)
class ModelRoute:
name: str
model_id: str
PRIMARY = ModelRoute(
name="primary",
model_id="claude-sonnet-4-6",
)
FALLBACK = ModelRoute(
name="fallback",
model_id="gpt-5.6-terra",
)
def complete_with_fallback(
messages: list[dict[str, Any]],
) -> str:
routes = [PRIMARY, FALLBACK]
errors: list[str] = []
for route in routes:
try:
return create_completion(
model=route.model_id,
messages=messages,
policy=RetryPolicy(
max_attempts=2,
total_deadline_seconds=25,
),
)
except Exception as error:
errors.append(
f"{route.name}:{type(error).__name__}"
)
raise RuntimeError(
"All configured model routes failed: "
+ ", ".join(errors)
)In production, log more context internally, but do not expose API keys, raw prompts or sensitive provider error details to end users.
Do Not Use Long Fallback Chains
A chain such as:
Model A
→ Model B
→ Model C
→ Model D
→ Model Ecan produce extreme latency and repeated charges.
A practical system usually needs:
- One primary route
- One equivalent fallback
- Possibly one controlled escalation route
After that, return a recoverable error or move the task to a background queue.
Circuit Breakers
When a route fails repeatedly, continuing to send every request to it wastes time.
A circuit breaker temporarily stops traffic.
It usually has three states.
Closed
The route is considered healthy.
Requests are allowed.
Open
The failure threshold has been exceeded.
Requests bypass the route and use a fallback or return an error.
Half-Open
After a cooldown period, the system allows a small number of test requests.
If they succeed, the circuit closes. If they fail, it opens again.
Conceptual flow:
Closed
→ repeated failures
→ Open
Open
→ cooldown
→ Half-open
Half-open
→ success
→ Closed
Half-open
→ failure
→ OpenA circuit breaker should consider rolling metrics such as:
- Minimum request count
- Error percentage
- Consecutive failures
429rate5xxrate- Timeout rate
- P95 latency
Do not open a circuit because of one isolated failure.
Circuit-Breaker Example
import time
from dataclasses import dataclass, field
@dataclass
class CircuitBreaker:
failure_threshold: int = 5
cooldown_seconds: float = 30.0
failures: int = 0
opened_at: float | None = field(default=None)
def allow_request(self) -> bool:
if self.opened_at is None:
return True
elapsed = time.monotonic() - self.opened_at
if elapsed >= self.cooldown_seconds:
# Allow one probe request.
return True
return False
def record_success(self) -> None:
self.failures = 0
self.opened_at = None
def record_failure(self) -> None:
self.failures += 1
if self.failures >= self.failure_threshold:
self.opened_at = time.monotonic()A distributed application needs shared circuit state in Redis, a database or a dedicated resilience layer rather than separate counters inside each worker.
Concurrency Control
Retries are not enough when the application is exceeding capacity.
Control the number of simultaneous requests with:
- Semaphores
- Worker queues
- Per-model concurrency limits
- Token-bucket rate limiting
- Leaky-bucket rate limiting
- Priority queues
- Admission control
Example:
import asyncio
model_semaphore = asyncio.Semaphore(20)
async def limited_model_call(callable_request):
async with model_semaphore:
return await callable_request()A concurrency limit protects:
- Provider quotas
- Application memory
- Worker capacity
- Downstream databases
- User latency during traffic spikes
Queue Requests Instead of Rejecting Everything
For non-interactive tasks:
Incoming task
→ Queue
→ Controlled worker pool
→ Model API
→ Result storeThis allows the application to absorb short traffic bursts without immediately exceeding API limits.
Every queued task should include:
- Unique task ID
- Model route
- Prompt version
- Attempt count
- Creation time
- Deadline
- Priority
- Idempotency key
Expired tasks should not remain in the queue indefinitely.
For async jobs that can wait hours, see the [Batch API guide](/research/batch-api-cut-openai-claude-gemini-api-costs-lumeapi).
Load Shedding
When the system cannot serve all traffic, it may need to reject or degrade lower-priority work.
Examples:
- Disable optional AI summaries
- Reduce the number of generated alternatives
- Shorten maximum output
- Delay analytics jobs
- Route free users to a faster model
- Stop nonessential background evaluations
- Return a cached answer where safe
This is better than allowing every request to become slow and fail.
Output Validation and Repair
After transport success, validate the response.
#### JSON Validation
Use JSON Schema or a typed validation library.
#### Tool-Call Validation
Check:
- Tool name
- Required arguments
- Data types
- Permission rules
- Maximum values
- Whether the tool is allowed for this user
#### Code Validation
Run:
- Parser
- Type checker
- Linter
- Unit tests
- Security checks
#### RAG Validation
Check:
- Required citations
- Source existence
- Evidence support
- Missing evidence
- Unsupported claims
When validation fails, choose among:
Repair with the same model
Escalate to a stronger model
Use a deterministic correction
Request user clarification
Send to human review
Reject the resultDo not automatically retry an invalid response with the exact same prompt many times.
Error Messages for Users
Internal errors and user-facing errors should be different.
Bad:
HTTP 503 upstream_generation_error provider_cluster_7Better:
The AI service is temporarily unavailable.
Your request was not completed. Please try again.For background jobs:
We could not complete this task after several attempts.
It has been moved to the retry queue.Do not claim that a task failed when its completion state is uncertain. For operations with possible duplicate effects, show a "status unknown" state and verify before resubmitting.
What to Log
Every request should produce structured metadata.
Recommended fields:
request_id
task_id
user_tier
route
model_id
prompt_version
started_at
completed_at
status_code
error_type
attempt_number
fallback_used
input_tokens
output_tokens
time_to_first_token
total_latency
validation_result
task_successAvoid logging secrets such as:
- API keys
- Authentication headers
- Passwords
- Private credentials
Whether prompts and responses can be stored depends on the application's privacy policy, user expectations and legal requirements.
LumeAPI's terms describe it as an independent OpenAI-compatible gateway that exposes API keys, wallet billing, catalog pricing and usage logs while outputs are produced by third-party AI providers. Applications should account for this data path when deciding what information may be sent through the service. ([LumeAPI Terms][5])
Reliability Metrics That Matter
Track reliability separately for every model and route.
Availability
Successful transport responses
÷ Total requestsTask Success Rate
Valid completed tasks
÷ Total attempted tasksRetry Rate
Requests requiring an additional attempt
÷ Total requestsFallback Rate
Requests completed through fallback
÷ Total requestsA rising fallback rate can reveal degradation before the primary route fails completely.
P50 and P95 Latency
Average latency can hide slow-user experiences.
P95 shows how slow the worst 5% of requests are.
429 Rate
Track by:
- Model
- Account
- Minute
- Request type
- User tier
- Input-token range
5xx Rate
A sudden increase can indicate provider or gateway instability.
Validation Failure Rate
This catches model-quality problems that HTTP monitoring misses.
Cost per Successful Task
Retries and fallbacks create extra charges.
Use:
Cost per successful task =
All primary, retry and fallback spending
÷ Successful tasksService-Level Objectives
Define measurable targets.
Example:
Transport success: 99.5%
Task success: 97%
P95 latency: under 15 seconds
429 rate: under 0.5%
Fallback rate: under 2%
Invalid JSON rate: under 0.2%Targets should differ by feature.
A live support assistant may need stricter availability than an internal summarization tool.
Provider Status vs Application Health
A public provider status page is useful but insufficient.
Your application can fail while the provider reports normal operation because:
- Your account limit is exhausted
- One model is affected
- One region is affected
- Your prompts are too large
- Your gateway configuration is incorrect
- Your own database is slow
- Your retry logic is broken
Monitor the complete path from application to validated result.
Use synthetic tests:
Every few minutes:
Send a small known request
→ Verify transport
→ Verify response structure
→ Record latencyDo not send synthetic tests so frequently that they create unnecessary cost or quota pressure.
A Practical Resilience Architecture
A production request path might look like this:
Incoming request
→ Validate input
→ Check deadline
→ Apply concurrency limit
→ Select primary model
→ Send request
→ Retry transient failure
→ Validate output
→ Use fallback if allowed
→ Execute side effect idempotently
→ Record metrics
→ Return resultFor an AI agent:
Agent step
→ Model route
→ Tool-call validation
→ Idempotency check
→ Tool execution
→ Save state
→ Next stepPersist state between steps so a worker crash does not restart the entire agent from the beginning.
See the [Production LLM API](/production-llm-api) commercial page for a staged migration checklist.
Production Checklist
Before launching, verify the following.
Timeouts
- Connection timeout is explicit.
- Request timeout is explicit.
- Total task deadline exists.
- Streaming interruption is handled.
- Background jobs have expiration times.
Retries
- Only transient errors are retried.
- Exponential backoff is enabled.
- Jitter is enabled.
- Maximum attempts are limited.
- SDK retries are understood.
- Provider retry delays are respected.
Rate Limits
- Concurrency is controlled.
- Requests and tokens are monitored.
- Queues absorb temporary bursts.
- Traffic can be degraded or shed.
- Quota alerts are configured.
Fallbacks
- The fallback is tested.
- Prompts work on the fallback model.
- Output validation is consistent.
- A circuit breaker exists.
- Fallback loops are impossible.
- Emergency direct-provider access is considered.
Data Integrity
- Idempotency keys are used.
- Side effects are separated from generation.
- Task state is persisted.
- Duplicate results are detected.
- Partial streaming output cannot trigger tools.
Observability
- Every request has an ID.
- Attempts and fallbacks are logged.
- P50 and P95 latency are measured.
429and5xxerrors are tracked.- Validation failures are measured.
- Cost per successful task is calculated.
When to Use LumeAPI
LumeAPI may be a useful production route when a team wants:
- One OpenAI-compatible endpoint
- One API key
- Access to supported GPT, Claude and Gemini models
- Easier application-level model switching
- Unified wallet billing
- Transparent model catalog pricing
- Text, image and asynchronous video APIs
- Lower standard real-time prices for supported models
Its current documentation provides the shared base URL, model catalog and model-specific request examples. ([LumeAPI Docs][2])
However, a third-party gateway should not be treated as a substitute for every official provider capability.
Use direct provider APIs when the application requires:
- Provider-native hosted tools
- Native caching or batch features
- Direct enterprise support
- Specific data-processing agreements
- Specialized compliance controls
- Immediate access to new models
- Provider-specific real-time or multimodal features
A hybrid architecture is often stronger than dependence on one route.
Frequently Asked Questions
Should I retry every failed LLM request?
No. Retry temporary network errors, 429 responses and selected 5xx failures. Do not repeatedly retry invalid API keys, malformed requests, unsupported parameters or context-length errors.
How many retries should an LLM API use?
Two or three total attempts are a reasonable starting point for many real-time workloads. The correct number depends on the user deadline, provider behavior and whether requests are safe to repeat.
What is exponential backoff?
It increases the waiting period after every failed attempt. Adding jitter randomizes the delay so many clients do not retry simultaneously.
What should I do after a 429 error?
Read the provider's retry information, wait, reduce concurrency and retry only after an appropriate delay. If the limit is persistent, reduce request size or frequency, increase quota or use a tested alternative route.
Should I retry a timeout?
Sometimes. A connection timeout is commonly safe to retry. A read timeout may occur after the provider began processing the request, so idempotency and duplicate-work risks must be considered.
What is an LLM fallback?
A fallback is a tested alternative model or provider used when the primary route cannot complete the request.
Is fallback the same as retry?
No. A retry repeats a route after a temporary failure. A fallback changes the model, provider or route.
Can I automatically switch from GPT to Claude or Gemini?
Yes, but the fallback must be evaluated for the same task. Model families can differ in prompt behavior, tool calling, structured output and safety behavior.
Does an OpenAI-compatible API guarantee identical model behavior?
No. It standardizes part of the request and response format. It does not make GPT, Claude and Gemini behave identically.
Does LumeAPI automatically provide failover?
LumeAPI provides unified access to supported models. Application-level fallback and routing should be implemented by the developer unless a managed failover feature is explicitly documented.
How can I prevent duplicate requests?
Use stable task IDs, idempotency keys and persistent operation states. Separate model generation from business side effects.
What is a circuit breaker?
It temporarily stops sending traffic to a route after repeated failures. After a cooldown period, a small number of test requests determine whether the route has recovered.
What is the most important production metric?
Task success rate is more useful than HTTP success alone. For economic performance, measure cost per successfully completed task.
Final Recommendation
Production LLM reliability does not come from assuming that a provider will never fail.
It comes from designing every request with failure in mind.
Use explicit timeouts. Retry only transient errors. Apply exponential backoff with jitter. Respect provider retry instructions. Limit total attempts and total execution time. Validate every response, even when the HTTP request succeeds.
Maintain one tested fallback instead of a long chain of unverified models. Use circuit breakers when a route remains unhealthy. Control concurrency before rate-limit errors become widespread. Protect business actions with idempotency keys so a timeout cannot create duplicated side effects.
Most importantly, measure the complete task:
Request sent
→ Transport succeeds
→ Output validates
→ Business action completes
→ User receives the resultLumeAPI can simplify this architecture by giving developers one OpenAI-compatible endpoint for supported GPT, Claude and Gemini models, along with one API key, unified wallet billing and a public model catalog. That makes model switching and controlled fallback easier to implement without maintaining a separate client for every model family. ([LumeAPI Docs][2])
A unified endpoint is not a complete reliability strategy by itself.
The strongest production system combines:
- A stable primary route
- Limited intelligent retries
- Output validation
- A tested fallback
- Idempotent execution
- Health monitoring
- Clear operational limits
- A direct recovery path when the gateway or provider is unavailable
Failures will still happen.
The goal is to make those failures controlled, observable and recoverable—so users experience a reliable product even when the model infrastructure behind it is temporarily imperfect.
[1]: https://docs.anthropic.com/en/api/errors "Errors | Claude Platform Docs" [2]: https://lumeapi.site/docs "Developer Docs — OpenAI-Compatible API Reference" [3]: https://ai.google.dev/gemini-api/docs/rate-limits "Rate limits | Gemini API" [4]: https://docs.anthropic.com/en/api/rate-limits "Rate limits | Claude Platform Docs" [5]: https://lumeapi.site/terms "Terms of Service | LumeAPI"