Text size
Dark mode
RecallRunGuides & benchmarks
Learn to build real systemsGuides•Benchmarks•Experiments•Community
Search the site/Free checklist

Guides/OpenAI SDK

OpenAI SDK: Errors, retries & timeouts

Network calls fail. Rate limits are hit, servers have bad minutes, requests are malformed, keys expire.

In this post

What this post covers
  1. 1. The exception hierarchy
  2. 2. Automatic retries, observed
  3. 3. Timeouts
  4. 4. A handling pattern for applications
  5. Recap

Network calls fail. Rate limits are hit, servers have bad minutes, requests are malformed, keys expire. The SDK already retries the retryable errors automatically (connection problems, 408, 409, 429 and 5xx) with exponential backoff. Your job is to set sensible timeouts, handle the errors that retrying cannot fix, and log the request id so problems can be traced.

1. The exception hierarchy

APIError APIConnectionError └ APITimeoutError APIStatusError 400 BadRequestError 401 AuthenticationError 403 PermissionDeniedError 404 NotFoundError 422 UnprocessableEntityError 429 RateLimitError 5xx InternalServerError fix the request / config: retrying won't help transient: retried automatically transient: retried automatically

2. Automatic retries, observed

import openai
from fake_openai import fake_client

# two 429s then success: the SDK retries with backoff and you never see the errors
client = fake_client(script=[{"status": 429}, {"status": 500}, "Recovered."], max_retries=2)
r = client.responses.create(model="gpt-5.6-luna", input="hello")
print(r.output_text, "| HTTP requests made:", len(client.fake.requests))

# more failures than retries: the error reaches your code
client = fake_client(script=[{"status": 429, "message": "Rate limit reached for requests"}] * 5, max_retries=2)
try:
    client.responses.create(model="gpt-5.6-luna", input="hello")
except openai.RateLimitError as e:
    print(type(e).__name__, e.status_code, "| request id:", e.request_id,
          "| requests made:", len(client.fake.requests))

# a 400 is NOT retried: sending the same bad request again would fail again
client = fake_client(script=[{"status": 400, "message": "Invalid value for 'model'"}], max_retries=2)
try:
    client.responses.create(model="gpt-5.6-luna", input="hello")
except openai.BadRequestError as e:
    print(type(e).__name__, e.status_code, e.message, "| requests made:", len(client.fake.requests))

Output

Recovered. | HTTP requests made: 3
RateLimitError 429 | request id: req_fake_8 | requests made: 3
BadRequestError 400 Error code: 400 - {'error': {'message': "Invalid value for 'model'", 'type': 'fake_error', 'code': None}} | requests made: 1

3. Timeouts

import openai
from fake_openai import fake_client

fake_client().responses.create(model="warm-up", input="x")            # one-time import cost

client = fake_client(script=[{"delay": 2.0}, "too late"], timeout=0.5, max_retries=0)
try:
    client.responses.create(model="gpt-5.6-luna", input="summarise this long report")
except openai.APITimeoutError as e:
    print("timed out:", type(e).__name__)

# per-call override for a known-slow request (e.g. high reasoning effort)
client = fake_client(script=[{"delay": 0.8}, "done"], timeout=0.5, max_retries=0)
r = client.with_options(timeout=2.0).responses.create(model="gpt-5.6-luna", input="hard question")
print("with a longer timeout:", r.output_text)

Output

timed out: APITimeoutError
with a longer timeout: done
The default timeout is 10 minutes

That suits long reasoning calls, but not a web request where a user is waiting. Set a timeout per use case (interactive: 30–60 s; background: longer), stream interactive responses so users see progress, and remember that a retried timeout can double the wait. Budget total time = timeout × (retries + 1).

4. A handling pattern for applications

import logging
import openai

log = logging.getLogger("assistant")

def answer(question: str) -> str:
    try:
        r = client.responses.create(model=MODEL, input=question, max_output_tokens=800)
    except openai.RateLimitError:
        return "We're busy right now. Please try again in a minute."      # already retried by the SDK
    except openai.APITimeoutError:
        return "That took too long. Try a shorter question."
    except openai.APIConnectionError:
        log.exception("network problem reaching OpenAI")
        return "Temporary connection problem."
    except openai.BadRequestError as e:
        log.error("bad request %s: %s", e.request_id, e.message)          # a bug in OUR code
        return "Sorry, something went wrong."
    except (openai.AuthenticationError, openai.PermissionDeniedError):
        log.critical("OpenAI credentials rejected")                        # page someone
        raise
    if r.status == "incomplete":
        log.warning("incomplete response %s: %s", r.id, r.incomplete_details)
    return r.output_text
ErrorMeaningAction
401 / 403bad, revoked or under-privileged keyalert; never retry in a loop
400 / 422your request is invalid (parameters, schema, too long)fix the code; log the request id
404unknown model, file or response idcheck names and ids (models get retired)
429rate limit or quota exceededretried automatically; if persistent, reduce concurrency or raise limits; check billing
5xx, connection, timeouttransient server or network troubleretried automatically; degrade gracefully if it persists

Recap

  • The SDK retries connection errors, 408/409/429/5xx (max_retries, default 2) with backoff.
  • 4xx errors other than 408/409/429 mean fix your request; retrying will not help.
  • Set timeouts per use case; the default is long.
  • Log request_id and check status == "incomplete".

Get the free RAG production checklist

25 checks to run before real users see your RAG feature: exact-match misses, invented citations, prompt injection through documents, quality that slips after a prompt change. Confirm your email and the PDF comes with the welcome email, plus each new experiment.

One email to confirm. Unsubscribe any time. What's in it · Privacy

Want the production version of this? See ShipRAG: A production RAG + MCP starter kit for FastAPI: hybrid search, validated citations, evals in CI, Docker, 32 tests.

Written by

Alby Tomy

Python/FastAPI Engineer | Data Engineer | AI Engineer building RAG and MCP systems.

X · GitHub · Contact & enquiries