OpenAI API 如何计算 Token 用量?
简答:OpenAI API 的 Token 用量通常分三步确认:纯文本可用 tiktoken 做粗略统计;包含历史消息、工具、图片或文件时,用 Responses API 的输入 Token 计数接口;请求完成后,再以响应中的 usage 字段作为单次调用的实际用量。费用不能只看字符数,应按不同 Token 类别和当前模型官方价格分别计算。截至 2026-11-03,官方文档将 Chat Completions 的字段写作 prompt_tokens、completion_tokens、total_tokens,将 Responses 的字段写作 input_tokens、output_tokens、total_tokens。
定义:Token 是模型处理文本或其他输入时使用的计量单位,一个 Token 可能对应字符、单词的一部分、完整单词或标点,Token 数不等于字数。
先看结论:不同场景该用哪种计算方法
如果只是想快速判断一段纯文本的大致长度,可以使用 Tokenizer 或 tiktoken;如果请求中包含消息角色、历史对话、工具定义、JSON Schema、图片或文件,应使用官方输入 Token 计数接口;如果请求已经发送,则以 API 响应中的 usage 为准。三种方法的准确性和适用范围不同,不能把本地纯文本统计当成完整请求的最终用量。
| 场景 | 推荐方法 | 能否代表最终完整用量 |
|---|---|---|
| 单段纯文本、提示词草稿 | Tokenizer 或 tiktoken | 通常只能估算纯文本部分 |
| 消息、历史对话、工具、结构化 Schema | Responses API 输入 Token 计数接口 | 适合核对完整输入 |
| 请求已经完成 | 读取响应中的 usage 字段 | 最适合记录单次实际用量 |
| 按月或按项目统计 | 用量仪表板和导出数据 | 用于周期性汇总,不替代单次响应记录 |
官方说明指出,消息结构、工具、架构、图像和文件都会影响完整输入 Token;本地 tokenizer 对图片和文件不能直接提供完整计数。因此,复杂请求不要只用字符数除以某个固定数字来推算。
Token、输入和输出分别是什么意思
输入 Token是发送给模型的内容,包括指令、用户消息、历史对话以及部分请求结构;在 Chat Completions 中通常对应 prompt_tokens,在 Responses 中对应 input_tokens。
输出 Token是模型生成的内容;在 Chat Completions 中通常对应 completion_tokens,在 Responses 中对应 output_tokens。推理模型还可能产生不会直接显示在最终答案中的推理 Token,官方说明会将其计入输出用量,因此可见文字很短,不代表输出用量一定很小。
缓存输入 Token是重复使用的输入部分,是否出现以及具体价格要以当前模型和官方定价说明为准。总 Token通常可理解为输入与输出等计费类别的合计,但详细字段可能因端点和模型不同而增加缓存或推理明细。
英文常见的“约 4 个字符对应 1 个 Token”只能作为粗略估算,不能直接套用于中文、代码、JSON、Markdown 或混合文本。空格、大小写、标点、编码和模型差异都可能改变切分结果。
第一步:本地估算纯文本 Token
本地统计适合在发送请求前快速比较提示词长度、判断历史对话是否持续变长,或在不调用 API 的情况下做初步预算。官方文档建议针对目标模型选择对应编码,并提醒 tiktoken 主要适用于纯文本。
Python 示例:
import tiktoken
model = "YOUR_MODEL"
text = "请把这段文字总结成三点。"
encoding = tiktoken.encoding_for_model(model)
print(len(encoding.encode(text)))
上面代码中的 YOUR_MODEL 需要替换为你实际调用且由当前官方文档支持的模型。若模型无法被本地库识别,不要自行猜测编码,应根据当前模型文档或使用官方输入 Token 计数接口。
注意:这段代码只统计变量 text 的纯文本,不会自动补上消息角色、历史对话边界、工具定义、结构化输出 Schema、图片和文件等完整请求内容。
第二步:请求前精确计算完整输入 Token
当请求包含多轮消息、系统指令、工具或多模态输入时,可以调用 Responses API 的输入 Token 计数接口。官方文档给出的端点是 POST /v1/responses/input_tokens,返回对象中包含整数型 input_tokens。
最小 cURL 结构如下:
curl https://api.openai.com/v1/responses/input_tokens \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "YOUR_MODEL",
"input": "请总结这段内容。"
}'
如果要计算多轮对话,应把实际发送的消息数组放入 input;如果要计算工具影响,则把实际工具定义一并传入;图片或文件也应使用实际请求格式。这样得到的结果比只对可见文字运行 tiktoken 更接近完整输入用量。
官方文档特别说明,工具和 Schema 会增加难以在本地准确计算的 Token,图片还会根据尺寸和 detail 设置影响用量。需要核对时,尽量让计数请求与正式请求使用相同的模型、指令、输入、工具和附件。
第三步:请求完成后读取实际 usage
请求完成后,应保存响应中的 usage,而不是根据返回文字长度反推。Chat Completions 和 Responses 的字段名称不同,不能混用。
| 含义 | Chat Completions | Responses |
|---|---|---|
| 输入 Token | usage.prompt_tokens | usage.input_tokens |
| 输出 Token | usage.completion_tokens | usage.output_tokens |
| 总 Token | usage.total_tokens | usage.total_tokens |
Chat Completions 的 Python 读取示例:
response = client.chat.completions.create(
model="YOUR_MODEL",
messages=[{"role": "user", "content": "你好"}]
)
usage = response.usage
print(usage.prompt_tokens)
print(usage.completion_tokens)
print(usage.total_tokens)
Responses 的读取方式取决于你使用的 SDK 版本和调用形式,核心是检查响应对象中的 usage.input_tokens、usage.output_tokens 和 usage.total_tokens。部署前应以当前 API Reference 和本机 SDK 帮助为准,不要仅凭旧代码推断字段。
如果使用流式 Chat Completions,官方文档说明可以设置 stream_options.include_usage 为 true,在结束标记前获取一个包含整体 usage 的额外数据块。若连接在最后数据块到达前中断,未收到 usage 不代表本次请求没有消耗 Token,应保留请求 ID、最后事件和脱敏日志后再通过用量记录核对。
Token 用量如何换算成费用
通用计算思路是把不同类别的 Token 分开,再分别套用当前模型官方单价:
费用 = 输入 Token ÷ 计价单位 × 输入单价
+ 缓存输入 Token ÷ 计价单位 × 缓存输入单价
+ 输出 Token ÷ 计价单位 × 输出单价
这里的“计价单位”、模型名称、输入单价、缓存输入单价和输出单价必须从当前官方定价页面核实。本文不写固定价格,因为价格和模型计费类别可能变化。推理 Token 是否以额外明细呈现,也要结合所用模型和响应字段确认。
一个可审计的成本记录至少应保存:请求时间、模型、端点、输入 Token、输出 Token、缓存输入或推理 Token 明细、请求 ID,以及当日采用的官方价格版本。不要用“回复有多少字”替代 API 返回的 usage。
中文、代码、JSON 和历史对话为什么更容易超出预期
- 中文:不能直接套用英文字符与 Token 的粗略比例,不同文本和模型的切分结果可能不同。
- 代码:符号、缩进、路径、变量名和重复片段都会进入输入,长代码通常应先做文件筛选或摘要。
- JSON 和 Schema:字段名、描述、枚举值和嵌套结构都会增加输入,工具定义也属于需要计数的请求内容。
- 历史对话:如果每轮都把旧消息重新发送,旧内容会持续进入新的输入 Token 计算。
- 图片和文件:不能只按文件名或提示词长度估算,官方输入 Token 计数接口更适合核对完整请求。
- 输出限制:输入 Token 与输出 Token 是不同部分;应根据所用端点支持的参数和模型限制设置输出上限。
一份可执行的 Token 核对清单
- 先确定实际使用的端点、模型和请求格式,不要把 Chat Completions 字段直接套到 Responses。
- 只有纯文本时,用 Tokenizer 或 tiktoken 做本地估算。
- 包含对话、工具、Schema、图片或文件时,使用输入 Token 计数接口计算完整输入。
- 正式请求完成后,读取并保存响应中的 usage;流式请求要确认是否收到最终 usage 数据块。
- 按输入、缓存输入和输出等类别分别套用当前官方定价,不使用过期价格。
- 将请求 ID、模型、Token 数和时间写入脱敏日志,方便与用量仪表板按 UTC 时间核对。
本页是独立的 AI API 接入与排查指南,不是任何品牌的官网或官方下载页面;涉及模型、价格和 SDK 行为时,请以当前官方文档、API 响应和控制台显示为准。
常见问题
OpenAI API 可以直接按字数计算 Token 吗?
不可以把字数当作精确 Token 数。Token 受语言、模型、编码、空格、大小写、标点和请求结构影响。纯文本可以用 tiktoken 做估算,完整请求应使用输入 Token 计数接口或读取 API 返回的 usage。
Chat Completions 和 Responses 的 Token 字段一样吗?
不一样。Chat Completions 通常使用 prompt_tokens、completion_tokens 和 total_tokens;Responses 通常使用 input_tokens、output_tokens 和 total_tokens。实际字段仍应以当前端点返回的 API 响应为准。
为什么 tiktoken 统计结果和 API 实际用量不一致?
tiktoken 主要统计纯文本,而完整请求还可能包含消息角色与边界、历史对话、工具定义、JSON Schema、图片和文件。官方文档也提示,本地 tokenizer 对这些内容存在限制。
流式输出没有收到 usage,是否代表没有消耗 Token?
不代表。流式 Chat Completions 可以通过 stream_options.include_usage 请求最终 usage 数据块,但如果连接在该数据块到达前中断,可能无法收到它。此时应保留请求 ID和脱敏日志,再结合用量记录核对。
如何计算一次 API 调用的费用?
先读取实际输入、缓存输入和输出等 Token 数,再分别除以官方定价使用的计价单位并乘以对应单价,最后相加。具体价格、模型和计费类别应在当前官方定价页面核实。
历史对话会重复计算 Token 吗?
如果应用在每次请求中重新发送历史消息,这些内容会进入新的输入请求并影响用量。可以在不改变业务需求的前提下压缩、总结或删除不必要的历史内容,然后用完整输入计数接口复核。
相关阅读
- 先确认 OpenAI 兼容接口的 Base URL 和端点
- 模型名报错时按端点和模型权限排查
- Token 用量与 429 限流的关系
- 请求超时或流式中断时如何保留日志
- 在 Dify 中配置兼容模型并核对调用结果