Kimi Batch API 怎么提交和查询

2026-08-25

数据截至 2026-08,价格与限额以各官网为准。本文只讲计费与接入机制,不列具体价格数字。

Kimi 的 Batch API 不是”把请求发快一点”,而是换了一条完全不同的通道:你先把一堆请求写成一个 JSONL 文件传上去,拿到 file_id,再用它创建一个批处理任务,然后靠轮询任务状态等结果,最后从 output_file_id 指向的文件里把结果一行行读回来。整条链路里有三个点需要在动手前就确认清楚——上传文件时 purpose 是必填枚举、必须显式填成 batch,一个批次里所有行的 model 必须完全一致,以及 completion_window 一旦超时任务会直接变成 expired 而不是继续排队。计费上官方说明 Batch 接口按低于标准价计费,具体比例见官方定价页;官方同时写明 Batch API 不受实时并发限制,所以它的价值是”总量大、不着急”的场景,而不是压低单次响应等待。

这条链路长什么样:文件进、文件出

Kimi 官方的《使用 Batch API 批量处理任务》指南把流程拆成五步:构造输入文件、上传文件、创建任务、等待完成、处理结果。注意这五步里有两步是在跟文件接口打交道,而不是跟批处理接口打交道——输入靠文件接口上传,输出也靠文件接口下载,批处理接口本身只负责”创建任务 / 列出任务 / 查任务详情 / 取消任务”这四件事。

理解这个分工很重要,因为它决定了你的排查路径。任务卡在校验阶段,问题多半在输入文件的格式;任务跑完却拿不到内容,问题多半在下载结果那一步用错了文件 ID。这两类问题的官方文档页根本不是同一页。

官方文档同时说明,除了写代码调接口,Kimi 开放平台控制台里也有一条不用写代码的路径:《使用控制台进行批量推理》里给出的入口是”用户中心 → 项目管理 → 查看项目 → 批量推理 → 创建批任务”,在弹窗里设置批量任务名称、最长等待时间、数据文件三项后提交,任务跑完点详情就能下载输出结果文件,历史的输入文件与输出文件在项目的”文件”页面里能找到。但这条路径官方标注了一个前提:批量推理针对某一项目进行,且只有 Tier1 及以上的用户可以使用批量推理。这个限定是写在控制台那篇教程里的,别把它当成整个 Batch API 的通用门槛去理解,也别反过来假设 API 路径就一定没有任何账号层级要求——官方文档在 API 那一侧没有对应说明。

输入文件:四个必填字段,一个都不能少

JSONL 的意思是每一行都是一个独立、完整的 JSON 对象,代表一个推理请求。官方给出的字段表只有四个,全部标为必须:

  • custom_id:自定义请求标识,用于追踪结果,需在文件内唯一
  • method:请求方法,固定为 POST
  • url:请求地址,固定为 /v1/chat/completions
  • body:请求体,官方说明与 Chat Completions API 参数一致

这四个字段里,methodurl 是官方规定死的固定值,body 的结构直接沿用 Chat Completions API 的参数,只有 custom_id 是完全由你自己命名的标识字段。它的作用在最后一步才显现:输出文件里每行结果都会带回同名的 custom_id,你靠它把结果和原始数据对上。所以别图省事写成一串随机串——写成能反查回源数据主键的形式,后面处理结果时会省掉一次昂贵的匹配。

官方列出的输入文件要求还有几条容易忽略的:文件必须是 .jsonl 扩展名,内容不能为空,且有单文件大小上限(具体数值以官方文档当前版本为准);每行必须是合法 JSON 对象且包含上面四个字段;custom_id 在文件内必须唯一;所有行的 model 必须相同,一个批次只允许一个模型;指定的模型必须存在且用户有访问权限。

最后那条”一个批次只允许一个模型”是设计层面的硬约束,不是建议。如果你的业务本来就要分模型跑——比如简单条目走一个模型、复杂条目走另一个——那就得拆成两个批次、两个文件、两个任务分别提交,没有在同一个文件里混排的写法。

还有一条容易被跳过的说明:官方明确写了 Batch 支持的这些模型的 temperaturetop_pnpresence_penaltyfrequency_penalty 参数均不可修改,请勿在 body 中设置这些参数。这条要特别当心:如果 body 是从实时调用的代码里直接复制过来的,就很可能原样带着 temperature 这类采样参数。复制之前先把这几个键删掉。

至于 Batch 支持哪些模型,见本文最后一节——这里官方文档有两页对不上,需要单独说。

上传与创建:两个需要逐字核对的参数

上传这一步走的是文件上传接口,关键在于 purpose 必须设置为 batch。官方文档给出的 purpose 枚举一共四个取值:file-extract(抽取文件内容)、image(上传图片用于视觉理解)、video(上传视频用于视频理解)、batch(上传 JSONL 文件用于批处理任务)。填错了后面创建任务时不会自动纠正,因为创建接口明确要求 input_file_id 必须是通过 purpose="batch" 上传的 .jsonl 文件。

