计费与积分
先给结论:LokoSpace API 按积分结算,1 元 = 100 积分。所有模型单价都从 model_pricing 读取,本文档不写死任何模型价格。你每次调用后,响应里的 lokospace_billing 都会给出本次扣了多少、扣完还剩多少。
三种计费模式
模型用哪种模式,看它的 billing_mode。不要拿一个模式的价格去套另一个模式。
| 模式 | 计费单位 | 典型用途 | 扣费公式 |
|---|---|---|---|
per_token | 每 1K token | 文本对话 | points × (计费输入 + 输出 × completion_ratio) / 1000 |
per_unit | 每次 | 图片生成等按次能力 | points × 数量 |
per_second | 每秒 | 视频生成、音频 | points × 计费秒数 |
金额最后统一保留两位小数。积分账本的最小精度是 0.01,不会把少量 token 向上凑成 1K。
按 token 计费
per_token 的 points 是每 1K 输入 token 的积分价,不是每 1K 输出 token 的价。输出 token 先乘 completion_ratio,再和输入一起算。
计费输入 = 正常输入 token
+ 缓存命中 token × cacheReadRatio
+ 缓存写入 token × cacheWriteRatio
计费单位 = (计费输入 + 输出 token × completion_ratio) / 1000
本次积分 = round2(points × 计费单位)
completion_ratio 缺省为 1。控制台的模型详情里如果写的是 2,表示输出 token 按 2 倍输入价计费。
举例,仅演示算法,不代表任何模型的实际售价:某模型 points = 10、completion_ratio = 2,本次正常输入 3000 token、输出 1000 token,没有缓存,则:
(3000 + 1000 × 2) / 1000 × 10 = 50 积分
缓存怎么算
缓存只影响 per_token,不影响 per_unit 和 per_second。
缓存命中和缓存写入的 token 都属于输入 token 的一部分。平台先按下面的倍率折算,再套上面的公式:
| 项目 | 常见配置 | 计费效果 |
|---|---|---|
缓存命中 cacheReadRatio | 0.15 | 命中部分按输入价的 0.15 倍计费 |
缓存写入 cacheWriteRatio | 1.5 | 写入部分按输入价的 1.5 倍计费 |
| 未命中、普通输入 | 1 | 按正常输入价计费 |
这三个倍率不是全局固定值。实际以该模型的计费详情为准,也就是 model_pricing.pricing_basis 里的配置:
- 只有
cacheBilling明确为true时,cacheReadRatio和cacheWriteRatio才生效。 cacheBilling缺失或为false时,两个倍率都按 1 处理。既没有缓存折扣,也没有写入加价。- 倍率字段缺失时也按 1 处理,不会猜测一个折扣。
这意味着两件事。第一,缓存写入不一定省钱,首次写入可能比普通输入更贵,只有同一段前缀被重复使用时才摊得回来。第二,缓存命中率不是平台承诺的指标,它取决于模型是否支持缓存、前缀是否稳定,以及本次上游是否真的返回了缓存用量。
你可以在响应里核对缓存用量:
usage.cached_tokens:本次命中的缓存 token。usage.cache_write_tokens:本次写入的缓存 token。lokospace_billing.metering.cacheReadTokens、cacheWriteTokens:网关归一化后的计量值。
平台会对开启了缓存、且需要显式断点的模型自动处理缓存断点。你不需要为了缓存额外加参数;如果请求里已经带了断点,平台不会重复打。
按次计费
per_unit 的 points 是单次价格。图片生成按张数乘:
总积分 = round2(单张 points × n)
n 的范围是 1 到 10。不同分辨率可能是不同的 points,以 GET /images/models 返回的 pricing[].credits_per_unit 为准。图片是整笔成功或整笔失败:n > 1 时只要有一张失败,整笔退款,不会返回部分图片、也不会只扣成功的几张。
按秒计费
per_second 的 points 是每秒价格。计费秒数向上取整,并且不低于模型的最短计费秒数:
计费秒数 = ceil(max(实际秒数, min_billing_seconds))
总积分 = round2(points × 计费秒数)
视频不传 duration 时,用模型或定价里的默认时长;都读不到时按 5 秒兜底。比如单价 2 积分/秒、duration = 5.5、最短 1 秒,计费秒数是 6 秒,总价 12 积分。
音频转写按上游返回的实际秒数计费,同样向上取整,最低按模型的 min_billing_seconds,通常为 1 秒。
余额在哪里看
可用积分按下面这个口径算:
可用积分 = 蓝积分 + 黄积分 - 冻结黄积分
查看位置有三个:
- LokoSpace 客户端「个人中心 → 积分」。这里能看到余额和积分流水。
- 已登录会话可以调用
GET /api/credits/balance。返回credits、creditsBlue、creditsFrozen等字段。这个接口走登录态,不是给sk-loko-密钥调用的。 - 正常扣费的响应。
lokospace_billing.balanceAfter是本次扣费后的余额;命中幂等重放时可能为null。非流式响应和媒体创建响应还会带x-lokospace-credits-used,流式请求看最后一个结算帧。
余额不足会在调用上游之前判断。文本请求如果只是请求的输出上限太高,网关会按可用余额把输出上限下调到能支付的范围,并通过 x-lokospace-output-budget 返回实际生效的上限;只有连最小输出都支付不起时,才会返回 402。
失败会不会退款
先分清两种结算方式。
文本和音频文件转写是后结算:请求成功拿到用量后才扣费,不存在“先扣一笔再退”的动作。图片和视频是创建时先扣费,失败再按原路退回。实时语音按会话分段预扣,结束时按实际秒数多退少补。
| 场景 | 是否扣费 | 处理方式 |
|---|---|---|
| 调用前余额不足,返回 402 | 不扣 | 不会调用上游,充值后重试 |
| 参数错误、鉴权失败、无模型权限 | 不扣 | 按错误码修正请求,不要靠重试解决 |
| 文本请求在上游返回内容前失败 | 不扣 | 运行记录标记失败,可以直接重试 |
| 文本流式已经开始输出后中断 | 暂不结算 | 进入账单待核对状态,不会重复扣费,也不会按整单退款 |
| 文本上游成功,但结算写入异常 | 暂不结算 | 同样进入待核对,不要重复发起同一请求 |
| 图片生成失败或超时 | 全额退 | 蓝积分退蓝、黄积分退黄,按原扣费拆分退回 |
| 视频提交失败、生成失败或超时 | 全额退 | 同上;退款后在任务查询里返回 failed |
| 音频转写失败 | 不扣 | 没有成功用量就不结算 |
| 相同幂等键重复提交 | 不重复扣 | 已完成的请求返回原结果或 409,不会再次结算 |
几个容易误解的点:
- 图片
b64_json结果不会缓存到数据库。相同幂等键在完成后重试会返回 409MEDIA_RESULT_NOT_CACHED,这不会再次扣费,但也拿不到上次的 base64。要重新生成就换新幂等键,那会是一笔新费用。 - 视频任务超过 2 小时仍未完成,查询时会标记失败并退款。如果服务进程重启,后台轮询会停,需要你再次调用
GET /videos/generations/:id才会继续查询和触发退款。 - 文本流式如果已经收到部分内容,不要把它当成“完全失败”再发一遍。先拿
request_id对账;重复发新请求可能产生第二笔真实调用。
对账要看哪些字段
每次调用至少保留这四个值:
| 字段 | 在哪里 | 用途 |
|---|---|---|
request_id | 响应头 x-lokospace-request-id,或响应体 lokospace_billing.requestId | 定位这一次请求 |
creditsUsed | 响应体 lokospace_billing.creditsUsed | 本次扣费额 |
balanceAfter | 响应体 lokospace_billing.balanceAfter | 扣费后余额 |
usage | 文本响应体;流式在最后一个结算帧 | 输入、输出、缓存 token |
排查扣费问题时,先看 usage 和 lokospace_billing,再看积分流水。不要只看 max_tokens 估算费用,它是上限,不是实际输出量。
每次业务请求生成一个唯一的 Idempotency-Key,重试时复用同一个,业务换了才换新的。这样网络超时或客户端重连不会多扣一次。