Exception Hierarchy

All SDK exceptions inherit from RulebookError. HTTP errors are further organized by status code so you can catch exactly what you need.
RulebookError
└── APIError
    ├── APIConnectionError
    │   └── APITimeoutError
    └── APIStatusError
        ├── BadRequestError          (400)
        ├── AuthenticationError      (401)
        ├── PermissionDeniedError    (403)
        ├── NotFoundError            (404)
        ├── UnprocessableEntityError (422)
        ├── RateLimitError           (429)
        └── InternalServerError      (5xx)

Basic Error Handling

from rulebook import Rulebook, NotFoundError, AuthenticationError, APIConnectionError

client = Rulebook(api_key="your-key")

try:
    detail = client.exchanges.retrieve("INVALID")
except NotFoundError as e:
    print(f"Exchange not found: {e.message}")
    print(f"Status code: {e.status_code}")      # 404
except AuthenticationError:
    print("Invalid API key — check your credentials")
except APIConnectionError:
    print("Network error — check your connection")

Exception Attributes

Every APIStatusError (and its subclasses) carries the full context of the failed request:
from rulebook import Rulebook, APIStatusError

client = Rulebook(api_key="your-key")

try:
    client.exchanges.retrieve("INVALID")
except APIStatusError as e:
    print(e.message)       # "Exchange not found"
    print(e.status_code)   # 404
    print(e.body)          # {"success": false, "error": "Not Found", ...}
    print(e.response)      # httpx.Response object
    print(e.request)       # httpx.Request object
AttributeTypeDescription
messagestrHuman-readable error description
status_codeintHTTP status code
bodyobject | NoneParsed JSON response body
responsehttpx.ResponseRaw HTTP response
requesthttpx.RequestThe request that failed

Catching All API Errors

Use the base classes to handle broad categories:
from rulebook import Rulebook, APIStatusError, APIConnectionError, RulebookError

client = Rulebook(api_key="your-key")

try:
    exchanges = client.exchanges.list()
except APIConnectionError:
    # Network-level failure (DNS, timeout, socket)
    print("Could not reach the API")
except APIStatusError as e:
    # Any HTTP 4xx/5xx error
    print(f"API error {e.status_code}: {e.message}")
except RulebookError as e:
    # SDK-level error (e.g., missing API key)
    print(f"SDK error: {e}")

Input Validation

The SDK validates required parameters before making HTTP calls. This raises a standard ValueError, not an SDK exception:
try:
    client.exchanges.retrieve("")  # empty string
except ValueError as e:
    print(e)  # "Expected a non-empty value for `exchange_name` but received ''"

Fee Schedule Results Error Handling

from rulebook import Rulebook, NotFoundError, PermissionDeniedError, AuthenticationError

client = Rulebook(api_key="your-key")

# Retrieve a single result
try:
    result = client.fee_schedule_results.retrieve("a1b2c3d4-e5f6-7890-abcd-ef1234567890")
    print(f"{result.supplier_name}: {result.fee_amount}")
except NotFoundError:
    print("Fee schedule result not found")
except PermissionDeniedError:
    print("You do not have access to this exchange's data")

# Get results by version
try:
    page = client.fee_schedule_results.get_results_by_version("f7e8d9c0-...")
    print(f"{page.total_records} results in this version")
except NotFoundError:
    print("Version not found")
except PermissionDeniedError:
    print("You do not have access to this version's exchange")

Common Error Scenarios

ScenarioExceptionHow to fix
Missing or invalid API keyAuthenticationErrorCheck your api_key or RULEBOOK_API_KEY env var
Exchange doesn’t existNotFoundErrorVerify the exchange name with exchanges.list()
Fee schedule result not foundNotFoundErrorVerify the ID with fee_schedule_results.list()
Version not foundNotFoundErrorCheck version_id from a fee schedule result
No access to exchangePermissionDeniedErrorContact sales to expand your access
Too many requestsRateLimitErrorBack off and retry, or contact sales for limits
Server errorInternalServerErrorRetry — SDK retries automatically by default
Network failureAPIConnectionErrorCheck your internet connection
Request timeoutAPITimeoutErrorIncrease timeout via client.with_options(timeout=60)