Token 成本估算器

按手动输入的 token 数量和美元/百万 token 单价做基础估算;默认价格仅为示例,不是实时模型报价。

点击计算

用途与使用方法

适合在已知输入、输出用量与对应单价时计算两项小计之和。页面没有选择模型、拉取价格或自动统计文本 token 的入口。

  1. 从实际响应 usage 或可信用量记录取得输入和输出 token 数,不要以字数直接代替。
  2. 去服务商当前定价页核对具体模型、计费档位和单位,把美元/百万 token 单价填入两个价格框。
  3. 点击“计算”,查看两项用量与合计;需要缓存、工具或其他费用时另外计算,并以服务商账单为准。

默认输入与实际输出

以下为 2026-10-05 使用页面内置合成示例的浏览器核对记录;没有执行生成的命令、配置或技能。

tok-in: 12000
tok-out: 3000
price-in: 1.25
price-out: 10
input 12000 tok × $1.25/M
output 3000 tok × $10/M
≈ $0.0450 USD

适用边界

  • 预填的输入 1.25、输出 10 只是本页示例参数,页面没有说明对应模型与适用日期;不能称为当前 Codex、ChatGPT 或任何模型报价。
  • 公式为(输入 tokens × 输入单价 + 输出 tokens × 输出单价)÷ 1,000,000。默认 12000/3000 得 $0.0450,其中输入 $0.015、输出 $0.030。
  • 只有两档单价,没有独立缓存输入、缓存写入、工具调用、存储、图像音频、税费或订阅额度字段;不要把该合计当完整账单。
  • OpenAI reasoning tokens 计入输出用量;若从可见答案字数估计,会遗漏这部分。缓存输入可能采用不同价格,应按实际分类另计。
  • 本次只验证默认正数算例;负数、空白、非数字、极大值及精度边界未验证。

常见问题

默认单价会自动更新吗?

页面未显示自动价格来源或更新时间;应视为示例,按服务商最新价表手动填写。

能把这个美元结果换成 ChatGPT 套餐额度吗?

本页没有套餐额度换算规则。API 单价估算不能直接证明套餐消耗或剩余额度。

为什么和账单不同?

核对模型与档位、缓存分类、完整 usage、工具及其他计费项;两项简单估算可能遗漏这些因素。

继续阅读

请只使用脱敏示例,不要粘贴密钥或保密资料。站点的数据处理声明见隐私政策;本次核对没有进行独立网络行为审计。

手工对账练习:从 usage 分类到四项费用

下面是一份完整的虚构用量工作表。A、B、C 只是三次教学请求;字段是本例自定义的归一化口径,不代表任何 API 原始响应格式。所有价格均为随意设定的练习数字,不是当前或历史模型报价。练习约定 input_total 已包含 cached_input,output_total 是完整计费输出,工具按调用次数另外收费;没有缓存写入、存储、税费、折扣、最低收费或其他费用。

练习币种:USD
未缓存输入:$2.00 / 1,000,000 tokens
缓存输入:  $0.50 / 1,000,000 tokens
输出:      $8.00 / 1,000,000 tokens
工具调用:  $0.01 / 次

request_id,input_total,cached_input,output_total,tool_calls
A,30000,20000,5000,2
B,50000,40000,6000,3
C,40000,20000,4000,1
合计,120000,80000,15000,6

第一步先检查口径,再减出未缓存输入。每行 cached_input 都应不大于 input_total;如果真实服务商把缓存与普通输入分别给出,就不能套用本例的减法。这里的未缓存输入分别为 10,000、10,000、20,000,合计 40,000;40,000 + 80,000 = 120,000。

请求未缓存输入费用缓存输入费用输出费用工具费用合计 USD
A10000 × 2 / 1000000 = 0.02020000 × 0.5 / 1000000 = 0.0105000 × 8 / 1000000 = 0.0402 × 0.01 = 0.0200.090
B10000 × 2 / 1000000 = 0.02040000 × 0.5 / 1000000 = 0.0206000 × 8 / 1000000 = 0.0483 × 0.01 = 0.0300.118
C20000 × 2 / 1000000 = 0.04020000 × 0.5 / 1000000 = 0.0104000 × 8 / 1000000 = 0.0321 × 0.01 = 0.0100.092
合计0.0800.0400.1200.0600.300

本页两类用量框应该怎样填?

若目标是估算“未缓存输入 + 输出”这个小计,可把输入 tokens 填 40000、输入 $/M 填 2;输出 tokens 填 15000、输出 $/M 填 8。按页面已说明的公式,人工期望值为 $0.2000。此处是手算参考,没有把这些新输入作为浏览器实测记录。

