# Anthropic Messages API

URL: https://docs.router.ezat.io/zh/anthropic/messages

> 用 Anthropic 的 Messages 格式发送请求（流式或非流式）、计算 token 数、列出模型，并了解支持哪些功能。

## 何时阅读本页

你通过 Anthropic 的 Messages 格式调用 AI 模型：使用 Anthropic SDK、Claude Code 或直接发送 HTTP 请求。本页介绍端点、请求头、模型名称、流式输出（streaming）、超时，以及哪些功能不支持。完整的请求和响应 schema 见 [API 参考](https://docs.router.ezat.io/zh/reference/api.md)。

## Base URL 与端点

Base URL 是 `https://api.router.ezat.io`。Anthropic 的 SDK 会自己加上 `/v1/…`，所以传给它们的 base URL 不要带 `/v1`。

| 方法 | 路径 | 作用 |
| --- | --- | --- |
| `POST` | `/v1/messages` | 创建一条消息。加上 `"stream": true` 即为流式输出。 |
| `POST` | `/v1/messages/count_tokens` | 计算请求的输入 token 数。免费。 |
| `GET` | `/v1/models` | 列出你的 key 可以使用的模型。 |
| `GET` | `/v1/models/{model_id}` | 按 id 或别名查询单个模型。 |

其他路径都返回 `404`（见下文「支持与不支持」一节）。

## 认证

用以下任一请求头发送你的 EZAT key（`ezat_sk_…`）：

- `x-api-key: <key>`（Anthropic SDK 的发送方式），或
- `Authorization: Bearer <key>`（Claude Code 使用 `ANTHROPIC_AUTH_TOKEN` 时的发送方式）。

如果一个请求同时带有两者，EZAT 使用 `x-api-key`。key 的安全使用以及 `401` 和 `403` 错误见[认证](https://docs.router.ezat.io/zh/authentication.md)。

## 创建消息

```bash tab="curl" test
curl --fail-with-body https://api.router.ezat.io/v1/messages \
  -H "x-api-key: $EZAT_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "content-type: application/json" \
  -d '{
    "model": "anthropic/claude-sonnet-5-5",
    "max_tokens": 64,
    "messages": [{"role": "user", "content": "Say hello in five words."}]
  }'
```

```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"))
print(message.usage)
```

```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(""));
console.log(message.usage);
```

响应是 Anthropic 的 `message` 对象：

```json
{
  "id": "msg_…",
  "type": "message",
  "role": "assistant",
  "model": "anthropic/claude-sonnet-5-5",
  "content": [{ "type": "text", "text": "Hello, it's nice to meet you." }],
  "stop_reason": "end_turn",
  "stop_sequence": null,
  "usage": {
    "input_tokens": 14,
    "output_tokens": 9,
    "cache_creation_input_tokens": 0,
    "cache_read_input_tokens": 0
  }
}
```

- **`model` 与你发送的完全一致**。发送别名，返回的也是别名。Claude Code 和各 SDK 依赖这一点。
- **读取 `text` 块**。`content` 中可能在文本之前出现 `thinking` 或 `tool_use` 块，也可能只有这些块而没有文本，所以不要假定 `content[0]` 是文本。
- **响应头中的 `request-id`** 是 EZAT 为该请求分配的 id。联系支持团队时请提供它。

## 流式输出消息

加上 `"stream": true`。响应是 Anthropic 格式的 Server-Sent Events，不做任何改动。

```bash tab="curl" test
curl --fail-with-body -N https://api.router.ezat.io/v1/messages \
  -H "x-api-key: $EZAT_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "content-type: application/json" \
  -d '{
    "model": "anthropic/claude-sonnet-5-5",
    "max_tokens": 64,
    "stream": true,
    "messages": [{"role": "user", "content": "Count from 1 to 10."}]
  }'
```

```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"],
)

with client.messages.stream(
    model="anthropic/claude-sonnet-5-5",
    max_tokens=64,
    messages=[{"role": "user", "content": "Count from 1 to 10."}],
) as stream:
    for text in stream.text_stream:
        print(text, end="", flush=True)
    print()
    print(stream.get_final_message().usage)
```

```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 stream = client.messages.stream({
  model: "anthropic/claude-sonnet-5-5",
  max_tokens: 64,
  messages: [{ role: "user", content: "Count from 1 to 10." }],
});
stream.on("text", (text) => process.stdout.write(text));
const message = await stream.finalMessage();
console.log("\n", message.usage);
```

| 事件 | 何时出现 |
| --- | --- |
| `message_start` | 第一个事件。携带消息本身（其中 `model` 为你发送的值），以及输入和缓存 token 数。 |
| `content_block_start` | 一个文本块、思考块或工具调用块开始。 |
| `content_block_delta` | 块的一部分：`text_delta`、`thinking_delta` 或 `input_json_delta`。 |
| `content_block_stop` | 该块结束。 |
| `message_delta` | `stop_reason` 和最终的输出 token 数。 |
| `message_stop` | 最后一个事件。EZAT 记录完该请求的用量后关闭连接。 |
| `ping` | 15 秒内没有其他事件时发送，让代理保持连接。 |
| `error` | 流开始后发生的失败（此时 HTTP 状态码已经是 `200`）。之后流结束。 |

在第一个事件之前，如果某个部署失败，EZAT 会换到另一个部署重试，所以流开始时的模型提供方错误通常不会传到你这里。事件开始之后不再重试。

## 计算 token 数

`POST /v1/messages/count_tokens` 接受与 `/v1/messages` 相同的请求体（`model` 和 `messages`，如有使用再加上 `system` 和 `tools`），但不带 `max_tokens`，返回 `{"input_tokens": N}`。

```bash tab="curl" test
curl --fail-with-body https://api.router.ezat.io/v1/messages/count_tokens \
  -H "x-api-key: $EZAT_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "content-type: application/json" \
  -d '{
    "model": "anthropic/claude-sonnet-5-5",
    "messages": [{"role": "user", "content": "How many tokens is this?"}]
  }'
```

```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"],
)

count = client.messages.count_tokens(
    model="anthropic/claude-sonnet-5-5",
    messages=[{"role": "user", "content": "How many tokens is this?"}],
)
print(count.input_tokens)
```

```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 count = await client.messages.countTokens({
  model: "anthropic/claude-sonnet-5-5",
  messages: [{ role: "user", content: "How many tokens is this?" }],
});
console.log(count.input_tokens);
```

计算 token 数免费：不计费，也不会出现在「用量」中。但它会计入该 key 的每分钟请求数限制。`anthropic-beta` 对它不生效。

## 列出模型

```bash tab="curl" test
curl --fail-with-body https://api.router.ezat.io/v1/models \
  -H "x-api-key: $EZAT_API_KEY" \
  -H "anthropic-version: 2023-06-01"
```

```json
{
  "data": [
    {
      "type": "model",
      "id": "anthropic/claude-sonnet-5-5",
      "display_name": "…",
      "created_at": "2026-10-01T00:00:00.000Z"
    }
  ],
  "has_more": false,
  "first_id": "…",
  "last_id": "…"
}
```

- 列表包含你的 key 可以使用的所有模型，使用规范 id。限定了部分模型的 key（见 [API key](https://docs.router.ezat.io/zh/api-keys.md)）只能看到这些模型。
- `GET /v1/models/{model_id}` 接受别名，并返回规范 id。规范 id 中含有斜杠，所以在这里要做 URL 编码（`anthropic%2Fclaude-sonnet-5-5`），或者改用别名。

## 模型 id 与别名

- **规范 id** 的格式为 `author/model`，例如 `anthropic/claude-sonnet-5-5`。
- **别名**。Anthropic 自己的模型名称（例如 `claude-sonnet-5-5` 和 `claude-sonnet-5.5`）是规范 id 的别名，所以为 Anthropic API 编写的代码无需修改即可使用。匹配不区分大小写。
- 两种写法计费相同。[模型](https://docs.router.ezat.io/zh/models.md)页面列出了每个 id 及其别名。
- 未知的名称返回 `404` `not_found_error`，消息为 `model: <name>`。key 无权使用的模型返回 `403` `permission_error`。

## 请求头

请求头：

| 请求头 | 是否必需 | EZAT 如何处理 |
| --- | --- | --- |
| `x-api-key` 或 `Authorization: Bearer` | 是 | 你的 EZAT key。 |
| `content-type: application/json` | `POST` 请求必需 | |
| `anthropic-version` | 否 | 接受，但不会使用。SDK 会发送它们自己的值；任何值都可以，也可以不发送。 |
| `anthropic-beta` | 否 | 在 `/v1/messages` 上原样传给模型提供方。提供方不接受的 beta 会在提供方那里失败。 |
| `x-ezat-session` | 否 | 为对话命名，让它的请求留在同一个部署上，提示缓存（prompt caching）继续有效。可以是任意字符串，只使用前 256 个字符。见[提示缓存与会话](https://docs.router.ezat.io/zh/guides/prompt-caching.md)。 |
| `x-request-id` | 否 | 你自己为请求设定的 id（字母、数字、`.`、`_`、`:` 和 `-`，最多 128 个字符）。EZAT 会原样返回。 |

响应头：

| 响应头 | 含义 |
| --- | --- |
| `request-id` | EZAT 为该请求分配的 id。凡是通过了 key、模型、限额和余额检查的请求都会带上。 |
| `x-request-id` | 你发送的 `x-request-id`，或由 EZAT 生成的 id。错误响应体中会以 `request_id` 再次给出。 |
| `retry-after` | 出现 `429` 和 `529` 时：重试前需要等待的秒数。 |

EZAT 不发送 Anthropic 的 `anthropic-ratelimit-*` 响应头。它的限制见[速率限制](https://docs.router.ezat.io/zh/reference/rate-limits.md)。

## 工具

你自己定义并执行的工具，用法与在 Anthropic API 上相同：`tools`、`tool_choice`、响应中的 `tool_use` 块，以及在下一条消息中发送的 `tool_result` 块。

```json
{
  "model": "anthropic/claude-sonnet-5-5",
  "max_tokens": 1024,
  "tools": [
    {
      "name": "get_weather",
      "description": "Current weather for a city",
      "input_schema": {
        "type": "object",
        "properties": { "city": { "type": "string" } },
        "required": ["city"]
      }
    }
  ],
  "messages": [{ "role": "user", "content": "What's the weather in Oslo?" }]
}
```

由 Anthropic 替你执行的服务端工具（网页搜索、代码执行）不可用。见下文的表格。

## 图片和 PDF

图片以 base64 `image` 块发送（JPEG、PNG、GIF 或 WebP），PDF 以 base64 `document` 块发送：

```json
{
  "role": "user",
  "content": [
    {
      "type": "image",
      "source": { "type": "base64", "media_type": "image/png", "data": "iVBORw0KGgo…" }
    },
    { "type": "text", "text": "What is in this image?" }
  ]
}
```

在这个 API 上 EZAT 不会下载图片，所以 `url` 类型的 source 无法使用。要发送图片 URL，请使用 [OpenAI 兼容 API](https://docs.router.ezat.io/zh/openai/chat-completions.md)，它会替你下载图片。整个请求体最大 32 MB；超过会返回 `413` `request_too_large`。

## 思考

`thinking` 会原样传给模型，`thinking` 块会出现在响应和流中。思考 token 按输出 token 计费。

```json
{
  "model": "anthropic/claude-sonnet-5-5",
  "max_tokens": 16000,
  "thinking": { "type": "adaptive" },
  "messages": [{ "role": "user", "content": "Plan a three-day trip to Oslo." }]
}
```

可用选项见 Anthropic 的[扩展思考（extended thinking）指南](https://platform.claude.com/docs/en/build-with-claude/extended-thinking)。带思考的请求建议使用流式输出：它们往往耗时较长。

## 提示缓存

`cache_control` 的用法与 Anthropic API 相同，TTL 可以是 5 分钟（`{"type": "ephemeral"}`）或 1 小时（`{"type": "ephemeral", "ttl": "1h"}`）。缓存写入和缓存读取按各自的价格计费（见[价格](https://docs.router.ezat.io/zh/pricing.md)），`usage` 通过 `cache_creation_input_tokens` 和 `cache_read_input_tokens` 报告它们。

EZAT 会把每个对话保持在同一个部署上，让缓存持续生效。[提示缓存与会话](https://docs.router.ezat.io/zh/guides/prompt-caching.md)介绍了断点该放在哪里、会话请求头，以及有特殊情况的模型。

## 长请求与超时

- **非流式请求有 590 秒的期限**。模型到时还没完成，请求会以 `504` `timeout_error` 结束，消息为 ``The model took longer than 590 seconds. Send long requests with "stream": true.``。该请求不计费。
- **长输出请使用流式**。流式响应没有期限：静默 15 秒后 EZAT 会发送 `ping` 事件，所以模型思考期间连接保持打开。`max_tokens` 较大、带思考或输入很长的请求，都请使用流式输出。

## 错误

错误使用 Anthropic 的格式和 Anthropic 的状态码：

```json
{
  "type": "error",
  "error": { "type": "billing_error", "message": "Your credit balance is too low. Add credits in the EZAT Router dashboard." },
  "request_id": "…"
}
```

失败的请求不计费。[错误](https://docs.router.ezat.io/zh/reference/errors.md)列出了所有状态码和类型，并说明哪些可以重试。

## 流被中断

如果你在流结束前关闭它，EZAT 会停止模型，并按已生成的内容计费：输入和缓存 token 按精确值计，输出 token 根据已经流式返回的文本估算。在「用量」中，该请求显示为「已取消」，并标为估算值。如果模型提供方在流的中途失败，你会收到一个 `error` 事件，该请求不计费。详情见[计费](https://docs.router.ezat.io/zh/billing.md)。

## 支持与不支持

| 功能 | 状态 | 说明 |
| --- | --- | --- |
| 消息，流式和非流式 | 支持 | |
| 计算 token 数 | 支持 | 免费；不显示在「用量」中 |
| 模型列表和查询 | 支持 | |
| 你定义的工具（`tools`、`tool_choice`） | 支持 | |
| base64 图片和 PDF | 支持 | 每个请求体最大 32 MB |
| 通过 URL 发送图片（`source.type: "url"`） | 不支持 | 请发送 base64，或使用 OpenAI 兼容 API |
| 思考 | 支持 | |
| 提示缓存，5 分钟和 1 小时 | 支持 | `capabilities.promptCaching` 为 `false` 的模型除外；见[提示缓存](https://docs.router.ezat.io/zh/guides/prompt-caching.md) |
| 结构化输出（`output_config.format`） | 支持 | |
| `temperature`、`top_p`、`top_k` | 透传 | 只有 `capabilities.sampling` 为 `true` 的模型接受；其他模型收到非默认值时返回 `400` |
| `anthropic-beta` | 透传 | 哪些 beta 可用由模型提供方决定 |
| Message Batches API（`/v1/messages/batches`） | 不可用 | 返回 `404` |
| Files API（`/v1/files`） | 不可用 | 返回 `404`；请以 base64 内联发送文件 |
| 服务端工具：网页搜索、代码执行，以及其他由 Anthropic 执行的工具 | 不可用 | |
| MCP connector（`mcp_servers`） | 不可用 | |

每个模型的 `capabilities` 见 `GET https://api.router.ezat.io/api/models`（无需 key），以及[模型](https://docs.router.ezat.io/zh/models.md)页面的「能力」一列。

不可用的端点返回 `404` 和 `{"statusCode": 404, "message": "Cannot POST /v1/messages/batches"}`，这不是 Anthropic 的错误格式。EZAT 目前还没有拦截服务端工具和 MCP connector：请求会原样发给模型提供方，而提供方不会执行它们，所以预计会报错。不要发送它们。

## 给 AI agent 的说明

1. Base URL 为 `https://api.router.ezat.io`（不带 `/v1`：SDK 会自己加上）。key 从环境变量 `EZAT_API_KEY` 读取，以 `x-api-key` 或 `Authorization: Bearer` 发送。
2. 使用 `GET https://api.router.ezat.io/v1/models` 返回的模型 id，或 Anthropic 的模型名称（它是别名）。响应中的 `model` 原样返回你发送的值。
3. 从 `content` 中的 `text` 块读取文本，不要读 `content[0]`。
4. 可能超过几分钟的请求都要使用流式（`"stream": true`）。非流式请求在 590 秒后以 `504` `timeout_error` 结束。
5. 不要使用 Message Batches、Files API、服务端工具（网页搜索、代码执行）或 MCP connector：它们不可用。
6. 出错时，先查看 `error.type` 和[错误](https://docs.router.ezat.io/zh/reference/errors.md)，再决定是否重试。向支持团队报告 `request-id`。