# How to Handle and Differentiate Various Error Types from the Anthropic API

> Learn to handle and differentiate Anthropic API error types with the Python SDK. Catch specific exceptions like RateLimitError for precise error handling and retry logic.

- Repository: [Anthropic/anthropic-sdk-python](https://github.com/anthropics/anthropic-sdk-python)
- Tags: how-to-guide
- Published: 2026-02-23

---

**The Anthropic Python SDK converts every HTTP error into a typed exception hierarchy, allowing you to catch specific errors like `RateLimitError` or inspect the `body.type` attribute of `APIStatusError` to implement precise error handling and retry logic.**

The `anthropics/anthropic-sdk-python` repository provides a robust exception architecture that maps every API error response to a typed Python exception. When working with the Claude API, understanding how to handle and differentiate various error types from the API is essential for building resilient applications that can retry transient failures or abort on authentication issues.

## Understanding the Error Payload Structure

When the Anthropic API returns an error, the JSON body follows a consistent structure containing an `error` object with `type` and `message` fields:

```json
{
  "error": {
    "type": "rate_limit_error",
    "message": "This request would exceed the rate limit for your organization..."
  }
}

```

The SDK models this payload using Pydantic classes defined in `src/anthropic/types/shared/`. The **`ErrorResponse`** class in [`error_response.py`](https://github.com/anthropics/anthropic-sdk-python/blob/main/error_response.py) serves as the top-level container, while **`ErrorObject`** in [`error_object.py`](https://github.com/anthropics/anthropic-sdk-python/blob/main/error_object.py) acts as a discriminated union of all concrete error types. Each specific error—such as `RateLimitError`, `AuthenticationError`, or `InvalidRequestError`—inherits from the generated `BaseModel` and includes a literal `type` field that uniquely identifies the condition.

## The SDK Exception Hierarchy

All API errors raise exceptions defined in **[`src/anthropic/_exceptions.py`](https://github.com/anthropics/anthropic-sdk-python/blob/main/src/anthropic/_exceptions.py)**, organized into a clear inheritance tree:

```text
AnthropicError (base)
 └─ APIError (holds request, message, and optional body)
     └─ APIStatusError (raised for any 4xx/5xx response)
         ├─ BadRequestError (400)
         ├─ AuthenticationError (401)
         ├─ PermissionDeniedError (403)
         ├─ NotFoundError (404)
         ├─ ConflictError (409)
         ├─ RequestTooLargeError (413)
         ├─ UnprocessableEntityError (422)
         ├─ RateLimitError (429)
         ├─ ServiceUnavailableError (503)
         ├─ OverloadedError (529)
         ├─ DeadlineExceededError (504)
         └─ InternalServerError (≥500)

```

**`APIStatusError`** serves as the parent for all HTTP error status codes. Each subclass corresponds to a specific status code, allowing you to catch granular errors without parsing JSON manually.

## How Status Codes Map to Exceptions

The mapping logic resides in the `_make_status_error` method inside **[`src/anthropic/_client.py`](https://github.com/anthropics/anthropic-sdk-python/blob/main/src/anthropic/_client.py)**. When the underlying HTTP client (`httpx`) returns a response with `is_error` set to `True` (status ≥ 400), the SDK:

1. Extracts the status code from the response
2. Instantiates the corresponding `APIStatusError` subclass based on the code mapping
3. Parses the JSON body into the appropriate `ErrorObject` model (e.g., `RateLimitError` model) and stores it in the exception's `body` attribute

This architecture means every exception contains both the HTTP-level information (status code, headers) and the strongly-typed API error model.

## Differentiating Errors in Practice

### Catching Specific Exception Classes

For clear control flow, import and catch the specific exception classes that correspond to the errors you expect. This approach works for both synchronous and asynchronous clients:

```python
from anthropic import Anthropic, RateLimitError, AuthenticationError

client = Anthropic()

try:
    response = client.messages.create(
        model="claude-3-5-sonnet-20240620",
        max_tokens=1024,
        messages=[{"role": "user", "content": "Explain quantum computing."}]
    )
    print(response.content)
except RateLimitError as e:
    # Access the request ID for support tickets

    print(f"Rate limited. Request ID: {e.request_id}")
    # Implement exponential back-off before retrying

except AuthenticationError as e:
    print(f"Authentication failed: {e.message}")
    # Do not retry; log and alert the team

```

**`RateLimitError`** and other concrete exceptions expose `message` and `request_id` attributes directly, making it easy to log diagnostic information without drilling into the response body.

### Inspecting Error Types via the Response Body

When you need to handle multiple error types dynamically or want a single catch-all with conditional logic, catch **`APIStatusError`** and inspect the `body` attribute:

```python
from anthropic import Anthropic, APIStatusError

client = Anthropic()

try:
    client.messages.create(
        model="claude-3-opus-20240229",
        max_tokens=512,
        messages=[{"role": "user", "content": "What is the meaning of life?"}]
    )
except APIStatusError as err:
    # body is the parsed ErrorObject model (e.g., AuthenticationError, InvalidRequestError)

    err_type = getattr(err.body, "type", "unknown")
    
    if err_type == "authentication_error":
        print("🔐 Invalid API key. Check your ANTHROPIC_API_KEY environment variable.")
    elif err_type == "invalid_request_error":
        print("❌ Request validation failed. Review your message payload.")
    elif err_type == "rate_limit_error":
        print("⏱️ Rate limit hit. Backing off before retry...")
    elif err_type == "overloaded_error":
        print("⚡ Service overloaded. Retry after a longer pause.")
    else:
        print(f"Unhandled API error ({err_type}): {err.message}")

```

This pattern avoids importing numerous exception classes and lets you branch based on the literal `type` string defined in the API specification.

### Handling Errors in Async Applications

The async client (`AsyncAnthropic`) mirrors the synchronous exception hierarchy exactly. You can use the same exception classes with `async/await` syntax:

```python
import asyncio
from anthropic import AsyncAnthropic, AuthenticationError, OverloadedError

async def main() -> None:
    client = AsyncAnthropic()
    try:
        await client.messages.create(
            model="claude-3-sonnet-20240229",
            max_tokens=256,
            messages=[{"role": "user", "content": "Summarize general relativity."}]
        )
    except AuthenticationError:
        print("🛡️  Bad API key – aborting workflow.")
        raise  # Re-raise to stop execution

    except OverloadedError:
        print("⚡  Service temporarily overloaded – scheduling retry.")
        # Implement back-off and retry logic here

    except Exception as exc:
        print(f"⚠️ Unexpected error: {exc}")

asyncio.run(main())

```

## Retry Strategies and Error Classification

Not all errors warrant a retry. Use the following classification when implementing retry logic:

- **Safe to retry with back-off:**
  - **`RateLimitError`** (429) – Retry after respecting the `Retry-After` header
  - **`OverloadedError`** (529) – Anthropic-specific capacity issue; retry with exponential back-off
  - **`ServiceUnavailableError`** (503) – Temporary server unavailability

- **Do not retry without corrective action:**
  - **`AuthenticationError`** (401) – Indicates an invalid or expired API key
  - **`PermissionDeniedError`** (403) – Insufficient permissions for the requested resource
  - **`NotFoundError`** (404) – Resource does not exist; retrying will not help
  - **`BadRequestError`** (400) or **`UnprocessableEntityError`** (422) – Malformed request payload

When inspecting `body.type`, treat `"invalid_request_error"` and `"authentication_error"` as terminal failures, while `"rate_limit_error"` and `"overloaded_error"` signal temporary conditions suitable for retry.

## Summary

- The SDK parses API error JSON into typed **`ErrorObject`** models defined in `src/anthropic/types/shared/`
- **`APIStatusError`** subclasses in [`src/anthropic/_exceptions.py`](https://github.com/anthropics/anthropic-sdk-python/blob/main/src/anthropic/_exceptions.py) map HTTP status codes to specific Python exceptions
- The **`_make_status_error`** method in [`src/anthropic/_client.py`](https://github.com/anthropics/anthropic-sdk-python/blob/main/src/anthropic/_client.py) performs the status-code-to-exception translation
- You can handle errors by **catching concrete exception classes** (e.g., `RateLimitError`) or by **inspecting `err.body.type`** on a generic `APIStatusError`
- **`RateLimitError`** and **`OverloadedError`** are generally safe to retry, while **`AuthenticationError`** and request validation errors require configuration changes

## Frequently Asked Questions

### What is the difference between APIStatusError and AnthropicError?

**`AnthropicError`** is the base class for all SDK-related exceptions, including network failures and configuration errors. **`APIStatusError`** is a specific subclass of `APIError` (which inherits from `AnthropicError`) that represents HTTP 4xx and 5xx responses from the Anthropic API. If you catch `APIStatusError`, you are specifically handling error responses from the API server, not connection issues or SDK validation errors.

### How do I access the raw JSON error response?

Every `APIStatusError` exposes the parsed response body through its **`body`** attribute, which contains an instance of the appropriate `ErrorObject` model (such as `RateLimitError` or `AuthenticationError`). While the exception's `message` attribute provides a human-readable description, `err.body` gives you typed access to fields like `type` and `message` from the original JSON. There is no public attribute for the raw JSON string; the SDK deserializes it immediately upon receiving the response.

### Which Anthropic API errors should I retry?

You should retry **`RateLimitError`** (HTTP 429) and **`OverloadedError`** (HTTP 529) after implementing an exponential back-off strategy. These indicate temporary capacity or rate-limiting issues. Do not retry **`AuthenticationError`** (401), **`PermissionDeniedError`** (403), **`NotFoundError`** (404), or request validation errors (400, 422), as these indicate client-side misconfigurations or invalid parameters that will fail identically on subsequent attempts.

### Why do some exception classes share names with error models?

The SDK maintains two parallel hierarchies: **exception classes** in [`src/anthropic/_exceptions.py`](https://github.com/anthropics/anthropic-sdk-python/blob/main/src/anthropic/_exceptions.py) (which are raised) and **error models** in `src/anthropic/types/shared/` (which are Pydantic models for JSON deserialization). For example, `RateLimitError` exists as both an exception class (for raising/catching) and as a Pydantic model (for parsing the API response). The exception's `body` attribute holds an instance of the corresponding model, linking the two systems together.