# Switch from another provider

URL: https://docs.router.ezat.io/tools/migrate

> Move code from Anthropic's API or from an OpenAI-compatible router such as OpenRouter to EZAT Router, and what to check afterwards.

## When to use this page

Your code or tool already calls Anthropic's API, or an OpenAI-compatible router such as OpenRouter, and you want it to call EZAT Router instead. In most cases you change the base URL and the key, and check the model names.

## From Anthropic's API

EZAT speaks Anthropic's Messages format, and Anthropic's model names are aliases of EZAT's ids, so code written for Anthropic's API works unchanged.

| | Anthropic's API | EZAT Router |
| --- | --- | --- |
| Base URL | `https://api.anthropic.com` | `https://api.router.ezat.io` |
| Key | An Anthropic Console key | Your EZAT key (`ezat_sk_…`), in `x-api-key` or `Authorization: Bearer` |
| Model names | `claude-sonnet-5-5` and the other Anthropic names | The same names work. EZAT's own ids are `author/model`, for example `anthropic/claude-sonnet-5-5`. |
| `model` in responses | The name you sent | The name you sent |
| Errors | Anthropic's format | Anthropic's format, plus `402` `billing_error` when your balance is used up |

The quickest switch needs no code change. The Anthropic SDKs read `ANTHROPIC_BASE_URL` and `ANTHROPIC_API_KEY`:

```bash
export ANTHROPIC_BASE_URL="https://api.router.ezat.io"
export ANTHROPIC_API_KEY="$EZAT_API_KEY"
```

Or pass both to the client. Before:

```python
client = anthropic.Anthropic(api_key=os.environ["ANTHROPIC_API_KEY"])
```

After:

```python tab="Python" test
import os

import anthropic

client = anthropic.Anthropic(
    base_url="https://api.router.ezat.io",
    api_key=os.environ["EZAT_API_KEY"],
)

message = client.messages.create(
    model="anthropic/claude-sonnet-5-5",
    max_tokens=64,
    messages=[{"role": "user", "content": "Say hello in five words."}],
)
print("".join(block.text for block in message.content if block.type == "text"))
```

```ts tab="TypeScript" test
import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic({
  baseURL: "https://api.router.ezat.io",
  apiKey: process.env.EZAT_API_KEY,
});

const message = await client.messages.create({
  model: "anthropic/claude-sonnet-5-5",
  max_tokens: 64,
  messages: [{ role: "user", content: "Say hello in five words." }],
});
console.log(message.content.filter((block) => block.type === "text").map((block) => block.text).join(""));
```

