---
title: "Anthropic SDK"
description: "The official Anthropic SDKs for Python and TypeScript call pirouter with the base URL set to https://api.pirouter.ai (no /v1) and a pirouter API key. This page covers installing, configuring and verifying the SDK, and what to do about common errors, including the 401 caused by ANTHROPIC_AUTH_TOKEN in the environment."
canonical: "https://pirouter.ai/docs/integrations/anthropic-sdk"
last-updated: "2026-10-17"
lang: "en"
source: "content/docs/en/integrations/anthropic-sdk.mdx"
---

# Anthropic SDK

The official Anthropic SDKs for Python and TypeScript call pirouter with the base URL set to https://api.pirouter.ai (no /v1) and a pirouter API key. This page covers installing, configuring and verifying the SDK, and what to do about common errors, including the 401 caused by ANTHROPIC_AUTH_TOKEN in the environment.

The official Anthropic SDKs need no plugin to call pirouter: when you create the client, set the base URL to pirouter's address and the key to a pirouter key. The rest of your code stays as it is.

## Prerequisites

- A pirouter API key. If you do not have one, create it with the [quickstart](https://pirouter.ai/docs/quickstart.md) and store it in an environment variable:

```bash
export PIROUTER_API_KEY="sk-pi-..."
```

- Python 3.10 or later, or Node.js 20 or later.
- The SDK:

Python:

```bash
pip install anthropic
```

TypeScript:

```bash
pnpm add @anthropic-ai/sdk
```

The TypeScript samples use top-level `await`: save the file as `.mjs`, or set `"type": "module"` in `package.json`.

## Configure

Pass two settings when you create the client: `base_url` (`baseURL` in TypeScript) is `https://api.pirouter.ai`, without `/v1`, because the SDK adds `/v1/messages` itself; `api_key` (`apiKey` in TypeScript) is your pirouter key.

The SDK sends the key in the `x-api-key` header. When the `Authorization` header is empty, that is the header the gateway reads.

Python:

```python
import os
import anthropic

client = anthropic.Anthropic(
    base_url="https://api.pirouter.ai",
    api_key=os.environ["PIROUTER_API_KEY"],
)

message = client.messages.create(
    model="anthropic/claude-sonnet-5.5",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Hello!"}],
)
print(message.content[0].text)
```

TypeScript:

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

const client = new Anthropic({
  baseURL: 'https://api.pirouter.ai',
  apiKey: process.env.PIROUTER_API_KEY,
})

const message = await client.messages.create({
  model: 'anthropic/claude-sonnet-5.5',
  max_tokens: 1024,
  messages: [{ role: 'user', content: 'Hello!' }],
})
console.log(message.content[0].text)
```

Write the full `vendor/model` ID. For the models you can use, see the Anthropic column of the protocol table in [Models and protocols](https://pirouter.ai/docs/models.md#which-protocols-each-model-accepts): besides Claude, models of several other vendors accept the Anthropic protocol.

### Use environment variables

A client created without arguments reads the `ANTHROPIC_BASE_URL` and `ANTHROPIC_API_KEY` environment variables. Set them once, then write `anthropic.Anthropic()` (`new Anthropic()` in TypeScript) in your code:

`~/.zshrc`:

```bash
export ANTHROPIC_BASE_URL="https://api.pirouter.ai"
export ANTHROPIC_API_KEY="$PIROUTER_API_KEY"
```

### Stream the response

Send the request with `client.messages.stream(...)`; the text arrives as a series of events:

Python:

```python
import os
import anthropic

client = anthropic.Anthropic(
    base_url="https://api.pirouter.ai",
    api_key=os.environ["PIROUTER_API_KEY"],
)

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

TypeScript:

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

const client = new Anthropic({
  baseURL: 'https://api.pirouter.ai',
  apiKey: process.env.PIROUTER_API_KEY,
})

const stream = client.messages.stream({
  model: 'anthropic/claude-sonnet-5.5',
  max_tokens: 1024,
  messages: [{ role: 'user', content: 'Hello!' }],
})
stream.on('text', (text) => process.stdout.write(text))
await stream.finalMessage()
console.log()
```

You should see the reply printed piece by piece.

## Verify

Save the sample above as `main.py` or `main.mjs`, then run it:

Python:

```bash
python main.py
```

TypeScript:

```bash
node main.mjs
```

You should see a one-line reply from the model.

Open **Logs** in the console. A few seconds after the request, it appears in the list with `/v1/messages` under **Endpoint**. Every response carries an `X-Pi-Trace-Id` header, the same value as the **Trace ID** in the log details.

## Troubleshooting

### 401 invalid\_api\_key

A `401` with the error code `invalid_api_key` and the `message` "You didn't provide an API key. Provide it in an Authorization header using Bearer auth (Authorization: Bearer YOUR\_KEY)." means the request carried no key, or carried something other than a pirouter key starting with `sk-pi-`. Check that `PIROUTER_API_KEY` is set in the terminal that runs the code.

### 401 although the code passes the key

Check the environment for `ANTHROPIC_AUTH_TOKEN`, which is often set on machines that run Claude Code:

```bash
echo "$ANTHROPIC_AUTH_TOKEN"
```

When it has a value, the TypeScript SDK sends it in an `Authorization: Bearer` header even though the code passes `apiKey`; the Python SDK reads it when the code passes no `api_key`. The gateway reads `Authorization` first and, when it has a value, does not look at `x-api-key`; a value that is not a pirouter key gets a `401`.

Remove the variable before running the code, or set it to the pirouter key as well:

```bash
unset ANTHROPIC_AUTH_TOKEN
```

### Base URL ends in /v1

With the base URL set to `https://api.pirouter.ai/v1`, the SDK builds the path `/v1/v1/messages` and the request fails. Drop the `/v1` and use `https://api.pirouter.ai`.

### 422 validation\_failed

A `422` with the error code `validation_failed` and a `message` starting "This model is not served on the endpoint you called." means the model does not accept the Anthropic protocol, for example `openai/gpt-6-sol`. The rest of the `message` lists the endpoints it is served on. Pick a model marked Yes in the Anthropic column of the protocol table, or use the [OpenAI SDK](https://pirouter.ai/docs/integrations/openai-sdk.md).

## Not verified yet

- The gateway serves `POST /v1/messages` only. `POST /v1/messages/count_tokens`, which `client.messages.count_tokens` calls, is not available today and returns `400` with `model is required`.
- Message Batches (`client.messages.batches`) are not available today.
