# Use Xantly with the Anthropic TypeScript SDK

The `@anthropic-ai/sdk` npm package takes a `baseURL` constructor option. Point it at Xantly's `/v1/messages` endpoint (byte-accurate Anthropic Messages API) and every call, `messages.create`, streaming, tool use, prompt caching, works untouched.

## Prerequisites

- Node 18+ (or Bun / Deno / Cloudflare Workers)
- `npm install @anthropic-ai/sdk` (version 0.27.0+)
- A Xantly API key, [create one](/dashboard/api-keys)

## Setup

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

const client = new Anthropic({
  baseURL: 'https://api.xantly.com', // bare host, SDK appends /v1/messages
  apiKey: process.env.XANTLY_API_KEY, // xantly_sk_...
})
```

Env-var style:

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

## messages.create

```ts
const resp = await client.messages.create({
  model: 'anthropic/claude-sonnet-4.6',
  max_tokens: 1024,
  messages: [{ role: 'user', content: 'Write a bubble sort in TypeScript.' }],
})
// content is always an array of blocks
const text = resp.content.find((b) => b.type === 'text')?.text ?? ''
console.log(text)
```

## Streaming

```ts
const stream = await client.messages.stream({
  model: 'anthropic/claude-sonnet-4.6',
  max_tokens: 1024,
  messages: [{ role: 'user', content: 'Stream a haiku.' }],
})

for await (const event of stream) {
  if (event.type === 'content_block_delta' && event.delta.type === 'text_delta') {
    process.stdout.write(event.delta.text)
  }
}
```

Xantly preserves every Anthropic SSE event exactly.

## Tool use

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

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

for (const block of resp.content) {
  if (block.type === 'tool_use') {
    console.log(block.name, block.input)
  }
}
```

## Prompt caching

```ts
const resp = await client.messages.create({
  model: 'anthropic/claude-sonnet-4.6',
  max_tokens: 1024,
  messages: [{
    role: 'user',
    content: [
      { type: 'text', text: '<large context>', cache_control: { type: 'ephemeral' } },
      { type: 'text', text: 'Summarize.' },
    ],
  }],
}, {
  headers: { 'anthropic-beta': 'prompt-caching-2024-07-31' },
})

console.log(resp.usage.cache_creation_input_tokens)
```

## Model choice

| Model ID | When |
|---|---|
| `anthropic/claude-sonnet-4.6` | Pinned Claude, default. |
| `anthropic/claude-opus-4.5` | Highest-quality Claude. |
| `anthropic/claude-haiku-4.5` | Fast + cheap Claude. |
| `xantly/auto-quality` | BaRP routes T1 pool, translates back to Messages shape. |
| `xantly/auto-value` | Balanced. |

## Verify

```ts
const resp = await client.messages.create({
  model: 'anthropic/claude-haiku-4.5',
  max_tokens: 32,
  messages: [{ role: 'user', content: 'say pong' }],
})
console.log(resp.content.find((b) => b.type === 'text')?.text)
```

## What you get

- **Byte-accurate Messages API.** Every SSE event, every response field preserved.
- **Waterfall fallback.** Anthropic overloaded? Xantly silently retries on GPT/Groq and re-translates the response back into Anthropic shape.
- **Memory.** Set `headers: { 'X-Xantly-Memory-Mode': 'recall' }` on any call.
- **Cost transparency.** Every request logs with routing decision + cost.
- **Beta headers pass through.** Prompt caching, PDFs, computer-use, all work untouched.

## Gotchas

**`baseURL` (camelCase) and no `/v1`.** TypeScript SDK uses `baseURL` (capital URL). Must be bare host, `https://api.xantly.com`.

**`max_tokens` is required.** Anthropic's Messages API requires it. The SDK will throw if you omit it.

**`content` is always an array.** Even for text-only responses. Use `resp.content.find((b) => b.type === 'text')?.text`.

**Streaming API uses `messages.stream(...)` not `messages.create({ stream: true })`.** The stream helper returns an iterable event stream + exposes `finalMessage()`.

**Edge runtime.** Works in Cloudflare Workers and Vercel Edge out of the box, the SDK uses `fetch`.

## Next steps

- [Anthropic SDK (Python)](/docs/use-with-anthropic-sdk-python), Python equivalent.
- [Claude Code](/docs/use-with-claude-code), Anthropic's CLI, same API shape.
- [OpenAI SDK (TypeScript)](/docs/use-with-openai-sdk-typescript), if you'd rather use OpenAI shape.
- [Streaming Responses](/docs/streaming-responses), SSE format details.
