# Anthropic SDK

The exchange serves Anthropic Messages, so the official Python and TypeScript clients work without changes. Set the origin of the exchange and your key.

Use this SDK for direct Messages API calls. To run Claude Code and its tools from your application, use the [Claude Agent SDK](https://inference.ai.exchange/docs/connect-claude-agent-sdk.md).

## Print the setup with the Liquid CLI

The [Liquid CLI](https://inference.ai.exchange/docs/cli.md) prints the setup for the Anthropic SDK with your model filled in. To run your own program with the exchange in its environment, use `liquid exec`. It sets `OPENAI_BASE_URL`, `ANTHROPIC_BASE_URL` and the keys for that process only.

```bash
curl -fsSL https://inference.ai.exchange/cli/install.sh | bash
liquid login
liquid connect anthropic-sdk
liquid exec -- python app.py
```

To set up the Anthropic SDK yourself, follow the steps below.

## Before you start

- A workspace with a balance and a `spend` key. See [API keys](https://inference.ai.exchange/docs/keys.md).
- The key exported in your shell:

  ```bash
  export LIQUID_API_KEY=sk-liquid-...
  ```

## 1. Point the client at the exchange

Use the origin **without** `/v1`, because this client adds `/v1/messages` itself. If the URL ends in `/v1`, the client requests `/v1/v1/messages`.

**Python**

```python
import os
from anthropic import Anthropic

client = Anthropic(
    base_url="https://router.inference.ai.exchange",
    api_key=os.environ["LIQUID_API_KEY"],
)

client.messages.create(
    model="liquid.auto",
    max_tokens=1024,
    messages=[{"role": "user", "content": "hello"}],
)
```

**TypeScript**

```typescript
import Anthropic from '@anthropic-ai/sdk'

const client = new Anthropic({
  baseURL: 'https://router.inference.ai.exchange',
  apiKey: process.env.LIQUID_API_KEY,
})

const message = await client.messages.create({
  model: 'liquid.auto',
  max_tokens: 1024,
  messages: [{ role: 'user', content: 'hello' }],
})
```

The client sends the key in the `x-api-key` header, which the exchange accepts. Use any model, router model or preset on the book. `GET /v1/anthropic/models` lists the names. See [Market data](https://inference.ai.exchange/docs/market.md).

## 2. Read the cap before the first token

The `x-liquid-job-id` and `x-liquid-cap-usd` headers arrive before any token. `cap_usd` is the maximum charge for the job.

Read the headers from the raw response: `with_raw_response` (Python) or `.asResponse()` (TypeScript).

## 3. Cache a prompt prefix

`cache_control` works as it does with Anthropic. Mark the last block of the prefix to cache. The exchange changes your markers into the cache plan of the job, for the winning provider.

```python
client.messages.create(
    model="liquid.auto",
    max_tokens=1024,
    system=[
        {"type": "text", "text": "You are terse."},
        {
            "type": "text",
            "text": long_reference_document,
            "cache_control": {"type": "ephemeral", "ttl": "1h"},
        },
    ],
    messages=[{"role": "user", "content": "what changed in section 4?"}],
)
```

| Marked block              | Becomes                                      |
| ------------------------- | -------------------------------------------- |
| A block of `system`       | A breakpoint at the end of the system prompt |
| A tool definition         | A breakpoint after the last tool             |
| A block inside a turn     | A breakpoint at the end of that turn         |
| Top-level `cache_control` | A breakpoint after the last message          |

Rules for breakpoints:

- Each breakpoint caches at least the prefix that you marked.
- The exchange sends four breakpoints at most. If there are more, it drops the shallowest, because a deeper one caches more of the prompt.
- `"ttl": "1h"` asks for the one-hour write. All other values are five minutes. The exchange prices the two separately.
- The price cap includes cache writes up to the deepest breakpoint, so a write cannot charge more than your cap.

The `usage` block reports cached input separately from fresh input:

```json
{
  "input_tokens": 412,
  "cache_read_input_tokens": 10000,
  "cache_creation_input_tokens": 0,
  "output_tokens": 128
}
```

`input_tokens` counts only the input that the cache did not read or write, so the sum of the three does not count cached input two times.

If a request has no markers, the exchange places its own breakpoints. It does this only if both conditions are true:

- The request declares tools or continues a conversation.
- The estimated prompt is 512 tokens or more.

To keep only your own markers, or no markers, send `exchange.cache.mode` as `buyer` or `off`:

```python
client.messages.create(
    model="liquid.auto",
    max_tokens=1024,
    messages=[{"role": "user", "content": "hello"}],
    extra_body={"exchange": {"cache": {"mode": "buyer"}}},
)
```

See [Prompt caching](https://inference.ai.exchange/docs/caching.md) for how the exchange places breakpoints, prices cached input, and keeps your cache separate from other buyers.

## Limits and details

### Output limit

The Anthropic format requires `max_tokens`.

- If no offer allows that many output tokens, the exchange lowers `max_tokens` to the largest limit of the offers that can take the job. A longer answer then stops with `stop_reason: "max_tokens"`.
- The exchange does not lower `max_tokens` to fit your balance or cap. It refuses the request instead. See [Errors](https://inference.ai.exchange/docs/errors.md).

### Hosted tools

The exchange does not run hosted tools.

- To search the web, add `:web` (beta) to the model name. The exchange then removes `web_search` from the request and runs its own search.
- It refuses a request with a tool such as `web_search`, `web_fetch` or `code_execution`. The error is `400 unsupported_parameter`, and `param` is `tools[N]`.
- Send a tool with a name and an `input_schema`, which your client runs. The typed client tools `bash`, `text_editor`, `computer` and `memory` are accepted.

### Reasoning blocks

The reasoning of a model comes back as `thinking` blocks before the answer. This is also true for models that are not Claude. Encrypted reasoning comes back as `redacted_thinking`.

- Send these blocks back unchanged, as the Anthropic API requires in a tool-use loop.
- A block from a model that does not sign its reasoning has an empty `signature`. The exchange does not send that block to Claude.

### Cost control

- To set a cost cap or service floor for each request, send the `exchange` block in the body, or the same rules as `x-liquid-*` headers. See [Purchasing rules](https://inference.ai.exchange/docs/rules.md).
- To limit total spend across many requests, set a spending limit on the key. See [API keys](https://inference.ai.exchange/docs/keys.md).

## Next steps

- [Prompt caching](https://inference.ai.exchange/docs/caching.md): breakpoints, retention and reporting.
- [Modalities](https://inference.ai.exchange/docs/modalities.md): send images and documents as content blocks.
- [Claude Code](https://inference.ai.exchange/docs/connect-claude-code.md): Anthropic's terminal agent, using this format.
- [OpenAI SDK](https://inference.ai.exchange/docs/connect-openai-sdk.md): the same idea in the OpenAI format.
- [Purchasing rules](https://inference.ai.exchange/docs/rules.md): every field of the `exchange` block.