本页两项小计 = 40000 × 2 / 1000000 + 15000 × 8 / 1000000
             = 0.080 + 0.120 = $0.200
另算缓存费用 = 80000 × 0.5 / 1000000 = $0.040
另算工具费用 = 6 × 0.01 = $0.060
练习完整合计 = 0.200 + 0.040 + 0.060 = $0.300
逐请求交叉核对 = 0.090 + 0.118 + 0.092 = $0.300

不要把 input_total=120000 直接按未缓存单价 2 填入后,再加缓存费用。那样输入与输出小计会变成 $0.360,另加缓存 $0.040 和工具 $0.060 后得到 $0.460,比正确的 $0.300 多 $0.160:80,000 个缓存 token 已在 input_total 内,又被多算了一次未缓存价格。

业务量翻倍时,哪些数能直接翻倍?

如果只是把这组三次请求原样重复一次,且价格、缓存比例和每次工具调用数均不变,则六次请求共 240,000 输入、160,000 缓存输入、30,000 输出和 12 次工具调用,四项费用为 $0.160、$0.080、$0.240、$0.120,总计 $0.600。若只是输入长度翻倍,或重试增加了调用,不能据此推断所有费用翻倍。

拿真实账单对照前的四个检查

  1. 确认 usage 的包含关系、单位和计费输出口径;不要把总输入与其缓存子集相加,也不要把输出明细再叠加到已含该明细的总输出。
  2. 按实际模型、请求档位和价格生效期拆组;不同单价不能直接共用本例的一行。
  3. 为缓存、工具及其他费用保留独立来源和小计,再与账单上的折扣、税费及舍入规则对齐。本练习保留小数计算,未模拟真实账单舍入。
  4. 本页只做两类 token 费用的乘加,没有请求明细导入、缓存分类、工具计费或账单核销功能。上表是可人工复算的教学工作表,不能证明实际应付金额。

本练习于 2026-10-13 使用 Python 标准库 Decimal 离线复算,确认分项和两种汇总方式一致;没有请求模型、读取私人账单或测试页面的新输入。

费用计算方式

分别填写输入 token、输出 token,以及各自每百万 token 的美元单价。费用等于输入数量除以一百万后乘输入单价,加上输出数量除以一百万后乘输出单价。结果以美元显示,并保留四位小数。

计算示例

假设输入 100000 token、输出 20000 token,填写的输入单价为 1 美元/百万、输出单价为 2 美元/百万,估算结果为 0.14 美元。这是演示用单价;工具根据表单数值计算,不自动获取模型价格,也没有计算缓存、批处理或其他费用项。

微额请求累计:为什么每笔都显示 0.0000,整批却有费用

上一节解决“费用分成哪些类别”,这里处理另一个容易漏掉的问题:同一批请求,先把每笔金额显示成四位小数再求和,可能与保留原始精度后求和差很多。页面显示是阅读格式;把显示值复制进另一个表格,并不自动得到可用于汇总的原始金额。

设想要在离线验收里估算一批相同的短请求。本例固定 250 次,每次输入 13 tokens、输出 3 tokens,输入单价 1.25 USD/M、输出单价 8 USD/M,整批教学预算 0.0100 USD。这些都是虚构的练习参数,不对应任何模型或服务商报价。只研究两类 token 的乘加;不增加缓存、工具、税费等项目,不把结果解释为真实应付金额。

每次输入费用 = 13 × 1.25 / 1000000 = 0.00001625 USD
每次输出费用 =  3 × 8    / 1000000 = 0.00002400 USD
每次合计     = 0.00004025 USD
250 次原值合计 = 0.01006250 USD
合计后显示四位 = 0.0101 USD
每笔先显示四位再相加 = 250 × 0.0000 = 0.0000 USD

这里不是二进制浮点误差:即使用精确十进制计算,提前丢弃小数位仍会丢掉信息。原值合计已经大于 0.0100;错误的逐笔显示合计却会判定没有超出预算。两者相差 0.01006250 USD,原因是 250 次同方向的舍入损失累积。

手算还提供一项独立验收答案:单次金额等于 161/4,000,000 美元,250 次等于 161/16,000 美元。代码用 Fraction 的这个固定分数核对 Decimal 计算结果,不从被测函数生成“期望值”。在相同用量与价格假设下,248 次为 0.00998200,249 次为 0.01002225,因此最多 248 次满足这份教学预算。这个数量不是未来实际请求成本的保证。

