# Messages（Anthropic 格式）

> POST /v1/messages 与 count_tokens 的接入、请求转发方式、流式事件、计费与错误格式。

来源：https://www.scirouter.cn/docs/messages · 更新于 2026-10-25

```http
POST https://api.scirouter.cn/v1/messages
POST https://api.scirouter.cn/v1/messages/count_tokens
```

请求与响应格式与 Anthropic Messages API 一致。Anthropic SDK、Claude Code 等按 Anthropic 格式发送请求的客户端，替换 base URL 与 Key 即可调用。平台上的所有对话模型均可通过此接口调用；模型的线路没有 Anthropic 入口时，网关转换请求并尽量保留内容，见[请求的转发方式](#请求的转发方式)。

## 接入

Anthropic SDK 与 Claude Code 会在 base URL 后拼接 `/v1/messages`，因此 **base URL 为 `https://api.scirouter.cn`，不带 `/v1`**。Key 为控制台创建的 API Key，携带方式见[认证与 API Key](https://www.scirouter.cn/docs/authentication)。

Python：

```python
import os
import anthropic

client = anthropic.Anthropic(
    api_key=os.environ["SCIROUTER_API_KEY"],
    base_url="https://api.scirouter.cn",  # SDK 自己会拼 /v1/messages
)

msg = client.messages.create(
    model="YOUR_MODEL_ID",
    max_tokens=1024,
    messages=[{"role": "user", "content": "这段序列的二级结构倾向？"}],
)
print(msg.content)
```

TypeScript：

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

const client = new Anthropic({
  apiKey: process.env.SCIROUTER_API_KEY,
  baseURL: 'https://api.scirouter.cn',
})

const msg = await client.messages.create({
  model: 'YOUR_MODEL_ID',
  max_tokens: 1024,
  messages: [{ role: 'user', content: '这段序列的二级结构倾向？' }],
})
```

curl：

```bash
curl https://api.scirouter.cn/v1/messages \
  -H "x-api-key: $SCIROUTER_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "YOUR_MODEL_ID",
    "max_tokens": 1024,
    "messages": [{"role": "user", "content": "这段序列的二级结构倾向？"}]
  }'
```

`anthropic-version` 可省略，默认值见[请求头](#请求头)。

## Claude Code

Claude Code 的安装、`settings.json` 写法、环境变量与使用要点见 [Claude Code](https://www.scirouter.cn/docs/claude-code)。

## 请求

| 字段 | 必填 | 说明 |
|---|---|---|
| `model` | 是 | 平台的模型 id，也可填写平台为该模型配置的别名（如厂商官方的模型名）。名称末尾带 `-YYYYMMDD` 日期时，去掉日期后查找。响应中的 `model` 为解析后的平台模型 id |
| `max_tokens` | 是 | 非负整数，处理方式见 [max_tokens](#maxtokens) |
| `messages` | 是 | 不能为空。`role` 可为 `user`、`assistant`，以及对话中途的 `system`（Claude Code 每个请求都带一条） |
| `stream` | 否 | `true` 时为流式，见[流式响应](#流式响应) |
| `tools` | 否 | 见[工具](#工具) |
| `metadata` | 否 | 不转给上游 |
| 其他字段 | 否 | 按线路处理，见[请求的转发方式](#请求的转发方式)；各参数无法带到线路上时的处理见[参数如何被处理](https://www.scirouter.cn/docs/params) |

请求体上限为 32 MB（对话补全为 4 MB），超过时返回 413 `payload_too_large`。

### max_tokens

| 情况 | 平台的处理 |
|---|---|
| 超过模型或套餐的单次输出上限 | 按上限发出，在响应头 `X-SciRouter-Max-Tokens-Clamped` 与 `scirouter.max_tokens_clamped` 中写明请求值、实际发出的值与生效的上限，不静默截短。此时 `thinking.budget_tokens` 若不小于发出的值，一并降为该值减 1（厂商要求思考预算小于 `max_tokens`） |
| 输入估算本身超过套餐的每分钟 token 数 | 返回 400 `invalid_request_error`（`error.code` 为 `validation_failed`，`details.reason` 为 `plan_tpm_exceeded`）。`message` 以 `prompt is too long: N tokens > M maximum` 开头（N 为输入估算，M 为套餐每分钟上限），末尾带 `capability_rejected: prompt_too_long`，Claude Code 据此尝试自动压缩对话后重发（压缩请求本身也超过上限时压缩失败，需 `/clear` 或升级套餐），见[错误码](https://www.scirouter.cn/docs/errors)。`max_tokens` 写多大都不因每分钟 token 数被拒：它只按输出预留计入，结束后按实际用量结算 |
| 值为 `0` | 只预热厂商的提示词缓存，不生成。只走 Anthropic 入口，模型的线路都没有 Anthropic 入口时返回 400，见[无法转发的内容](#无法转发的内容) |
| 输入加 `max_tokens` 超过模型的上下文窗口 | 400 `context_window_exceeded`，见[参数如何被处理](https://www.scirouter.cn/docs/params)「上下文窗口」 |

对话补全接口的 `max_tokens` 超限返回 400，与本接口不同。

### 工具

| 工具 | 处理 |
|---|---|
| 自定义工具 | 照常使用 |
| advisor 工具（`advisor_*`） | 只走 Anthropic 入口。其 `model` 须为平台上有定价的模型（正式 id 或别名），否则返回 400 `validation_failed`，`details.reason` 为 `sub_model_unpriced`；调用方须有该模型的调用权限，否则与直接调用一样返回 403。子推理按该模型的价格另行计费，见[计费](#计费) |
| 厂商的服务端工具（`web_search_*`、`web_fetch_*`、`code_execution_*`、`tool_search_tool_*`、`mcp_toolset`）与 `mcp_servers`、`container` | 不支持，返回 400 `validation_failed`，`details.reason` 为 `server_tool_unsupported` |

## 请求的转发方式

模型背后的每条线路对应同一个上游账号，可接入多种协议，每种协议是线路的一个入口（见[多厂商差异](https://www.scirouter.cn/docs/vendors)「字段生效规则」）。Anthropic 格式的调用在一条线路上按以下顺序处理，前一级无法处理时进入下一级：

| 级别 | 条件 | 处理 |
|---|---|---|
| 1. 原样转发 | 线路有 Anthropic 入口 | 请求原样转发，只修改少数几处 |
| 2. 转换 | 线路没有 Anthropic 入口 | 转为主入口的协议后调用，扩展字段能携带的内容均带上 |
| 3. 降级 | OpenAI 格式中没有对应概念的块 | 改写为等价的文字或普通工具，内容不丢失 |
| 4. 返回 400 | 无法用文字表达的内容 | 见[无法转发的内容](#无法转发的内容) |

每一级去掉或改写的内容记录在响应头 `X-SciRouter-Param-Actions` 与扩展块 `param_actions` 中（`dropped` / `reshaped`）。

### 原样转发

线路有 Anthropic 入口时，请求原样转发，只修改以下几处：

- `model` 换成上游的模型名；
- 去掉 `metadata`；
- `max_tokens` 写入生效值；
- 按平台的[提示词缓存](https://www.scirouter.cn/docs/caching)策略处理 `cache_control` 断点；
- 去掉签名为空的思考块。这类块来自其他模型的思考过程，厂商会因签名无效拒绝整个请求。

厂商自定义参数、思考块与签名、文档块、`anthropic-beta` 均到达上游，响应原样返回。「原样」指请求的格式与字段：鉴权、计费、限额与线路选择仍由平台完成，发给厂商的 `x-api-key` 是平台在该厂商的凭据，调用方的 SciRouter Key 不会到达厂商。

### 转换

线路没有 Anthropic 入口时，网关将请求转为主入口的协议后调用，再将结果转回 Anthropic 格式。只保留能对应的字段：

| 内容 | 转换方式 |
|---|---|
| 内容块 | 可转换 `text`、`image`、`document`（PDF、纯文本）、`tool_use`、`tool_result`、`thinking`、`redacted_thinking` |
| 工具结果中的图片 | 移入紧随其后的用户消息，并标注来自哪个工具调用 |
| 对话中途的 `system` 消息 | 在原位置成为一条 system 消息 |
| `system` 第一块为 Claude Code 的归因块（`x-anthropic-billing-header: …`） | 去掉（Anthropic 的接口同样去掉该块） |
| `cache_control` 断点 | 上游支持内容块上的 `cache_control` 时带上断点 |
| 思考块的签名 | 按线路配置，以上游支持的扩展写法连同签名带上 |
| `anthropic-beta` | 按线路配置转发 |
| `thinking` | 按模型的思考控制翻译为该线路的写法；模型没有思考能力时去掉并留痕 |
| `top_k` | 带给支持它的线路 |
| `speed` 等计价参数 | 不带 |
| `context_management` | `clear_tool_uses_*` 由平台在转换后的历史上补实现（保留最近几次工具调用，更早的工具结果换成占位）；`compact_*` 等其余编辑去掉并留痕 |
| `document` 块 | 转为该线路的文件内容段 |
| `output_format` / `output_config.format` 的 JSON Schema | 转为 `response_format`；模型没有原生结构化输出时由平台模拟 |
| 输出上限 | 按该线路支持的字段名写入 |

`cache_control` 断点、思考块的签名与 `anthropic-beta`，线路未配置的不带并留痕。字段对照见[多厂商差异](https://www.scirouter.cn/docs/vendors)与[参数如何被处理](https://www.scirouter.cn/docs/params)。

转回的响应与原生响应的差异：

| 项 | 差异 |
|---|---|
| 流式的 `message_start.usage` | 为 0，真实用量在 `message_delta.usage` 中 |
| 工具调用 | 在流的末尾整块发出，参数完整 |
| 命中停止序列 | `stop_reason` 为 `end_turn`，`stop_sequence` 为 null（OpenAI 格式不报告命中的是哪一条） |
| 上游给出 Anthropic 没有的结束原因 | 原样写入 `stop_reason`（如 `insufficient_system_resource`），不视为正常结束 |
| 思考签名 | 上游经扩展字段返回的思考签名，还原为带签名的 `thinking` 块 |

多轮工具调用时，将上一轮的思考块原样带回。平台优先将请求发回签发该签名的线路；落到其他线路时签名可能无效，厂商返回 400，平台原样交回该错误。

### 降级

OpenAI 格式中没有对应概念的块，改写为等价的文字或普通工具。内容完整保留，只缺少厂商对这些块的专门处理：

| 原内容 | 改写为 |
|---|---|
| `search_result` | 带来源与标题的文本块 |
| 文档块的 `citations` / `context` | 文字说明 |
| Anthropic 定义的客户端工具（`bash_*` / `text_editor_*` / `computer_*`） | 同名的普通函数工具 |
| 非文本的 `system` 块 | 并入第一条用户消息 |
| 历史中的服务端工具结果（`web_search_tool_result` / `web_fetch_tool_result`） | 带标题与来源的文本；`server_tool_use` 去掉 |

每条改写在 `param_actions` 中记为 `reshaped`。带这些内容的请求，模型有带 Anthropic 入口的线路时一定走该线路（优先于权重与粘性），没有时才降级。

### 无法转发的内容

以下内容无法用文字表达：

- Files API 来源的文件与图片（文件在厂商侧，平台无法获取）；
- 按需压缩 `compaction`；
- `max_tokens: 0`；
- 非 JSON Schema 的输出格式；
- 没有可提取文字的块与工具（代码执行结果、`memory_*` 等）。

模型没有带 Anthropic 入口的线路时，返回 400 `unsupported_param`，`details.reason` 为 `unsupported_content`，`details.unsupported_content` 列出相应内容（如 `["document:file", "param:compaction"]`）。平台不会丢弃这些内容后作答。该错误由请求内容引起，重试无效。

## 响应

非流式：

```json
{
  "id": "msg_…",
  "type": "message",
  "role": "assistant",
  "model": "YOUR_MODEL_ID",
  "content": [{ "type": "text", "text": "…" }],
  "stop_reason": "end_turn",
  "usage": { "input_tokens": 42, "output_tokens": 318 },
  "scirouter": { "request_id": "req_…", "attempts": 1, "cost_quota": 18240, "…": "…" }
}
```

数值为示例。

顶层的 `scirouter` 是平台的扩展块，内容与[对话补全](https://www.scirouter.cn/docs/chat-completions)相同，SDK 忽略该字段。单次调用扣除的配额见 `scirouter.cost_quota`。

## 流式响应

请求带 `"stream": true` 时按 Anthropic 的事件格式发送：每条事件带 `event:` 行，事件类型与 Anthropic 相同（`message_start`、`content_block_delta`……`message_stop`）。与对话补全的流式响应的差异：

- 没有 `data: [DONE]`，流以 `message_stop` 结束。平台在结算后才发送 `message_stop`，其中多一个 `scirouter` 字段（含本次的 `cost_quota`）。
- 开始输出后出错时，发送一个 `event: error` 事件（内容为[错误](#错误)中的错误体），随后断开。**未收到 `message_stop` 即结束的流按失败处理。**
- 流中有以 `:` 开头的心跳注释行，SDK 自动忽略。

非流式请求最长等待约 270 秒，超时返回 504 `upstream_timeout`（`details.timeout` 为 `response`），不计费；回答可能很长时使用流式，见 [SDK 与框架](https://www.scirouter.cn/docs/sdks)「重试与超时」。

## 计费

按厂商返回的 `usage` 结算，口径与厂商一致：`input_tokens`、`cache_read_input_tokens`、`cache_creation_input_tokens`（含 5 分钟与 1 小时两档写入）分别按各自的单价计费；思考 token 包含在 `output_tokens` 中，按输出单价计费。

厂商将一次请求中的多次采样拆分在 `usage.iterations` 中时（服务端压缩 `context_management` 的 `compact_*` 编辑、按需压缩 `compaction`、advisor 工具、`fallbacks` 回退），按 `iterations` 逐条计费：

| 迭代 | 计费 |
|---|---|
| 本模型的迭代（含压缩） | 并入本次用量 |
| advisor 子推理、回退模型的迭代 | 按该模型在平台上的价格另行计费 |

`scirouter.cost_quota` 为合计。`fallbacks` 写成模型列表时，每个模型都须在平台上有定价且调用方有权调用，否则返回 400 `sub_model_unpriced`；写 `"default"` 时由厂商选择模型，按其返回的模型结算。

## 错误

`/messages` 与 `/messages/count_tokens` 的错误使用 Anthropic 的格式，鉴权失败与路径或方法错误（404 / 405）也是如此：

```json
{
  "type": "error",
  "error": {
    "type": "rate_limit_error",
    "message": "请求过于频繁，请稍后再试",
    "code": "rate_limited",
    "details": { "retry_after_seconds": 12 }
  },
  "request_id": "req_88a1"
}
```

- 状态码与对话补全逐码相同，`error.code` 使用同一套小写错误码，按 `error.code` 分支处理，对照见[错误码](https://www.scirouter.cn/docs/errors)。
- `error.type` 按状态码给出；配额、权限、Key 状态等策略拒绝例外，见下表。

平台拒绝请求时的标记：

| 标记 | 出现条件 | Claude Code 的处理 |
|---|---|---|
| `message` 末尾带 `capability_rejected: <class>` | 平台自身拒绝、关闭对应能力即可恢复的 400（结构化输出、思考强度、图片、文档、超长等） | 关闭该能力后重发 |
| `error.type` 为 `policy_blocked`，带 `x-should-retry: false` | 配额、权限、Key 状态等策略拒绝 | 直接展示，不重试 |
| `message` 为 `prompt is too long: N tokens > M maximum` 的格式 | 上下文超长 | 见 [Claude Code](https://www.scirouter.cn/docs/claude-code)「上下文窗口」 |

## 计价参数与模型变体

部分参数会使厂商按另一档单价计费。计价参数由平台配置，当前为走 Anthropic 入口时的以下参数：

| 参数 | 按另一档计价的取值 |
|---|---|
| `speed` | `standard` 以外的值 |
| `inference_geo` | 任意取值 |
| `service_tier` | `auto`、`standard_only` 以外的值 |

模型的线路都没有 Anthropic 入口时，这些参数无法带到线路上，不做检查。请求带有计价参数时：

| 情况 | 结果 |
|---|---|
| 平台已上架覆盖这些参数的模型变体（id 格式为 `厂商/模型名:fast`） | 自动改用变体，按变体的价格结算。响应中的 `model` 为变体的 id，响应头 `X-SciRouter-Variant-Switch` 列出触发的参数（逗号分隔） |
| 没有对应的变体 | 400 `validation_failed`，`details.reason` 为 `price_param_unsupported`，`details.params` 列出参数 |

也可直接将 `model` 设为变体的 id，或在别名后加后缀（`别名:fast`）。模型的变体、各变体强制的参数与自动改用的格式，见[模型中心](https://www.scirouter.cn/models)的详情页或 `GET /models` 扩展字段中的 `variant_params` 与 `variant_switch_formats`（见[模型列表](https://www.scirouter.cn/docs/models)）。对话补全（OpenAI 格式）默认只记录，不改用变体。

## 统计 token 数

```bash
curl https://api.scirouter.cn/v1/messages/count_tokens \
  -H "x-api-key: $SCIROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "YOUR_MODEL_ID",
    "messages": [{"role": "user", "content": "这段序列的二级结构倾向？"}]
  }'