创建任务调的是 POST /v1/batches,请求体里三个必填项:

  • input_file_id:上一步拿到的文件 ID
  • endpoint:官方标为枚举类型,可用选项目前只有 /v1/chat/completions 一个
  • completion_window:任务处理的时间窗口,官方说明支持语义化格式,例如 12h1d3d 这样的写法,并规定了最小值与最大值,具体上下限以官方文档为准

completion_window 是这三个参数里唯一需要你做判断的。它不是”预计多久跑完”,而是”最长允许跑多久”——官方在定价页里写得很直白:任务需在指定的 completion_window 内完成,超时将变为 expired 状态。也就是说窗口设短了不会让任务变快,只会让还没跑完的部分作废。官方在指南的扩展建议里给出的方向是根据实际数据量调整这个窗口,较大的数据集建议往长了设。

除了三个必填项,创建接口还接受一个可选的 metadata 对象,可以塞自定义键值对,官方对键值对数量以及 key、value 的长度都设了上限(具体数值以官方文档为准)。这个字段在任务详情里会原样返回,用来标记”这批是哪个业务、哪一天的数据”很合适,比在 custom_id 里编码这类信息干净得多。

创建成功后返回的对象里,id 就是后面轮询要用的 batch_id,此时 statusvalidating

查询:八个状态分别意味着什么

轮询走 GET /v1/batches/{batch_id}。官方把 status 定义为枚举,八个取值及其含义如下(这是结构性枚举,以官方文档为准):

状态官方说明
validating已创建,正在校验输入数据
failed数据校验失败,任务终止
in_progress数据校验通过,正在执行
finalizing执行完毕,正在准备结果
completed结果准备完毕,任务完成
expired未在 completion_window 内完成
cancelling已发起取消,等待实际取消
cancelled取消完成,任务终止

这张表里有个细节值得单独指出:failed 的官方定义是”数据校验失败,任务终止”,也就是说它描述的是整个任务在校验阶段就没过,而不是”有些请求失败了”。单条请求的失败情况不体现在 status 上,而是体现在 request_counts 里——这个对象包含 completed(已完成)、failed(失败)、total(总数)三个计数。所以一个 statuscompleted 的任务,里面完全可能有若干条请求是失败的。只看状态不看计数,你会以为万事大吉。

任务详情对象里还有一整组时间戳字段,全部是 Unix 时间戳且可能为 null:created_atin_progress_atexpires_atfinalizing_atcompleted_atfailed_atcancelling_atcancelled_at。这组字段的价值在于它把状态机的每一次跃迁都打了时间点,任务异常时可以直接看出来是卡在校验、卡在执行还是卡在准备结果。尤其 expires_at 值得在提交后就记下来,它是判断”还剩多少时间”的唯一依据。

轮询频率上,官方指南的扩展建议里给了一个区间,核心意思是别打得太频繁(具体建议区间见官方文档)。官方在示例代码里的轮询循环也做了一件对的事:除了等 completed,同时把 failedexpiredcancelled 三个终止态一起作为跳出条件。少判其中任何一个,脚本都可能在任务已经死了之后继续空转。

取回结果:两个文件 ID,别只看一个

任务完成后,output_file_id 字段包含结果文件 ID,通过获取文件内容接口下载。官方同时说明:如果有请求失败,error_file_id 包含错误文件 ID;在任务详情页的说明里进一步写明,当 statusfailed 时建议查看 error_file_id 获取错误详情。这两个字段的类型都标注为 string 或 null,所以取值前先判空。

输出文件同样是一行一个 JSON 对象,官方给的结构里每行包含 idcustom_idresponseerror 四个键,其中 response 内部有 status_coderequest_idbodybody 就是一个标准的 chat completion 结构,里面带 choicesusage

这里有两个实用点。一是 response.status_code 是逐条的,意味着你可以在一个成功的批次里逐条判断哪些请求返回了非 200;二是 usage 也是逐条的,如果你想核对这一批到底消耗了多少 token,不需要另外查账单,把输出文件里的 usage 累加起来就是一份逐条可追溯的明细。这份明细在做成本归因时比任何汇总数字都好用——它能告诉你到底是哪一类输入把 token 吃掉了。想把这件事做成常态化监控,可以配合API 成本监控怎么做那套思路,把批次维度的 usage 汇总接进你自己的看板。

列出与取消:边界比想象中窄

列出任务走 GET /v1/batches,返回一个分页列表对象,字段是 object(固定为 list)、data(每个元素是 BatchObject,字段含义与任务详情一致)、has_more(是否还有更多数据)。分页靠游标而不是页码:当 has_moretrue 时,把本页最后一个 batch.id 作为 after 参数传入即可获取下一页;官方特别提醒,若省略 after 参数,每次查询均返回第一页。写循环拉全量的时候忘了传 after,就会得到一个永远拉不完的死循环。