For Claude Code, see [Claude Code](https://docs.router.ezat.io/tools/claude-code.md): it uses `ANTHROPIC_AUTH_TOKEN` instead of `ANTHROPIC_API_KEY`.

What isn't the same as Anthropic's own API:

- **Not available:** the Message Batches API, the Files API, server tools (web search, code execution) and the MCP connector. Images must be sent as base64, not by URL.
- **`anthropic-beta`** is passed to the model provider, which may not accept every beta Anthropic's API does.
- **Non-streaming requests end after 590 seconds** with `504` `timeout_error`. Stream long requests.
- **Limits** are per EZAT key ([Rate limits](https://docs.router.ezat.io/reference/rate-limits.md)). EZAT doesn't send `anthropic-ratelimit-*` headers.
- **Prompt caches start empty.** Your first requests write the cache again.

The full list is in [Anthropic Messages API](https://docs.router.ezat.io/anthropic/messages.md).

## From OpenRouter and other OpenAI-compatible routers

Use EZAT's [OpenAI-compatible API](https://docs.router.ezat.io/openai/base-url.md). Change the base URL and the key, and check each model id.

| | OpenRouter | EZAT Router |
| --- | --- | --- |
| Base URL | `https://openrouter.ai/api/v1` | `https://api.router.ezat.io/openai/v1` |
| Key | An OpenRouter key | Your EZAT key, as `Authorization: Bearer` |
| Model ids | `author/model`, with a dot in version numbers: `anthropic/claude-sonnet-5.5` | `author/model`, with a hyphen: `anthropic/claude-sonnet-5-5`. The dotted name without the author, `claude-sonnet-5.5`, is an alias. |
| Models offered | Many vendors | The ones on [Models](https://docs.router.ezat.io/models.md) |
| `cache_control` on content parts | Yes | Yes, the same way. EZAT also caches automatically when you set none. |
| Router-specific fields such as `reasoning` | Used | Ignored. Use `reasoning_effort`. |
| `web_search_options` | Used | Refused with `400` |

<Callout type="warn" title="Rewrite dotted model ids">
`anthropic/claude-sonnet-5.5` is not an EZAT id and returns `404` `model_not_found`. Replace the dot with a hyphen (`anthropic/claude-sonnet-5-5`), or drop the `anthropic/` prefix (`claude-sonnet-5.5`). `GET https://api.router.ezat.io/openai/v1/models` lists the ids.
</Callout>

Before:

```python
client = OpenAI(
    base_url="https://openrouter.ai/api/v1",
    api_key=os.environ["OPENROUTER_API_KEY"],
)
```

After:

```python tab="Python" test
import os

from openai import OpenAI

client = OpenAI(
    base_url="https://api.router.ezat.io/openai/v1",
    api_key=os.environ["EZAT_API_KEY"],
)

completion = client.chat.completions.create(
    model="anthropic/claude-sonnet-5-5",
    max_tokens=64,
    messages=[{"role": "user", "content": "Say hello in five words."}],
)
print(completion.choices[0].message.content)
```

```ts tab="Node.js" test
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://api.router.ezat.io/openai/v1",
  apiKey: process.env.EZAT_API_KEY,
});

const completion = await client.chat.completions.create({
  model: "anthropic/claude-sonnet-5-5",
  max_tokens: 64,
  messages: [{ role: "user", content: "Say hello in five words." }],
});
console.log(completion.choices[0].message.content);
```

Tools that read `OPENAI_BASE_URL` and `OPENAI_API_KEY` need only those two changed. For editors and frameworks, see [OpenAI-format tools](https://docs.router.ezat.io/tools/openai-tools.md).

What isn't the same:

- **Only Chat Completions and the models endpoints.** No Responses API, embeddings, image generation or audio.
- **Refused fields:** `n` above 1, `logprobs`, `logit_bias`, audio, `web_search_options`, the legacy `functions` API and `response_format` `json_object` return `400`. The list is in [Chat Completions](https://docs.router.ezat.io/openai/chat-completions.md).
- **`temperature` and `top_p`** reach only the models that accept them; elsewhere they are dropped.
- **An overloaded model is `503`, and an empty balance is `402` `insufficient_quota`.**

## What to check after switching

First, list the models your key can use, and make sure every model id in your code is in the list or is an alias on [Models](https://docs.router.ezat.io/models.md):

```bash tab="Anthropic format" test
curl --fail-with-body https://api.router.ezat.io/v1/models -H "x-api-key: $EZAT_API_KEY"
```

```bash tab="OpenAI format" test
curl --fail-with-body https://api.router.ezat.io/openai/v1/models -H "Authorization: Bearer $EZAT_API_KEY"
```

Then:

1. **Search your code for features EZAT doesn't have:** batches, file uploads, server tools, the MCP connector, the Responses API, embeddings.
2. **Send a real request from your app** and find it on the dashboard's Usage page, with its tokens and cost.
3. **Check the balance.** EZAT is prepaid: when the balance is used up, requests fail with `402` until you add credits ([Billing](https://docs.router.ezat.io/billing.md)). A spend limit per key is on [API keys](https://docs.router.ezat.io/api-keys.md).
4. **Watch cache reads** on long prompts after the first few requests ([Prompt caching and sessions](https://docs.router.ezat.io/guides/prompt-caching.md)).

## For AI agents

1. From Anthropic's API: set base URL `https://api.router.ezat.io` and the EZAT key from `EZAT_API_KEY`. Keep Anthropic model names; they are aliases.
2. From OpenRouter or another OpenAI-compatible router: set base URL `https://api.router.ezat.io/openai/v1` and the EZAT key. Rewrite dotted model ids (`anthropic/claude-sonnet-5.5` becomes `anthropic/claude-sonnet-5-5`), and check every id against `GET https://api.router.ezat.io/openai/v1/models`.
3. Remove or replace calls to features EZAT doesn't have (batches, files, server tools, MCP connector, Responses API, embeddings, audio, image generation).
4. Don't keep the old provider's key in the new configuration, and don't print the EZAT key.
5. Make one request, and confirm it on the dashboard's Usage page.