Errors, retries and timeouts

Claude API · Practical · 11 min

Network calls fail; handling failures well is the difference between a demo and a service.

Any API call can fail: the network drops, you send something invalid, you go over a limit, or the service is busy. The SDK turns each HTTP error code into a Python exception you can catch.

The common ones:

  • BadRequestError (400): your request is invalid, for example a wrong parameter. Retrying the same request will not help.
  • AuthenticationError (401) and PermissionDeniedError (403): bad key or no access.
  • RateLimitError (429): too many requests or tokens per minute; wait and retry.
  • InternalServerError (500 and above), including 529 "overloaded": a temporary problem on Anthropic's side.
  • APIConnectionError and APITimeoutError: the request never got a proper answer.

All of these inherit from anthropic.APIError; HTTP ones from anthropic.APIStatusError, which has status_code. Catch the most specific ones first.

The SDK already retries connection errors, 408, 409, 429 and 5xx responses twice with exponential backoff (waiting a little longer each time), and honours the server's retry-after header. Change it with Anthropic(max_retries=...) or per call with client.with_options(max_retries=5).

The default timeout is 10 minutes. For a chat UI you may want much less; for long jobs use streaming instead of a longer timeout. Always log the request id from failed calls.

Example

import anthropic

client = anthropic.Anthropic(max_retries=3, timeout=60.0)

try:
    msg = client.messages.create(
        model="claude-opus-5-5", max_tokens=200,
        messages=[{"role": "user", "content": "Hello"}],
    )
except anthropic.BadRequestError as e:          # fix the request, don't retry
    print("Invalid request:", e.message)
except anthropic.RateLimitError:                # SDK already retried
    print("Still rate limited; queue it for later")
except anthropic.APIConnectionError:
    print("Network problem")
except anthropic.APIStatusError as e:           # any other HTTP error
    print(e.status_code, e.response.headers.get("request-id"))

Remember

  • Exceptions map to status codes; catch specific ones first
  • The SDK retries transient errors twice by default (max_retries)
  • 400s need a fix, 429/5xx need waiting

Watch out for

  • Wrapping calls in your own aggressive retry loop on top of the SDK's, multiplying traffic during an outage.

Check yourself

  1. Which error should you NOT simply retry?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: platform.claude.com/docs/en/api/errors