取消走 POST /v1/batches/{batch_id}/cancel,但可取消的状态窗口很窄:官方明确只有 validatingin_progressfinalizing 三种状态的任务可以取消;若任务已处于 completedfailedexpiredcancelled 状态,调用此接口将返回 400 错误。取消也不是瞬时的——状态会先变为 cancelling,最终才变为 cancelled,所以发起取消之后还得再轮询一轮确认。

取消接口的常见错误官方也列了:400 表示任务状态不允许取消或请求参数无效;401 表示 API Key 无效或缺失,需要检查 Authorization: Bearer <key> 是否正确;404 表示指定的 batch_id 不存在,需确认 ID 拼写正确且该任务属于当前组织;500 是服务端内部错误,官方建议稍后重试,若持续出现附带 request_id 联系支持团队。任务详情接口那一侧对 404 给出的错误类型是 resource_not_found_error

顺带一提,官方文档在文件配额上有一条限制单独写在创建任务的限制表里:每个组织对 batch 类型的文件数量有配额上限。跑得久了这个配额是会被历史文件占满的,所以把”下载完结果就清理不再需要的输入文件”写进流程里,比等到报错再去翻文件列表要省事。

多模态批处理:差异只在构造输入这一步

官方说明 Batch API 支持在输入文件中包含图片和视频内容,并且明确指出与文本任务的区别主要在构造输入文件这一步,其余流程(上传、创建任务、轮询、处理结果)完全一致。

图片和视频各有两种传入方式。一是 base64 内嵌,把媒体编码成 base64 直接写进 JSONL,官方标注适合小文件,并提醒 base64 会让体积明显膨胀,需要关注单文件大小限制。二是文件引用:先通过文件接口上传(图片用 purpose="image",视频用 purpose="video"),然后在 JSONL 里用 ms://<file_id> 的形式引用,官方标注适合大文件或同一份素材被多个请求复用的场景。

“同一份素材被多个请求复用”这个提示很实在。如果你要对同一张图问五个不同的问题,base64 方式会把这张图在 JSONL 里重复五遍,而文件引用方式只上传一次。这个差别在批量场景下会被行数直接放大。

官方文档里两处对不上的地方

第一处是 Batch 支持的模型。指南页写的是 Batch API 支持 kimi-k2.6kimi-k2.5 模型,暂不支持 kimi-k3;而批量推理定价页的说明部分列的是 kimi-k2.7-codekimi-k2.6kimi-k2.5。两页对不上,多出来的是那个 code 系列。这种情况下别猜哪页更新,也别按名字推断——提交前用一个只有一行的小 JSONL 试一次校验,看任务能不能过 validating,比读文档更能确定当下的实际支持范围。

第二处是文件上传的 purpose。上传接口的参数说明里 purpose 枚举明确包含 batch,但错误码页在 400 那一节列了一条 message,字面意思是 purpose 只接受 file-extract。这两处显然不是同一时间维护的。真遇到这条报错,先确认自己确实传的是 batch 字面量、大小写没错、也没有被 SDK 包装成别的值,再去看是不是账号维度的权限问题——而不是照着错误提示把 purpose 改成 file-extract,那会让你的 JSONL 被当成待抽取的普通文档。

另外还有一条不算矛盾但极易踩的:官方在 401 那一节专门写了平台 Key 隔离说明——中国站与国际站的账户、余额和 API Key 完全独立,混用会返回 401,需要确认调用端点与 Key 所属平台一致。Batch 的整条链路涉及文件上传、任务创建、结果下载三次带鉴权的调用,其中任何一次用错了平台的 Key,报出来的都是同一个 401,而不会告诉你是平台错配。

最后:先跑通一行,再跑一万行

这套接口本身并不复杂,代价高的是这样一种返工:把一个上万行的 JSONL 提交上去,等了很久拿回一个 failed。按官方对 failed 的定义,它指的是数据校验失败、任务终止——校验是整体性的,一个格式问题就足以让整批停在起跑线上。

所以顺序应该是反过来的:先构造一个只有一行的文件走完全流程——上传、创建、轮询到 completed、下载结果、解析出 custom_idcontent;确认这条链路每一环都通了,再把行数放大。放大之后再重点确认三件事:所有行 model 一致、custom_id 无重复、body 里没有残留被禁止修改的采样参数。

至于该不该用 Batch,判断标准其实只有一条:这批活能不能接受”结果不是立刻拿到”。能接受,Batch 按低于标准价计费、又不受实时并发限制,是明显划算的;不能接受,那再便宜也没用,该走实时接口就走实时接口。关于这两条通道跟缓存机制之间怎么组合,可以看缓存与批量接口该怎么选;如果你还没把 Kimi 的调用基础环境配好,先过一遍Kimi API 接入步骤;实时通道那边被限流打到的处理套路则在429 报错的通用处理办法里。

想系统学会用 AI?报名体系课或加入会员,照着学、照着用。