同一批 250 次请求,两条汇总路径保留每笔原值再相加250 × $0.00004025$0.01006250每笔先显示四位再相加250 × $0.0000$0.0000虚构固定单价实验 · 显示值丢失的信息,无法靠求和找回
四位显示便于阅读,但不能代替保存原值。图中参数仅用于离线练习。

可运行实验:保留原值,只在显示层舍入

把下面完整代码保存为 rounding_lab.py,用 Python 3.12 或更新版本运行。它只用标准库,不联网、不读取密钥或账单,也没有改写本页计算器。输入在函数参数中明确给出;需要换练习数据时可调用 batch_cost,并保留原来的固定答案测试。

  • 输入、输出 tokens 只接受真正的非负 int,各自最多 10¹²;次数最多 10⁶。布尔值虽然与整数有关,仍被明确拒绝。
  • 单价单位为美元/百万 tokens,只收普通十进制字符串,最多六位小数、最大 1,000,000。预算也收十进制字符串,最多十二位小数、最大 2×10¹⁸ 美元。两个限制都是实验合同,不是服务商限额。float、负数、科学计数、空白、NaN、Infinity 和超界值均拒绝,不做悄悄截断。
  • 每个计算函数使用独立的 50 位 Context,隔离调用方的精度、指数范围及陷阱。两项 token×单价相加最多需要 25 位有效数字,乘最多 10⁶ 次后不超过 31 位;分母是 10⁶,仍为有限十进制。50 位覆盖这个合同,并用 Inexact 陷阱防止无声损失。扩大范围或增加除法时必须重新证明。
  • 显示函数使用单独上下文,明确采用 ROUND_HALF_UP 保留四位。0.00005 在 HALF_UP 下成为 0.0001,在 HALF_EVEN 下成为 0.0000;这说明需要写明策略,不能默认所有系统一致。
  • 预算通过条件为未舍入金额 ≤ 预算。这里没有真实请求拦截、并发预算锁或账单核验,也不提供财务建议。
"""Synthetic USD/million-token arithmetic. Python 3.12+, standard library only."""
import re
import sys
from decimal import (Context, Decimal, Inexact, ROUND_HALF_UP,
                     ROUND_HALF_EVEN, localcontext)
from fractions import Fraction

MILLION = Decimal(1_000_000)
DISPLAY = Decimal('0.0001')
# Never inherit the caller's precision, exponent bounds, rounding or traps.
EXACT = Context(prec=50, rounding=ROUND_HALF_UP, Emin=-999999, Emax=999999)
EXACT.traps[Inexact] = True
VIEW = Context(prec=50, rounding=ROUND_HALF_UP, Emin=-999999, Emax=999999)


def integer(value, name, maximum):
    if type(value) is not int or not 0 <= value <= maximum:
        raise ValueError(name + ': expected bounded nonnegative integer')
    return value


def decimal_text(value, name, places, maximum):
    if (type(value) is not str or len(value) > 32
            or not re.fullmatch(r'(?:0|[1-9][0-9]*)(?:\.[0-9]{1,'
                                + str(places) + r'})?', value)):
        raise ValueError(name + ': invalid decimal text')
    result = Decimal(value)
    if not result.is_finite() or result > maximum:
        raise ValueError(name + ': exceeds lab bound')
    return result


def price(value):
    return decimal_text(value, 'price', 6, Decimal('1000000'))


def budget_amount(value):
    return decimal_text(value, 'budget', 12, Decimal('2000000000000000000'))


def unit_cost(input_tokens, output_tokens, input_price, output_price):
    i = integer(input_tokens, 'input', 10**12)
    o = integer(output_tokens, 'output', 10**12)
    pi, po = price(input_price), price(output_price)
    with localcontext(EXACT):
        return (Decimal(i) * pi + Decimal(o) * po) / MILLION


def batch_cost(input_tokens, output_tokens, input_price, output_price, requests):
    n = integer(requests, 'requests', 10**6)
    with localcontext(EXACT):
        return unit_cost(input_tokens, output_tokens, input_price, output_price) * n


def display(value):
    if (type(value) is not Decimal or not value.is_finite()
            or value < 0 or value > Decimal('2000000000000000000')):
        raise ValueError('display: expected bounded finite Decimal')
    with localcontext(VIEW):
        return value.quantize(DISPLAY, rounding=ROUND_HALF_UP)


