# Messages（Anthropic 格式）

> /v1/messages 与 count_tokens：用 Anthropic SDK 或 Claude Code 接入，以及它和对话补全不一样的地方。

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

```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 就能用；
平台上的对话模型都能这样调，不限于 Claude——背后线路不是 Anthropic 协议的，网关会替你转换（见下文「请求怎么到上游」）。

## 接入

Anthropic SDK 与 Claude Code 会自己在 base URL 后面拼 `/v1/messages`，所以 **base URL 写到域名为止，不带 `/v1`**：
把 `https://api.scirouter.cn/v1` 末尾的 `/v1` 去掉。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/v1".removesuffix("/v1"),  # SDK 自己会拼 /v1/messages
)

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

TypeScript：

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

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

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

curl（`anthropic-version` 可以不带，转发给 Anthropic 协议的上游时默认 `2023-06-01`）：

```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": "这段序列的二级结构倾向？"}]
  }'
```

`x-api-key` 与 `Authorization: Bearer` 两种写法都认；两个都带时必须是同一把 Key，否则 401 `unauthenticated`。
`Authorization: Bearer` 后面是空的，等于没带这个头。

## Claude Code

```bash
export SCIROUTER_GATEWAY="https://api.scirouter.cn/v1"
export ANTHROPIC_BASE_URL="${SCIROUTER_GATEWAY%/v1}"   # 去掉末尾的 /v1
export ANTHROPIC_AUTH_TOKEN="$SCIROUTER_API_KEY"        # 以 Authorization: Bearer 发送
export ANTHROPIC_MODEL="YOUR_MODEL_ID"
export ANTHROPIC_DEFAULT_HAIKU_MODEL="YOUR_MODEL_ID" # 后台小任务用的模型，也要换成平台上的
export CLAUDE_CODE_MAX_OUTPUT_TOKENS=8192               # 写所选模型的输出上限，Claude Code 才按真实上限规划
claude
```

- **Key 设一个变量就够**：`ANTHROPIC_AUTH_TOKEN`（发 `Authorization: Bearer`）或 `ANTHROPIC_API_KEY`（发 `x-api-key`）。变量名是 Claude Code 定的，值填你在 SciRouter 控制台创建的 API Key（`sk-sr-` 开头），不是 Anthropic 官方的 Key。两个都设、却不是同一把 Key 的请求会被 401 拒绝；
  其中一个是空串（发出空的 `Authorization: Bearer`）没关系，按没带处理。
