Error Handling

All xanax exceptions inherit from XanaxError. This means you can catch everything with a single except XanaxError or be specific about what you handle.

Exception hierarchy

XanaxError
├── AuthenticationError    — 401 responses, or missing API key for protected content
├── RateLimitError         — 429 responses; has .retry_after
├── NotFoundError          — 404 responses
├── ValidationError        — invalid parameters (raised before any request)
└── APIError               — other 4xx/5xx responses; has .status_code

Full example

from xanax import Wallhaven, SearchParams
from xanax.errors import (
    AuthenticationError,
    RateLimitError,
    NotFoundError,
    ValidationError,
    APIError,
    XanaxError,
)

client = Wallhaven(api_key="your-api-key")

try:
    results = client.search(SearchParams(query="anime"))
except AuthenticationError:
    print("Invalid or missing API key")
except RateLimitError as e:
    print(f"Rate limited. Retry after {e.retry_after} seconds")
except NotFoundError:
    print("Resource not found")
except ValidationError as e:
    print(f"Invalid parameters: {e}")
except APIError as e:
    print(f"API error {e.status_code}: {e}")
except XanaxError as e:
    print(f"Unexpected xanax error: {e}")

AuthenticationError

Raised in two situations:

  1. The server returns HTTP 401

  2. You call a protected endpoint without an API key (raised locally, no request made)

client = Wallhaven()

try:
    client.settings()  # requires API key
except AuthenticationError as e:
    print(e)  # "User settings require an API key..."

RateLimitError

Raised when the API returns HTTP 429. The Wallhaven API allows 45 requests per minute.

from xanax.errors import RateLimitError

try:
    result = client.search(params)
except RateLimitError as e:
    print(e.retry_after)  # seconds to wait (float or None if not provided by API)

To avoid handling this yourself, configure automatic retry:

client = Wallhaven(max_retries=3)  # retry up to 3 times with exponential backoff

With max_retries=3, the client waits 2^attempt seconds between retries (1s, 2s, 4s). If all retries are exhausted, RateLimitError is raised.

NotFoundError

Raised on HTTP 404. Typically means the wallpaper or tag ID doesn’t exist:

try:
    wallpaper = client.wallpaper("doesnotexist")
except NotFoundError:
    print("Wallpaper not found")

ValidationError

Raised locally before any network request when search parameters are invalid.

Common causes:

  • top_range set without sorting=Sort.TOPLIST

  • Invalid resolution format (e.g., "1920-1080" instead of "1920x1080")

  • Invalid ratio format

  • Invalid seed length or characters

from xanax.errors import ValidationError

try:
    SearchParams(sorting=Sort.DATE_ADDED, top_range=TopRange.ONE_MONTH)
except ValidationError as e:
    print(e)  # "top_range requires sorting=toplist"

APIError

Catch-all for other HTTP errors (500, 503, etc.):

from xanax.errors import APIError

try:
    result = client.search(params)
except APIError as e:
    print(e.status_code)  # e.g. 503
    print(e.message)

Async error handling

The async client raises the same exceptions:

import asyncio
from xanax import AsyncWallhaven, SearchParams
from xanax.errors import RateLimitError

async def main():
    async with AsyncWallhaven(api_key="your-api-key", max_retries=3) as client:
        try:
            result = await client.search(SearchParams(query="anime"))
        except RateLimitError as e:
            print(f"Still rate limited after retries: {e.retry_after}s")

asyncio.run(main())