# Async Client

`AsyncWallhaven` is a complete async counterpart to `Wallhaven`. Every method is `async def`, it uses `httpx.AsyncClient` internally, and it supports async context managers and async generators.

## Basic usage

```python
import asyncio
from xanax import AsyncWallhaven, SearchParams

async def main():
    client = AsyncWallhaven(api_key="your-api-key")

    results = await client.search(SearchParams(query="anime"))
    for wallpaper in results.data:
        print(wallpaper.resolution, wallpaper.path)

    await client.aclose()

asyncio.run(main())
```

## Recommended: async context manager

The context manager handles cleanup automatically:

```python
async def main():
    async with AsyncWallhaven(api_key="your-api-key") as client:
        results = await client.search(SearchParams(query="nature"))
        wallpaper = await client.wallpaper("94x38z")
```

## Auto-pagination

```python
async def main():
    async with AsyncWallhaven(api_key="your-api-key") as client:
        # Flat iteration over all wallpapers
        async for wallpaper in client.aiter_media(SearchParams(query="space")):
            print(wallpaper.id, wallpaper.path)

        # Page-by-page
        async for page in client.aiter_pages(SearchParams(query="forest")):
            print(f"Page {page.meta.current_page}: {len(page.data)} results")
```

## Available methods

All sync `Wallhaven` methods have async equivalents:

| Sync | Async |
| ---- | ----- |
| `wallpaper(id)` | `await client.wallpaper(id)` |
| `search(params)` | `await client.search(params)` |
| `tag(id)` | `await client.tag(id)` |
| `settings()` | `await client.settings()` |
| `collections(username?)` | `await client.collections(username?)` |
| `collection(username, id)` | `await client.collection(username, id)` |
| `download(wallpaper, path?)` | `await client.download(wallpaper, path?)` |
| `iter_pages(params)` | `client.aiter_pages(params)` (async generator) |
| `iter_media(params)` | `client.aiter_media(params)` (async generator) |

## Rate limit retry

Retry with exponential backoff uses `asyncio.sleep()`, so it never blocks the event loop:

```python
client = AsyncWallhaven(api_key="your-api-key", max_retries=3)
```

## Authentication and environment variables

`AsyncWallhaven` reads `WALLHAVEN_API_KEY` from the environment the same way `Wallhaven` does:

```python
import os
os.environ["WALLHAVEN_API_KEY"] = "your-api-key"

async with AsyncWallhaven() as client:  # key picked up automatically
    ...
```

## Concurrent requests

Because `AsyncWallhaven` is fully async, you can use `asyncio.gather()` or `asyncio.TaskGroup` to run requests concurrently:

```python
import asyncio
from xanax import AsyncWallhaven

async def main():
    async with AsyncWallhaven(api_key="your-api-key") as client:
        wp1, wp2, wp3 = await asyncio.gather(
            client.wallpaper("94x38z"),
            client.wallpaper("3l3mpl"),
            client.wallpaper("kw7k78"),
        )
        print(wp1.resolution, wp2.resolution, wp3.resolution)

asyncio.run(main())
```

## Integration with web frameworks

`AsyncWallhaven` works naturally with async web frameworks like FastAPI:

```python
from contextlib import asynccontextmanager
from fastapi import FastAPI
from xanax import AsyncWallhaven, SearchParams

client: AsyncWallhaven

@asynccontextmanager
async def lifespan(app: FastAPI):
    global client
    client = AsyncWallhaven(api_key="your-api-key")
    yield
    await client.aclose()

app = FastAPI(lifespan=lifespan)

@app.get("/search")
async def search(q: str):
    results = await client.search(SearchParams(query=q))
    return {"total": results.meta.total, "wallpapers": [w.id for w in results.data]}
```