- **输出上限**：Claude Code 不认识平台的模型 id 时，默认每次要 32000 个输出 token。超过模型或套餐的单次上限时，平台**按上限发出**（不报错），
  响应头 `X-SciRouter-Max-Tokens-Clamped`（如 `requested=32000; effective=8192; limit=model`）与扩展块的 `scirouter.max_tokens_clamped` 写明请求值、
  实际发出的值与是哪一道上限；回答写到上限时 `stop_reason` 是 `max_tokens`，和厂商自己截断时一样。想让 Claude Code 按真实上限规划，
  用 `CLAUDE_CODE_MAX_OUTPUT_TOKENS` 设成模型的上限，上限在[模型中心](https://www.scirouter.cn/models)的详情页里。用 `/model` 切换 sonnet / opus 时，
  还要设 `ANTHROPIC_DEFAULT_SONNET_MODEL`、`ANTHROPIC_DEFAULT_OPUS_MODEL`。
- **WebSearch 用不了**：它是厂商的服务端工具，平台暂不支持（400，`details.reason` 为 `server_tool_unsupported`）。
- **模型列表**：设了 `CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY=1` 时，Claude Code 启动时取一次平台的模型列表放进 `/model`，但它只留 id 里带 `claude` 或 `anthropic` 的；
  其余模型用 `ANTHROPIC_MODEL` 直接写平台的模型 id。
- **上下文窗口**：Claude Code 不认识的模型 id 一律按 20 万 token 算何时压缩对话。所选模型的窗口更小时，按 Claude Code 文档在它的模型配置里写明真实窗口，
  否则对话长了会撞上模型的上限。
- **背后是别家的 Anthropic 兼容接口时**：Claude Code 发的是 Claude 官方接口认的全套字段（`thinking` 的 `adaptive`、对话中途的 `system` 消息、
  `context_management`、`output_config`），这类线路原样转发，厂商不认就会 400。前几样 Claude Code 会自己去掉后重试；`context_management` 不会，
  遇到 400 `context_management: Extra inputs are not permitted` 时设 `CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS=1`。转换走法（OpenAI 等协议的线路）不受影响。

## 请求

| 字段 | 必填 | 说明 |
|---|---|---|
| `model` | 是 | 平台的模型 id；也可以写后台给这个模型配的别名（例如厂商官方的模型名），名字末尾带 `-YYYYMMDD` 日期时会去掉日期再找。响应里的 `model` 是解析后的平台 id |
| `max_tokens` | 是 | 正整数。超过模型或套餐的单次输出上限时按上限发出，并在响应头 `X-SciRouter-Max-Tokens-Clamped` 与 `scirouter.max_tokens_clamped` 里写明（不会悄悄截短）；这时 `thinking.budget_tokens` 若不再小于发出的值，一并降到它减 1（厂商要求思考预算小于 `max_tokens`）。对话补全的 `max_tokens` 超限仍是 400 |
| `messages` | 是 | 不能为空。`role` 可以是 `user`、`assistant`，以及对话中途的 `system`（Claude Code 每个请求都有一条） |
| `stream` | 否 | `true` 时流式，见下文 |
| `tools` | 否 | 自定义工具照常用。厂商的服务端工具（`web_search_*`、`web_fetch_*`、`code_execution_*`、`tool_search_tool_*`、`mcp_toolset`）与 `mcp_servers`、`container` 暂不支持：400 `validation_failed`，`details.reason` 为 `server_tool_unsupported` |
| `metadata` | 否 | 不转给上游 |
| 其他字段 | 否 | 按线路处理，见下一节 |

请求体上限 32 MB（整段对话加截图常常超过对话补全的 4 MB），超过 413 `payload_too_large`。

## 请求怎么到上游

- **模型背后是 Anthropic 协议的线路**：请求原样转发，只改几处——`model` 换成上游的名字、去掉 `metadata`、`max_tokens` 写生效值、
  按平台的[提示词缓存](https://www.scirouter.cn/docs/caching)策略处理 `cache_control` 断点、去掉签名为空的思考块（它们来自别家模型的思考过程，厂商会因签名无效拒绝整个请求）。
  厂商自定义参数、思考块与签名、文档块、`anthropic-beta` 都会到达上游，响应也原样带回。
  「原样」说的是请求的格式与字段：鉴权、计费、限额与选线路照旧由平台做，发给厂商的 `x-api-key` 是平台在那家的凭据，你的 SciRouter Key 不会到达厂商。
- **其他协议的线路**（OpenAI、Gemini 等）：网关把请求转成 OpenAI 格式再调，结果再转回 Anthropic 的形状。只有对得上的字段会留下，
  能转换的内容块是 `text`、`image`、`tool_use`、`tool_result`、`thinking`、`redacted_thinking`：工具结果里的图片挪进紧随的用户消息（前面标注来自哪个工具调用），
  对话中途的 `system` 消息就地成为一条 system 消息，`system` 第一块若是 Claude Code 的归因块（`x-anthropic-billing-header: …`）则去掉（Anthropic 的接口同样去掉它）；
  思考块、`cache_control` 断点与 `thinking`、`top_k`、`speed` 这类 Anthropic 特有参数不带过去；`output_format` / `output_config.format` 的 JSON Schema
  转成 `response_format`；发给 OpenAI 官方时输出上限写 `max_completion_tokens`。字段对照见[多厂商差异](https://www.scirouter.cn/docs/vendors)。
  转回来的响应与原生的几处差别：流式的 `message_start.usage` 为 0，真实用量在 `message_delta.usage`；工具调用在流的末尾整块发出（参数完整）；
  命中停止序列时 `stop_reason` 是 `end_turn`、`stop_sequence` 为 null（OpenAI 不报告命中的是哪一条）；上游给出 Anthropic 没有的结束原因时原样写进
  `stop_reason`（如 `insufficient_system_resource`），不当成正常结束。
- **请求里有只能原样转发的内容**（`document`、`search_result`、工具结果里的文档、`context_management`——只清思考块的编辑除外，转换走法本来就不带思考块——、非 JSON Schema 的输出格式）：只走 Anthropic 协议的线路；
  这个模型没有这种线路时返回 503 `model_unavailable`，`details.reason` 为 `no_capable_endpoint`，`details.unsupported_content` 列出是哪些内容
  （如 `["block:document"]`）——平台不会丢掉它们再作答。

## 响应

非流式：

```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 会忽略。

非流式请求最长约 300 秒，回答可能很长时用流式（见 [SDK 与框架](https://www.scirouter.cn/docs/sdks)「重试与超时」）。

## 错误

`/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.type` 按状态码给。按 `code` 分支，对照见[错误码](https://www.scirouter.cn/docs/errors)。

## 计价参数与模型变体

有些参数会让厂商按另一档单价收费。哪些参数算由平台配置，目前是 Anthropic 协议线路上的 `speed`（取 `standard` 以外的值）、`inference_geo`
与 `service_tier`（取 `auto`、`standard_only` 以外的值）；模型只有其他协议的线路时这些参数本来就带不过去，不检查。请求里带了它们时：

- 平台上架了覆盖这些参数的**模型变体**（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：count_tokens

```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`；
- 模型解析与授权和真正调用时一样，算一次每分钟请求数；**不计费，不进请求日志**；
- 模型有可用的 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` | 请求 | 带 Key 的另一种写法，见上文 |
| `anthropic-version` | 请求 | 透传给 Anthropic 协议的上游；不带时默认 `2023-06-01`。在 `/models` 上它决定返回哪种形状 |
| `anthropic-beta` | 请求 | 可以写多个头或逗号分隔，透传给 Anthropic 协议的上游 |
| `anthropic-dangerous-direct-browser-access` | 请求 | Anthropic SDK 在浏览器里直连时带的头，网关的跨源预检放行它。但 Key 不要放进浏览器（见[认证与 API Key](https://www.scirouter.cn/docs/authentication)） |
| `request-id` | 响应 | 与 `X-Request-Id` 同一个值，Anthropic SDK 读这个名字；每个响应都带，含鉴权与请求体校验失败的 |
| `X-SciRouter-Variant-Switch` | 响应 | 这次请求被自动改走了模型变体，值是触发的参数，见上文 |
| `X-SciRouter-Max-Tokens-Clamped` | 响应 | `max_tokens` 超过模型或套餐的单次上限、按上限发出时出现，形如 `requested=32000; effective=8192; limit=model`（`limit` 为 `model` 或 `plan`），见上文 |
| `x-should-retry` | 响应 | 值为 `false` 时这次错误重试不会好（不带 `Retry-After` 的 503，见[错误码](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 带 `x-should-retry: false`，SDK 与 Claude Code 就不再重试。哪些值得重试见[错误码](https://www.scirouter.cn/docs/errors)。
