第一次直接调 Claude 的接口:从 curl 到 Python,多轮对话、流式、看图,以及通过中转站要改的两处
不通过 Claude Code,自己写几行代码直接调模型,要哪些东西、请求和响应长什么样、多轮对话和流式怎么做。本文按官方的入门文档整理,每一步都通过中转站跑过。要改的只有接口地址和 key 两处。另外测到一个新手很容易踩的坑:max_tokens 给 100,回答是空的。
本站已实测,测试日期:2026-10-13。
本页目录(13 节)
这篇教程解决一个问题:不用 Claude Code,想在自己的脚本或程序里直接调 Claude,最少要做哪几件事?
一直用 Claude Code 的人迟早会碰到这个需求:批量处理一批文件、给自己的小工具加一个“问一下模型”的功能、写个定时脚本。这些用不着一整个智能体,一次接口调用就够了。
| 实测环境 | |
|---|---|
| 日期 | 2026-10-13 |
| 版本 | Anthropic Python SDK 1.13.0,Python 3.12.3,curl |
| 模型 | Sonnet 5.5 |
| 连接方式 | 通过酷站AI 中转站,不是直连官方 |
先分清:两种用法
官方把“用 Claude 做东西”分成两条路:
| Messages API | Claude Managed Agents | |
|---|---|---|
| 是什么 | 直接给模型发消息、拿回答 | 一套现成的、托管在云上的智能体框架 |
| 适合 | 自己写循环,要精细的控制 | 跑很久的任务、异步的活 |
这篇讲的是前一种。Claude Code 和 Agent SDK 底下用的也是它。
一、要准备的两样东西
一个接口地址和一把 key。
直连官方的话,地址是 https://api.anthropic.com,key 在官方控制台的 Settings → API keys 里创建,以 sk-ant- 开头,只在创建时显示一次。官方公布的支持地区名单里没有中国大陆、香港和澳门。
通过中转站的话,地址和 key 都换成中转站给你的。后面所有的代码,要改的就是这两处,别的一个字不用动。
把它们放进环境变量,不要写在代码里:
export ANTHROPIC_BASE_URL="中转站给的接口地址"
export ANTHROPIC_API_KEY="中转站给的 key"
官方 SDK 会自动读这两个变量。直连官方时不用设第一个。
二、第一个请求:curl
curl "$ANTHROPIC_BASE_URL/v1/messages" \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{
"model": "claude-sonnet-5-5",
"max_tokens": 300,
"messages": [{"role": "user", "content": "用一句话解释什么是 API。"}]
}'
请求里有三样是必须的:
| 字段 | 意思 |
|---|---|
model |
用哪个模型 |
max_tokens |
这次最多输出多少 token。必填 |
messages |
对话,一个列表,每条有 role(user 或 assistant)和 content |
三个请求头:
| 请求头 | 意思 |
|---|---|
x-api-key |
你的 key。也可以写成 Authorization: Bearer 你的key,官方现在把后一种当作正式写法 |
anthropic-version |
接口的版本,目前就是 2023-06-01 |
content-type |
固定是 application/json |
实测拿到的响应(删掉了几个暂时用不上的字段):
{
"id": "msg_011Cfy…",
"type": "message",
"role": "assistant",
"model": "claude-sonnet-5-5",
"content": [
{"type": "text", "text": "API(应用程序编程接口)是一套预先定义好的规则和约定,让不同的软件之间可以互相请求数据或调用功能,而无需了解对方的内部实现。"}
],
"stop_reason": "end_turn",
"usage": {"input_tokens": 48, "output_tokens": 63}
}
要看的三处:
content是一个列表,不是一个字符串。里面每一块有自己的类型,这次只有一块文字。stop_reason说明它为什么停。end_turn是自己说完了。usage是这次用了多少 token,计费按它算。
三、同一件事,用 Python
pip install anthropic
import anthropic
client = anthropic.Anthropic() # 自动读环境变量里的地址和 key
message = client.messages.create(
model="claude-sonnet-5-5",
max_tokens=300,
messages=[{"role": "user", "content": "用一句话解释什么是 API。"}],
)
text = "".join(block.text for block in message.content if block.type == "text")
print(text)
print(message.stop_reason, message.usage.input_tokens, message.usage.output_tokens)
取文字时按类型挑,不要写 message.content[0].text。现在的模型回答前面可能多一个思考块,第一块不一定是文字。
官方还有 TypeScript、Go、Java、C#、PHP、Ruby 的 SDK,用法是同一套。
四、一个很容易踩的坑:回答是空的
本站在测“加系统提示”时,把 max_tokens 随手写成了 100:
max_tokens=100 :stop_reason=max_tokens 内容块=['thinking'] output=100(思考 100) 文字=''
max_tokens=1000:stop_reason=end_turn 内容块=['text'] output=31(思考 0) 文字='吾无从知天时,君可观窗外云日,或查天气,再决出门与否。'
同一个请求,第一次模型先思考,100 个 token 全花在了思考上,文字一个字都没有,而这 100 个 token 照样计费。第二次它没有思考,31 个 token 就答完了。
要不要思考是模型每次自己决定的,你事先不知道。所以:
max_tokens留宽一些。 它是上限,不是你要付的数;没用完的不收钱。- 每次都看
stop_reason。 是max_tokens就说明被截断了。
思考的事在《Claude 的思考和 effort 怎么调》里讲得更细。
五、多轮对话:接口不记事
这是从聊天界面转过来的人最容易想错的地方:接口是无状态的,它不记得上一次请求。 想让它“接着聊”,要自己把之前的对话都发过去。
history = [{"role": "user", "content": "我叫小周,在做一个记账的小程序。记住这两点,回一个“好”。"}]
reply = client.messages.create(model="claude-sonnet-5-5", max_tokens=1000, messages=history)
history.append({"role": "assistant", "content": reply.content}) # 把它的回答原样放回去
history.append({"role": "user", "content": "我叫什么?在做什么?一句话。"})
reply2 = client.messages.create(model="claude-sonnet-5-5", max_tokens=1000, messages=history)
实测:
第 2 轮(带着历史):你叫小周,正在做一个记账的小程序。
同一个问题,不带历史:我不知道你叫什么,也看不出你在做什么……
各轮 input_tokens:66、86、51
两点:
- 把助手的回答放回去时,整个
content原样放,不要只取文字。里面可能有思考块和工具调用,带工具时少了它们会出错。 - 对话越长,每一轮的输入越多,也越贵。 第 2 轮的输入已经包含了第 1 轮的全部内容。长对话要靠提示缓存和压缩来控制。
系统提示不放在 messages 里,单独用 system 字段:
client.messages.create(
model="claude-sonnet-5-5", max_tokens=1000,
system="你是一个只用文言文回答的助手,回答不超过二十个字。",
messages=[{"role": "user", "content": "今天适合出门吗?"}],
)
以前有一种写法是在最后放一条 assistant 消息、写上回答的开头让它接着写(预填)。Claude 4.6 及之后的模型不支持了,官方接口会返回 400。
六、流式:边生成边显示
回答长的时候,等它全部生成完再显示,用户要干等。流式是让它生成一点就发一点。
用 SDK 最省事:
with client.messages.stream(
model="claude-sonnet-5-5", max_tokens=1000,
messages=[{"role": "user", "content": "用两句话解释什么是流式输出。"}],
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)
final = stream.get_final_message() # 完整的消息对象,和非流式拿到的一样
实测这两句话分 23 段到达,第一段在 1.9 秒,最后一段在 2.9 秒。把 23 段拼起来,和 get_final_message() 里的文字完全一致。
不需要逐段显示、只是怕长请求超时的话,可以只用最后那一行:让 SDK 替你把流读完,拿到的是一个完整的消息。SDK 在 max_tokens 很大时会要求你必须用流式。
自己发 HTTP 的话,请求里加 "stream": true,响应是一串事件。实测的顺序:
message_start → content_block_start → ping → content_block_delta×2 → content_block_stop → message_delta → message_stop
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"一、二、三、四、"}}
| 事件 | 内容 |
|---|---|
message_start |
开头。一个内容为空的消息对象 |
content_block_start |
一个内容块开始 |
content_block_delta |
这个块的一小段增量。文字是 text_delta,工具参数是 input_json_delta,思考是 thinking_delta |
content_block_stop |
这个块结束 |
message_delta |
快结束时来。stop_reason 和最终的输出 token 数在这里 |
message_stop |
结束 |
ping |
保持连接用的,随时可能出现,忽略它 |
两条要记的规矩:
- 遇到不认识的事件类型,跳过,不要报错。 官方保留了以后加新事件的权利。
- 报错也可能出现在流里。 响应已经是 200 了,中途还可能来一个
error事件,比如服务过载。
流中途断了怎么续:把已经收到的那部分存下来,再发一个请求让它从断的地方接着写。Claude 4.6 及之后的模型上,做法是加一条用户消息请它继续;思考块和工具调用是续不了的,只能从最近的一段文字接。
七、给它看图
图片放在 content 里,和文字并列,用 base64 编码:
message = client.messages.create(
model="claude-sonnet-5-5", max_tokens=1000,
messages=[{"role": "user", "content": [
{"type": "image", "source": {"type": "base64", "media_type": "image/png", "data": 图片的base64字符串}},
{"type": "text", "text": "这张图左半边和右半边各是什么颜色?一句话。"},
]}],
)
本站用代码生成了一张左红右蓝的小图发过去,回答是“左半边是红色,右半边是蓝色”,输入计了 73 个 token。
八、版本和 beta 头
版本。 anthropic-version 目前只有 2023-06-01 这一个在用的值。官方的承诺是:同一个版本下,已有的输入和输出参数不会变;但会加新的可选参数、往输出里加新的字段、加新的事件类型和枚举值。所以你的代码要能容忍响应里多出不认识的东西。
beta 头。 还在测试的功能要在请求里点名才能用:
anthropic-beta: 功能名1,功能名2
用 SDK 的话,走 client.beta 这个入口,功能名放在 betas 里:
client.beta.messages.create(
model="claude-sonnet-5-5", max_tokens=1000,
betas=["context-management-2025-06-27"],
messages=[...],
)
每个功能的文档里写着它的 beta 名。测试中的功能可能改、可能撤,价格和限流也可能不同。
九、接下来能做什么
接口的能力,官方分成五块:
| 包括什么 | 本站的文章 | |
|---|---|---|
| 模型的能力 | 思考、effort、结构化输出、引用、看图、读 PDF | 思考和 effort |
| 工具 | 让它调你写的函数,或者用官方的搜索、代码执行 | |
| 工具的基础设施 | 工具多的时候怎么按需加载 | |
| 上下文管理 | 缓存、压缩、清理 | 提示缓存、压缩和上下文编辑 |
| 文件 | 上传文件,在多次请求里复用 |
另外几篇用得上的:选哪个模型、多少钱,见《Claude 现在有哪些模型》;请求失败了怎么看,见《报错、限流和停止原因》。
顺带一提:官方文档里有一页专门“写给 Claude 看”的接口速查(API usage primer)。让 Claude 帮你写调接口的代码时,把那一页贴给它,它就不会用过时的写法。Claude Code 里自带的 /claude-api skill 做的是同一件事。
通过中转站实测到的
这篇里的每一段代码,通过酷站AI 都跑通了:
| 功能 | 结果 |
|---|---|
两种带 key 的写法(x-api-key 和 Authorization: Bearer) |
都认 |
| 非流式、流式 | 正常,流式的事件顺序和文档一致 |
多轮、system |
正常 |
| 图片输入 | 正常 |
client.models.list() |
返回 16 个模型 |
client.messages.count_tokens() |
能调,但数字和实际计费对不上,别用它估花费 |
client.beta.messages.create() |
能调 |
| 异步客户端同时发三个请求 | 正常 |
和直连官方不一样的地方,前面几篇各自写了,这里只列和入门有关的三条:
max_tokens一定要带。 漏了它,这家中转站返回的是一个让你“60 秒后重试”的 503,重试多少次都一样。- 模型名写完整。
claude-sonnet-5-5这样的,别名用不了。能用哪些模型以中转站的列表为准。 - 自己直接调接口并且要用缓存的话,请求里带一个固定的
metadata.user_id,否则缓存基本不命中。
花费
这一轮三十来次请求,都是很短的问答,按官方价估算不到 0.05 美元。
这次实测没有覆盖到的
- 官方接口没有测,本站没有官方的 key。创建 key 的步骤是照文档写的。
- 只用了 Python 和 curl。 其他语言的 SDK 没有试。
- 流式的断线续传、流里的报错事件没有碰到,也没有去造。
- PDF、用网址给图片、文件接口没有试。这家中转站没有开文件接口。
- Managed Agents 完全没有涉及。
来源
- Get started with Claude(Claude 开发平台文档)
- Intro to Claude(Claude 开发平台文档)
- Get your Claude API key(Claude 开发平台文档)
- API overview(Claude 开发平台文档)
- Using the Messages API(Claude 开发平台文档)
- Streaming messages(Claude 开发平台文档)
- Versions(Claude 开发平台文档)
- Beta headers(Claude 开发平台文档)
- Features overview(Claude 开发平台文档)
- API usage primer for Claude(Claude 开发平台文档)