# curl

URL: https://docs.router.ezat.io/zh/examples/curl

> 两种 API 格式的 curl 请求，可直接复制使用：普通请求、流式输出、工具调用往返、图片、计算 token 数、列出模型和处理错误。

## 何时阅读本页

你想在终端里试用 API，或者想看 SDK 调用背后的完整 HTTP 请求。同样的请求在代码中的写法见 [Node.js](https://docs.router.ezat.io/zh/examples/node.md) 和 [Python](https://docs.router.ezat.io/zh/examples/python.md)。

## 开始之前

在控制台（https://router.ezat.io/zh/keys）创建一个 key，并把它放进环境变量：

```bash
export EZAT_API_KEY="ezat_sk_…"
```

这里的每个请求都使用两种格式之一。选择你的代码或工具使用的格式：

| 格式 | 端点 | key 请求头 |
| --- | --- | --- |
| Anthropic | `https://api.router.ezat.io/v1/messages` | `x-api-key: $EZAT_API_KEY`（或 `Authorization: Bearer`） |
| OpenAI | `https://api.router.ezat.io/openai/v1/chat/completions` | `Authorization: Bearer $EZAT_API_KEY` |

`--fail-with-body` 让 curl 在遇到任何 HTTP 错误时以错误状态退出，同时仍然打印错误响应体。`anthropic/claude-sonnet-5-5` 是这些示例使用的模型；[模型](https://docs.router.ezat.io/zh/models.md)中的任何 id 都可以。

## 普通请求

```bash tab="Anthropic format" test
curl -sS --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."}]
  }'
```

```bash tab="OpenAI format" test
curl -sS --fail-with-body https://api.router.ezat.io/openai/v1/chat/completions \
  -H "Authorization: Bearer $EZAT_API_KEY" \
  -H "content-type: application/json" \
  -d '{
    "model": "anthropic/claude-sonnet-5-5",
    "max_completion_tokens": 64,
    "messages": [{"role": "user", "content": "Say hello in five words."}]
  }'
```

响应包含文本，以及一个 `usage` 对象，里面是计费所依据的 token 数。各类 token 的价格见[价格](https://docs.router.ezat.io/zh/pricing.md)。

## 流式输出

加上 `"stream": true`。`-N` 让 curl 在每个事件到达时立即打印出来。

```bash tab="Anthropic format" test
curl -sS -N --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,
    "stream": true,
    "messages": [{"role": "user", "content": "Count from one to five."}]
  }'
```

```bash tab="OpenAI format" test
curl -sS -N --fail-with-body https://api.router.ezat.io/openai/v1/chat/completions \
  -H "Authorization: Bearer $EZAT_API_KEY" \
  -H "content-type: application/json" \
  -d '{
    "model": "anthropic/claude-sonnet-5-5",
    "max_completion_tokens": 64,
    "stream": true,
    "stream_options": {"include_usage": true},
    "messages": [{"role": "user", "content": "Count from one to five."}]
  }'
```

Anthropic 格式会发送 `message_start`、`content_block_delta`、`message_delta` 和 `message_stop` 事件，长时间停顿期间还会发送 `ping` 事件。OpenAI 格式发送 `chat.completion.chunk` 事件，并以 `data: [DONE]` 结束；使用 `include_usage` 时，在它之前会有一个带 usage、`choices` 为空的 chunk。

## 工具调用往返

### 第 1 步：提供工具

```bash tab="Anthropic format" test
curl -sS --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,
    "tools": [{
      "name": "get_weather",
      "description": "Get the current weather in a city.",
      "input_schema": {"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]}
    }],
    "messages": [{"role": "user", "content": "What is the weather in Paris? Use the get_weather tool."}]
  }'
```

```bash tab="OpenAI format" test
curl -sS --fail-with-body https://api.router.ezat.io/openai/v1/chat/completions \
  -H "Authorization: Bearer $EZAT_API_KEY" \
  -H "content-type: application/json" \
  -d '{
    "model": "anthropic/claude-sonnet-5-5",
    "max_completion_tokens": 64,
    "tools": [{
      "type": "function",
      "function": {
        "name": "get_weather",
        "description": "Get the current weather in a city.",
        "parameters": {"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]}
      }
    }],
    "messages": [{"role": "user", "content": "What is the weather in Paris? Use the get_weather tool."}]
  }'
```

模型会以工具调用作答：Anthropic 格式中是一个 `tool_use` 块和 `"stop_reason": "tool_use"`，OpenAI 格式中是 `tool_calls` 和 `"finish_reason": "tool_calls"`。

### 第 2 步：发回结果

自己运行工具，然后重新发送整个对话：你的消息、模型的工具调用，以及工具结果。示例中写 `toolu_example` 或 `call_example` 的地方，请换成工具调用自己的 `id`。

```bash tab="Anthropic format" test
curl -sS --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,
    "tools": [{
      "name": "get_weather",
      "description": "Get the current weather in a city.",
      "input_schema": {"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]}
    }],
    "messages": [
      {"role": "user", "content": "What is the weather in Paris? Use the get_weather tool."},
      {"role": "assistant", "content": [
        {"type": "tool_use", "id": "toolu_example", "name": "get_weather", "input": {"city": "Paris"}}
      ]},
      {"role": "user", "content": [
        {"type": "tool_result", "tool_use_id": "toolu_example", "content": "Sunny and 18 degrees Celsius"}
      ]}
    ]
  }'
```

```bash tab="OpenAI format" test
curl -sS --fail-with-body https://api.router.ezat.io/openai/v1/chat/completions \
  -H "Authorization: Bearer $EZAT_API_KEY" \
  -H "content-type: application/json" \
  -d '{
    "model": "anthropic/claude-sonnet-5-5",
    "max_completion_tokens": 64,
    "tools": [{
      "type": "function",
      "function": {
        "name": "get_weather",
        "description": "Get the current weather in a city.",
        "parameters": {"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]}
      }
    }],
    "messages": [
      {"role": "user", "content": "What is the weather in Paris? Use the get_weather tool."},
      {"role": "assistant", "content": null, "tool_calls": [
        {"id": "call_example", "type": "function", "function": {"name": "get_weather", "arguments": "{\"city\": \"Paris\"}"}}
      ]},
      {"role": "tool", "tool_call_id": "call_example", "content": "Sunny and 18 degrees Celsius"}
    ]
  }'
```

这时模型会根据结果用文本作答。

## 图片

图片以 base64 内嵌发送。这里是一张 8×8 的红色 PNG。如果是真实文件，请把请求体写进文件，再用 `-d @body.json` 发送，因为大图片放不进命令行。

```bash tab="Anthropic format" test
curl -sS --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": [
      {"type": "image", "source": {"type": "base64", "media_type": "image/png",
        "data": "iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAIAAABLbSncAAAAEUlEQVR42mP4z8CAFTEMLQkAKP8/wc53yE8AAAAASUVORK5CYII="}},
      {"type": "text", "text": "What colour is this image? Answer in one word."}
    ]}]
  }'
```

```bash tab="OpenAI format" test
curl -sS --fail-with-body https://api.router.ezat.io/openai/v1/chat/completions \
  -H "Authorization: Bearer $EZAT_API_KEY" \
  -H "content-type: application/json" \
  -d '{
    "model": "anthropic/claude-sonnet-5-5",
    "max_completion_tokens": 64,
    "messages": [{"role": "user", "content": [
      {"type": "image_url", "image_url": {"url": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAIAAABLbSncAAAAEUlEQVR42mP4z8CAFTEMLQkAKP8/wc53yE8AAAAASUVORK5CYII="}},
      {"type": "text", "text": "What colour is this image? Answer in one word."}
    ]}]
  }'
```

支持 JPEG、PNG、GIF 和 WebP。OpenAI 格式的 `image_url` 还可以填 `https://` URL，由网关下载（每张图片最大 10 MB）。

## 计算 token 数

仅限 Anthropic 格式。计算 token 数免费、不计费，但会计入 key 的每分钟请求数。

```bash test
curl -sS --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": "Say hello in five words."}]
  }'
```

响应为 `{"input_tokens": …}`。

## 列出模型

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

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

它们列出你的 key 能调用的模型。如需不带 key 获取所有模型及其价格，请用 `curl -sS https://api.router.ezat.io/api/models`。见[模型](https://docs.router.ezat.io/zh/models.md)。

## 处理错误

这个请求使用了一个不存在的模型，所以会以 404 失败。`-D headers.txt` 会保存响应头，里面有联系支持时需要提供的 id。

```bash tab="Anthropic format" test
if curl -sS --fail-with-body -D headers.txt 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/no-such-model", "max_tokens": 16, "messages": [{"role": "user", "content": "Hi"}]}'
then
  echo "Expected an error" >&2
  exit 1
fi
echo
grep -iE '^(HTTP/|request-id|x-request-id|retry-after)' headers.txt
```

```bash tab="OpenAI format" test
if curl -sS --fail-with-body -D headers.txt https://api.router.ezat.io/openai/v1/chat/completions \
  -H "Authorization: Bearer $EZAT_API_KEY" \
  -H "content-type: application/json" \
  -d '{"model": "anthropic/no-such-model", "max_completion_tokens": 16, "messages": [{"role": "user", "content": "Hi"}]}'
then
  echo "Expected an error" >&2
  exit 1
fi
echo
grep -iE '^(HTTP/|request-id|x-request-id|retry-after)' headers.txt
```

curl 会打印错误响应体，例如 `{"type":"error","error":{"type":"not_found_error","message":"model: anthropic/no-such-model"},…}`，并以退出码 22 退出。在脚本中，只对 429、500、503 和 529 重试；有 `retry-after` 时，等待相应的秒数后再重试。所有状态码和错误类型见[错误](https://docs.router.ezat.io/zh/reference/errors.md)。

## 给 AI agent 的说明

1. 从 `$EZAT_API_KEY` 读取 key；绝不要把 key 直接粘贴到命令中。
2. Anthropic 格式：带 `x-api-key` 请求 `POST https://api.router.ezat.io/v1/messages`，`max_tokens` 为必填项。OpenAI 格式：带 `Authorization: Bearer` 请求 `POST https://api.router.ezat.io/openai/v1/chat/completions`。
3. 在脚本中使用 `--fail-with-body`，这样 HTTP 错误会让脚本停止，同时仍然显示错误响应体。
4. 从 `GET https://api.router.ezat.io/v1/models` 获取模型 id，不要猜。