def equal(actual, expected, name):
    # Explicit checks remain active under python -O.
    if actual != expected:
        raise AssertionError(f'{name}: {actual!r} != {expected!r}')


def rejects(fn):
    try:
        fn()
    except ValueError:
        return
    raise AssertionError('accepted invalid input')


def main():
    modes = ('--mutate-early-round', '--mutate-unit', '--mutate-tie')
    if len(sys.argv) > 2 or (len(sys.argv) == 2 and sys.argv[1] not in modes):
        raise SystemExit('usage: python rounding_lab.py [' + '|'.join(modes) + ']')
    mode = sys.argv[1] if len(sys.argv) == 2 else ''
    with localcontext(EXACT):
        unit = unit_cost(13, 3, '1.25', '8')
        total = batch_cost(13, 3, '1.25', '8', 250)
        if mode == '--mutate-early-round':
            total = display(unit) * 250
        if mode == '--mutate-unit':
            total *= 1000  # Wrong price unit: thousands instead of millions.
        equal(Fraction(total), Fraction(161, 16000), 'exact batch oracle')
        equal(unit, Decimal('0.00004025'), 'unit oracle')
        equal(display(total), Decimal('0.0101'), 'display oracle')
        early = display(unit) * 250
        equal(early, Decimal('0.0000'), 'early rounding counterexample')
        equal(unit_cost(3250, 750, '1.25', '8'), total, 'same-price regrouping')
        budget = budget_amount('0.0100')
        equal(total > budget, True, 'exact budget decision')
        equal(early > budget, False, 'rounded budget counterexample')
        c248 = batch_cost(13, 3, '1.25', '8', 248)
        c249 = batch_cost(13, 3, '1.25', '8', 249)
        equal(c248, Decimal('0.00998200'), '248 oracle')
        equal(c249, Decimal('0.01002225'), '249 oracle')
        equal(c248 <= budget < c249, True, 'adjacent budget boundary')
        equal(c248 <= budget_amount('0.00998200'), True, 'inclusive equality')
        equal(batch_cost(0, 0, '0', '0', 0), Decimal(0), 'zero')
        tie = Decimal('0.00005')
        with localcontext(VIEW):
            up = display(tie)
            even = tie.quantize(DISPLAY, rounding=ROUND_HALF_EVEN)
        if mode == '--mutate-tie':
            up = even
        equal(up, Decimal('0.0001'), 'half-up tie oracle')
        equal(even, Decimal('0.0000'), 'half-even tie oracle')
    # Independent integer-ratio oracle at the input limits, under hostile caller context.
    with localcontext() as hostile:
        hostile.prec = 3
        hostile.Emax = 9
        hostile.Emin = -9
        high = batch_cost(10**12 - 1, 10**12 - 1,
                          '999999.999999', '999999.999999', 10**6)
        fixed_high = Fraction(999999999998000000000001, 500000)
        equal(Fraction(high), fixed_high, 'high-digit oracle')
        equal(display(high), Decimal('1999999999996000000.0000'), 'high display')
        equal(batch_cost(10**12, 10**12, '1000000', '1000000', 10**6),
              Decimal('2000000000000000000'), 'maximum batch')
    checks = 0
    for position, bound in [('input', 10**12), ('output', 10**12), ('requests', 10**6)]:
        for bad in [-1, True, 1.5, '13', bound + 1, None]:
            args = dict(input_tokens=13, output_tokens=3, input_price='1.25',
                        output_price='8', requests=250)
            args[{'input': 'input_tokens', 'output': 'output_tokens',
                  'requests': 'requests'}[position]] = bad
            rejects(lambda args=args: batch_cost(**args))
            checks += 1
    for parser in [price, budget_amount]:
        for bad in ['NaN', 'Infinity', '-1', '1e3', '', ' 1', '01', 1.25, True, None,
                    '1.0000000000001', '999999999999999999999999999999999']:
            rejects(lambda bad=bad, parser=parser: parser(bad))
            checks += 1
    for bad in ['1.0000001', '1000001']:
        rejects(lambda bad=bad: price(bad))
        checks += 1
    for bad in [Decimal('NaN'), Decimal('Infinity'), Decimal('-1'), 1.25]:
        rejects(lambda bad=bad: display(bad))
        checks += 1
    equal(checks, 48, 'rejection count')
    equal(batch_cost(13, 3, '1.25', '8', 250), Decimal('0.01006250'), 'recovery')
    print(f'unit_exact_usd={unit:f}')
    print(f'batch_exact_usd={total:f}')
    print(f'batch_display_usd={display(total):f}')
    print(f'sum_of_row_displays_usd={early:f}')
    print(f'budget_usd={budget:f}; over_budget={total > budget}')
    print(f'requests_248_usd={c248:f}; requests_249_usd={c249:f}')
    print('max_identical_requests_within_budget=248')
    print('PASS: fixed oracles, regrouping, budget, ties, high digits, 48 rejections, recovery')


