# Use Xantly with the Anthropic Python SDK

Xantly ships a byte-accurate `/v1/messages` endpoint that mirrors Anthropic's Messages API. Point the official `anthropic` Python SDK at it and everything, `messages.create`, streaming, tool use, prompt caching, vision, works untouched. No SDK fork needed.

## Prerequisites

- Python 3.8+
- `pip install anthropic` (version 0.30.0+)
- A Xantly API key, [create one](/dashboard/api-keys)

## Setup

```python
from anthropic import Anthropic

client = Anthropic(
    base_url="https://api.xantly.com",
    api_key="xantly_sk_...",
)
```

Note: `base_url` is the **bare host**: no `/v1` suffix. The Anthropic SDK appends `/v1/messages` on its own.

Env-var form:

```bash
export ANTHROPIC_BASE_URL=https://api.xantly.com
export ANTHROPIC_API_KEY=xantly_sk_...
```

## messages.create

```python
resp = client.messages.create(
    model="anthropic/claude-sonnet-4.6",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Write a bubble sort in Python."}],
)
print(resp.content[0].text)
```

You can also use Xantly aliases:

```python
resp = client.messages.create(
    model="xantly/auto-quality",
    max_tokens=1024,
    messages=[...],
)
```

## Streaming

```python
with client.messages.stream(
    model="anthropic/claude-sonnet-4.6",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Stream a haiku."}],
) as stream:
    for text in stream.text_stream:
        print(text, end="", flush=True)
```

Xantly preserves every Anthropic SSE event exactly, `message_start`, `content_block_start`, `content_block_delta`, `content_block_stop`, `message_delta`, `message_stop`. No custom handling.

## Tool use (`tool_use` blocks)

```python
tools = [{
    "name": "get_weather",
    "description": "Get weather for a city.",
    "input_schema": {
        "type": "object",
        "properties": {"city": {"type": "string"}},
        "required": ["city"],
    },
}]

resp = client.messages.create(
    model="anthropic/claude-sonnet-4.6",
    max_tokens=1024,
    tools=tools,
    messages=[{"role": "user", "content": "Weather in Paris?"}],
)

for block in resp.content:
    if block.type == "tool_use":
        print(block.name, block.input)
```

## Prompt caching

Anthropic's prompt caching works as-is. Add the `cache_control` marker; Xantly forwards it:

```python
resp = client.messages.create(
    model="anthropic/claude-sonnet-4.6",
    max_tokens=1024,
    messages=[{
        "role": "user",
        "content": [
            {
                "type": "text",
                "text": "<large context here>",
                "cache_control": {"type": "ephemeral"},
            },
            {"type": "text", "text": "What's the summary?"},
        ],
    }],
    extra_headers={"anthropic-beta": "prompt-caching-2024-07-31"},
)
print(resp.usage.cache_creation_input_tokens)
```

Xantly's own L1/L2 semantic cache runs **in addition** to Anthropic's prompt cache, Anthropic caches within a single provider; Xantly caches across providers and sessions.

## Async client

```python
import asyncio
from anthropic import AsyncAnthropic

aclient = AsyncAnthropic(
    base_url="https://api.xantly.com",
    api_key="xantly_sk_...",
)

async def main():
    resp = await aclient.messages.create(
        model="xantly/auto-quality",
        max_tokens=512,
        messages=[{"role": "user", "content": "Ping"}],
    )
    print(resp.content[0].text)

asyncio.run(main())
```

## Model choice

| Model ID | When |
|---|---|
| `anthropic/claude-sonnet-4.6` | Pinned Claude, best default via Messages API. |
| `anthropic/claude-opus-4.5` | Highest-quality Claude. |
| `anthropic/claude-haiku-4.5` | Fast + cheap Claude. |
| `xantly/auto-quality` | BaRP T1, may pick Claude, GPT, or others, responses translated back into Messages shape. |
| `xantly/auto-value` | Balanced. |

## Verify

```python
resp = client.messages.create(
    model="anthropic/claude-haiku-4.5",
    max_tokens=32,
    messages=[{"role": "user", "content": "say pong"}],
)
print(resp.content[0].text)
```

Open your Xantly dashboard, the call logs with model, cache status, and USD cost.

## What you get

- **Byte-accurate Messages API compatibility.** Including betas (prompt caching, PDFs, computer-use).
- **Waterfall fallback.** Anthropic overloaded? Xantly retries on GPT/Groq and translates the response back into Messages shape. Your client code never sees the swap.
- **Memory across sessions.** `extra_headers={"X-Xantly-Memory-Mode": "recall"}`.
- **Cost per turn.** Every call logged with routing decision + cost.
- **Cross-provider tool translation.** When Xantly falls back to GPT, tool-use blocks are translated both ways.

## Gotchas

**`base_url` has no `/v1`.** Anthropic's SDK appends the path. Use the bare host, `https://api.xantly.com`, not `https://api.xantly.com/v1`.

**Anthropic SDK pins API versions via `anthropic-version` header.** Xantly respects the header and forwards to the underlying provider. Default (`2023-06-01`) works fine.

**Streaming with iterators.** `with client.messages.stream(...)` (context manager) is the stable API. Don't use the deprecated `stream=True` kwarg.

**Beta headers.** `anthropic-beta: prompt-caching-2024-07-31`, `pdfs-2024-09-25`, `computer-use-2024-10-22` all pass through.

## Next steps

- [Anthropic SDK (TypeScript)](/docs/use-with-anthropic-sdk-typescript), the Node.js equivalent.
- [Claude Code](/docs/use-with-claude-code), Anthropic's CLI tool with the same pattern.
- [OpenAI SDK (Python)](/docs/use-with-openai-sdk-python), if you'd rather use the OpenAI shape.
- [Memory & Context](/docs/memory-and-context), how L3 memory extends Claude sessions.
