How to implement exponential backoff for LLM API calls in Python
A decorator that retries flaky LLM API calls with exponential delay, using a mock client to demonstrate the pattern.
Python code
36 linesimport time
import random
class MockLLM:
def call(self, prompt):
if random.random() < 0.7: # 70% chance of transient failure
raise ConnectionError("API unavailable")
return f"LLM response for: {prompt}"
def with_exponential_backoff(max_retries=5, base_delay=0.1):
def decorator(func):
def wrapper(*args, **kwargs):
delay = base_delay
for attempt in range(max_retries + 1):
try:
return func(*args, **kwargs)
except Exception as e:
if attempt == max_retries:
raise e
print(f"Attempt {attempt + 1} failed: {e}")
time.sleep(delay)
delay *= 2
return None
return wrapper
return decorator
if __name__ == "__main__":
random.seed(42)
api = MockLLM()
@with_exponential_backoff(max_retries=3, base_delay=0.05)
def call_llm(prompt):
return api.call(prompt)
result = call_llm("Translate to French")
print(f"Final result: {result}")
Output
Attempt 1 failed: API unavailable
Attempt 2 failed: API unavailable
Final result: LLM response for: Translate to French
How it works
The decorator wraps any function and retries it when it raises an exception. Each retry waits delay seconds, then doubles it for the next attempt (delay *= 2). The max_retries parameter bounds the total attempts; the final failure re-raises the original exception so the caller can handle it. Using a MocksLLM class with random failures lets you test the retry logic without hitting a real API. The random.seed(42) ensures deterministic output for reproducible tests.
Common mistakes
- Raising the exception inside the loop before the final attempt, which skips retries
- Forgetting to multiply the delay, so subsequent retries don't actually back off
- Not catching all exception types, missing transient network errors
- Sleeping too long with a large base delay, blocking the caller thread
Variations
- Use `functools.wraps(func)` to preserve the original function's metadata.
- Add jitter: `delay = random.uniform(0, delay)` to avoid thundering herd.
Real-world use cases
- Retrying OpenAI or Anthropic API calls that fail with 429 or 503 status codes.
- Wrapping batch inference jobs that hit rate limits in a shared production pipeline.
- Recovering from transient network failures when a background worker calls a hosted LLM service.
Sponsored
More from AI & LLM integration patterns
- Cache LLM Completions by Hashing the Prompt in Python easy
- Chain of Thought Prompting in Python: Step-by-Step Reasoning Demo easy
- Circuit Breaker Pattern in Python for LLM API Calls medium
- Cosine Similarity to Retrieve Top K Chunks in Python easy
- Demonstrate Prompt Injection Bypass in Python easy
- How to Accumulate Streamed Tokens into a Final String in Python easy
Keep learning
Related tutorials and quizzes for this topic.