if __name__ == '__main__':
    main()

固定答案、负对照与恢复:让提前舍入真的失败

以下输出来自 Python 3.12.14 的本地离线运行。正常模式与 python -O 优化模式均退出 0;所有检查使用显式异常,不会因为 -O 移除 assert 而失效。

unit_exact_usd=0.00004025
batch_exact_usd=0.01006250
batch_display_usd=0.0101
sum_of_row_displays_usd=0.0000
budget_usd=0.0100; over_budget=True
requests_248_usd=0.00998200; requests_249_usd=0.01002225
max_identical_requests_within_budget=248
PASS: fixed oracles, regrouping, budget, ties, high digits, 48 rejections, recovery

除了手算的 161/16,000 美元总额,脚本分别核对 248 次和 249 次的固定小数答案,并测试刚好等于预算时允许通过。上界案例在调用方精度仅 3 位、指数范围被缩小的条件下仍得到固定分数答案;另测所有参数取上限时的总额 2×10¹⁸。这些检查覆盖的是受限输入合同,并不证明任意大的 Decimal 运算都精确。

48 次输入拒绝覆盖两类 tokens、次数、价格、预算和显示输入;拒绝后重新传入有效参数,必须恢复到 0.01006250。测试结果不是对网页输入控件的测试。

用三个负对照主动破坏实际计算路径。下面后三条各应退出 1;第一、二个被总额固定分数抓住,第三个被 HALF_UP 的固定边界答案抓住。它们分别代表提前舍入、把百万误作千的单位错误,以及偷换舍入策略。

python rounding_lab.py
python rounding_lab.py --mutate-early-round
python rounding_lab.py --mutate-unit
python rounding_lab.py --mutate-tie
python -O rounding_lab.py
python -O rounding_lab.py --mutate-early-round
python -O rounding_lab.py --mutate-unit
python -O rounding_lab.py --mutate-tie
python rounding_lab.py

提前舍入变体的关键失败行如下。去掉参数后重新运行正常版本,应再次退出 0;无需修改源文件。

AssertionError: exact batch oracle: Fraction(0, 1) != Fraction(161, 16000)

这里是主动注入的离线错误,不是线上工具故障。如果自己的汇总管道有类似差异,应回到 token 原量、适用单价和原值金额重新计算,不要给已经舍入的显示列乘“修正系数”。同价条件下,3,250 个输入 tokens 与 750 个输出 tokens 一次计算,应等于 250 次原值求和;如果模型、单价、生效期或费用类别发生变化,应分别计算再汇总。

把离线结果用于自己的估算前,检查这五件事

  1. 记录用途。估算教学预算、展示费用、核对服务商账单是不同任务;先说清哪一种结果需要保留哪些精度。
  2. 检查原数据。只有“0.0000”这样的展示文本时,无法反推出原始 token 数和未舍入金额;应回到用量来源,而不是把零当成免费请求。
  3. 把舍入规则写成显式配置。四位显示、分币显示、逐行舍入、分组舍入、发票舍入未必相同;本例不推测真实服务商的结算规则。
  4. 比较预算时注明边界。本例以原值 ≤ 预算为通过,并明确验证了 248/249 的相邻边界。真实调用的输出长度、重试和其他费用不确定时,还需要自己的预留策略,不能照搬 248 次。
  5. 给复现材料留证据。记录输入、币种、价格单位、价格来源与适用时间、计算方法和输出;本例仅验证虚构固定单价,未测试页面新输入、浏览器舍入、实际 API 用量或账单。

资料核对日期:2026-10-14。Python Decimal 官方文档解释 Context、有限十进制、Inexact 与 quantize;Python Fraction 官方文档说明有理数及 Decimal 转换。这两个当前文档入口已解析为 Python 3.15 文档,而实验解释器为 3.12.14;本文使用的接口在实测版本可用。本文只使用技术文档核对数值接口,没有引用任何模型报价,也没有推断任何服务商实际结算规则。

常见问题

这个工具会测量文本的 token 数吗?

不会。你需要填写已有的 token 数量;工具负责按数量和单价计算费用。

继续阅读