```

```json
{
  "input_tokens": 1834,
  "scirouter": { "model": "YOUR_MODEL_ID", "estimated": false, "request_id": "req_…" }
}
```

数值为示例。

| 项 | 说明 |
|---|---|
| 请求体 | 与 `/messages` 相同，不需要 `max_tokens`。`context_management` 原样转给厂商，计数结果为裁剪后、实际计费的上下文。可携带 `mcp_servers` 中的平台托管工具声明，与 `/messages` 一样摘除后计数 |
| 授权与限频 | 模型解析与授权与正式调用相同，计入一次每分钟请求数 |
| 计费与日志 | 不计费，不进入请求日志 |
| 计数方 | 模型有带 Anthropic 入口的可用线路时由厂商计数；没有、厂商 5 秒内未响应或报错时由平台估算（同一条线路故障后约 1 分钟内直接估算），此时 `scirouter.estimated` 为 `true` |

## 模型列表

`GET /models` 与 `GET /models/{id}` 带 `anthropic-version` 请求头时，按 Anthropic 的格式返回（Claude Code 与 Anthropic SDK 列出模型时会携带）：

```json
{
  "data": [{ "type": "model", "id": "YOUR_MODEL_ID", "display_name": "…", "created_at": "2026-09-01T00:00:00Z" }],
  "has_more": false,
  "first_id": "YOUR_MODEL_ID",
  "last_id": "YOUR_MODEL_ID"
}
```

一次返回全部模型，`has_more` 恒为 `false`；查询单个模型时也接受别名。不带该请求头时按 OpenAI 的格式返回，见[模型列表](https://www.scirouter.cn/docs/models)。

## 请求头与响应头

### 请求头

| 请求头 | 取值 | 说明 |
|---|---|---|
| `x-api-key` | API Key | 携带 Key 的另一种方式，见[认证与 API Key](https://www.scirouter.cn/docs/authentication) |
| `anthropic-version` | 如 `2023-06-01` | 走 Anthropic 入口时透传给上游，不带时默认为 `2023-06-01`；线路可按配置改成别的值，改了请求里带的值时记在控制台调用记录的「请求参数」里（`param` 为 `anthropic_version`）。在 `/models` 上决定返回哪种格式 |
| `anthropic-beta` | beta 名称 | 可写多个请求头或逗号分隔。走 Anthropic 入口时透传给上游；走其他入口转换时按线路配置转发，默认不转发。线路可按配置去掉其中几项，去掉时记在控制台调用记录的「请求参数」里（`scirouter.param_actions` 中 `param` 为 `anthropic_beta`、`reason` 为 `header_rule`） |
| `anthropic-dangerous-direct-browser-access` | — | Anthropic SDK 在浏览器中直连时携带，网关的跨源预检放行该请求头。Key 不应放入浏览器，见[认证与 API Key](https://www.scirouter.cn/docs/authentication) |
| `X-SciRouter-Unsupported-Params` | `strict` / `lenient` | `strict`：提示类参数无法带到线路上时也返回 400；`lenient`：契约类参数也去掉。见[参数如何被处理](https://www.scirouter.cn/docs/params) |
| `X-SciRouter-Validate-Schema` | `1` | 结构化输出由平台模拟时按 schema 校验结果，写入 `scirouter.schema_valid` |

### 响应头

| 响应头 | 取值 | 说明 |
|---|---|---|
| `request-id` | 与 `X-Request-Id` 相同 | Anthropic SDK 读取该名称。每个响应都带，包括鉴权与请求体校验失败的响应 |
| `X-SciRouter-Variant-Switch` | 触发的参数 | 请求被自动改用模型变体时出现，见[计价参数与模型变体](#计价参数与模型变体) |
| `X-SciRouter-Max-Tokens-Clamped` | 如 `requested=32000; effective=8192; limit=model` | `max_tokens` 超过模型或套餐的单次上限、按上限发出时出现；`limit` 为 `model` 或 `plan` |
| `X-SciRouter-Param-Actions` | 如 `dropped=top_k; translated=reasoning` | 有参数被去掉、翻译、模拟、整形或自愈时出现，见[参数如何被处理](https://www.scirouter.cn/docs/params) |
| `x-should-retry` | `false` | 本次错误重试无效（不带 `Retry-After` 的 503，以及 `policy_blocked` 的策略拒绝，见[错误码](https://www.scirouter.cn/docs/errors)）。Anthropic SDK 与 Claude Code 收到后不再自动重试 |

其余 `X-SciRouter-*`、`x-ratelimit-*` 响应头与对话补全相同，见[可观测性](https://www.scirouter.cn/docs/observability)。

## 缓存、幂等与重试

`Idempotency-Key` 与平台响应缓存的规则与对话补全相同，见[缓存与幂等](https://www.scirouter.cn/docs/caching)。

Anthropic SDK 默认自动重试 429 与 5xx，不检查 `error.code`，但先检查 `x-should-retry`：平台判定重试无效的 503 与策略拒绝（`policy_blocked`）带 `x-should-retry: false`，SDK 与 Claude Code 收到后不再重试。可重试的错误见[错误码](https://www.scirouter.cn/docs/errors)。
