Grok Batch API 怎么用:适用场景与提交形态
数据截至 2026-08,价格与限额以各官网为准。本文只讲计费与接入机制,不列具体价格数字。
Grok 的 Batch API 是一条「先把请求丢进队列、过后再回来收结果」的独立通道。官方文档把它和实时接口的区别摆得很直白:响应不是立刻返回而是异步排队,计费按低于标准价的方式走(具体比例见官方定价页),而且批请求不计入实时接口的每分钟限流。它的提交形态有两种——用 SDK 把请求对象攒成一个列表一次性 add 进去,或者写一个 JSONL 文件走 Files API 上传后引用。两种形态的能力不完全等价:文件批在创建之后是封口的,不能再追加请求。如果你手上的活是跑评测、洗一批历史数据、给数据库记录批量补 AI 字段这种「今天提交、明天要」的任务,改走 Batch 基本是稳赚;如果链路上有人在等着看结果,那就别碰它。
先判断这活到底该不该走 Batch
官方文档在开头就把两条通道摆成了一张对照:实时接口是「秒级返回、标准价、受每分钟限流约束」,Batch 是「异步排队、降价计费、请求不计入限流」。这三条差异里,第三条最容易被低估——很多团队卡在扩容不是因为付不起钱,而是因为 RPM/TPM 打满了,实时接口再怎么加并发也没用。Batch 直接把这类任务从限流账本里挪了出去。
官方列出的典型适用场景是:跑评测和基准测试、处理大数据集(客户反馈分析、工单分类、实体抽取)、大规模内容审核、文档批量摘要、数据富集流水线、以及定时跑的夜间任务。这几类的共同点是发起方是一段程序而不是一个人,没有谁盯着屏幕等。
关于完成时间,官方给了一个典型完成窗口(具体时长以官方文档当前版本为准),同时明确写了一句:完成时间是尽力而为,不做保证,实际耗时会随系统负载和批的规模变化。这句话的分量比时间数字本身重——你不能把 Batch 放在一条有硬交付时点的链路上,除非你自己在外面套了兜底逻辑。
还有一条前置检查特别容易漏:不是每个模型都收批请求。官方在文档顶部就挂了提醒,说明是否支持批处理要去各自的模型页面确认,不支持的模型会直接拒绝批请求。这条别靠猜,也别拿别的模型的结论去套。
如果你还在纠结「降本到底该先动缓存还是先动批量」,可以先看缓存和批量推理各自省在哪这篇通用机制篇,再回来看 Grok 的具体形态。
提交形态一:用 SDK 把请求攒成列表
这条路的顺序是固定的四步:建批、加请求、轮询状态、取结果。
建批这一步只做一件事——申请一个容器。官方示例里创建时传的是一个批名称(REST 侧字段是 name,Python SDK 侧参数是 batch_name),返回一个 batch_id,后面所有操作都拿这个 ID 索引。文档把批比作文件夹,建议按数据集、按实验、按任务类型分开建,不要一股脑塞进同一个批里。
加请求这一步是关键。官方文档说明,用 xAI SDK 时,对话类请求用 chat.create() 构造,图像用 image.prepare(),视频用 video.prepare(),视频续写用 video.prepare_extension(),然后把这些对象放进一个列表,调用 batch.add() 一次性提交。也就是说,同一个批里可以混装不同模态的请求,不需要按类型拆批。
这里有个字段值得单独拎出来讲:batch_request_id。官方说明它是每条请求的唯一标识,用来把结果和原始请求对回去;如果你不传,服务端会生成一个 UUID。文档还额外点了两个用途——一是幂等性(保证一条请求只被处理一次),二是把批请求和你自己系统里的记录挂钩。实践上这意味着你应该拿业务主键当 batch_request_id,而不是让服务端随机生成再费劲做映射。批一大,这个决定的成本差距会非常明显。
工具调用在批里也是可用的。官方明确写了两种都支持:服务端工具(网页搜索、代码执行、MCP 等)在处理过程中直接执行,返回的是最终结果,行为和实时接口一致;客户端函数工具也支持,模型会在响应里返回 tool_calls 交给你离线处理,但多轮工具调用需要你把工具结果拼回对话后再提交一个新的批请求——批里不会替你自动转下一轮。这条决定了带工具的复杂任务在 Batch 里要按轮次拆成多个批,是设计阶段就得想清楚的事。
提交形态二:JSONL 文件上传
如果你的请求是脚本、流水线或者外部工具生成的,SDK 攒列表那套就有点别扭了。官方给的替代形态是写一个 JSONL 文件,每行一个 JSON 对象,包含四个字段:custom_id(唯一标识,映射到 batch_request_id)、method(固定为 POST)、url(要打的 API 端点路径)、body(该端点对应的请求体)。
url 这个字段是一份结构性枚举,官方列出的受支持取值覆盖了对话补全、模型响应、图像生成、图像编辑、视频生成、视频编辑和视频续写这几类端点(具体路径以官方文档为准)。文档特别说明:同一个文件里可以混用不同端点,每条请求会被独立路由。这对做多模态流水线的人来说是个不小的便利。
不过文件批有三条硬约束,都是官方原文里写死的:
- 文件是异步在后台解析的,只要有任意一行不合法,整个批会被取消并给出错误信息。所以别把校验寄托在服务端,本地先把每行 JSON 过一遍。
- 文件批创建后即封口,不能再通过加请求的接口往里追加。要加只能新建一个批。
- 文件大小和请求条数都有官方给出的硬上限,
custom_id在文件内必须唯一。上限的具体数值以官方文档为准,别按记忆里的数字设计切分逻辑。
模型这一层文件批也有筛选:官方说明只接受启用了批处理的模型,图像和视频当前支持的是 grok-imagine-image 与 grok-imagine-video,其他 Imagine 系列模型会被以「不支持批处理」的理由拒掉。这是个很典型的坑——同一家的同系列模型,能力并不共享。
上传走的是 Files API,拿到文件 ID 之后在建批时用 input_file_id 引用它。之后的监控和取结果,和内联批完全一样。
批状态怎么读:两级计数器
Batch 是异步的,所以「它现在跑到哪了」这个问题需要主动去问。官方把状态分成两级。
批级状态挂在 batch.state 上,是一组计数器:num_requests(批里的请求总数)、num_pending(排队待处理)、num_success(成功完成)、num_error(出错失败)、num_cancelled(被取消)。官方给的判定条件很干脆:轮询到 num_pending 归零,就说明所有请求都处理完了——不管是成功、失败还是被取消。
注意这个判定的语义:归零不等于全成功。真正要看的是 num_error 有没有非零。写轮询循环的时候,退出条件写 num_pending == 0,退出之后紧跟着必须查一次错误计数,否则失败的那部分会被你无声吞掉。
请求级状态在 batch_request_metadata 里,通过列举批内请求的接口拿到,取值是四个:pending(排队中)、succeeded(已完成、结果可取)、failed(处理时出错)、cancelled(被取消,比如批在它被处理之前就整体取消了)。批级计数器告诉你「还剩多少」,请求级状态告诉你「具体是哪一条挂了」,排查时两个都要用。
还有一个字段别忘了看:expires_at。官方说明批是有过期时间的,过期之后结果不再可访问。也就是说结果不是永久存着的,跑完就得把数据搬回自己这边。
轮询频率上,官方示例的注释里明确写了「避免猛敲接口」,示例代码在每轮之间加了等待。这个等待时长你自己按批的规模定,别抄示例里的数字当成规范。至于实时接口那条链路上的限流字段该怎么读,可以参考限流字段与 RPM/TPM 的通用读法。
取结果:可以边跑边取,但要分页
这是 Grok Batch 一个比较舒服的设计:官方明确说结果可以随时取,不必等整批跑完,单条请求一处理完它的结果就可用了。所以对长批来说,正确姿势是一边跑一边消费已完成的部分,而不是傻等 num_pending 归零。
取回来的结果按 batch_request_id 和原始请求对应。响应类型按模态区分:对话补全用 result.response,里面是熟悉的 .content、.usage、.finish_reason 这些字段;图像用 result.image_response,提供 .url、.base64、.usage、.model;视频用 result.video_response,提供 .url、.duration、.usage、.model。官方特别说明这几个响应类型和常规的图像、视频生成方法返回的是同一套类型,所以你原有的解析代码大概率能直接复用。SDK 还提供了 .succeeded 和 .failed 两个便捷属性,把成功和失败分开。
分页这块是必须处理的,不是可选项。官方说明结果是分页返回的,用 limit 控制每页大小,用 pagination_token 翻下一页,当 pagination_token 为空时表示已经翻到底。列批接口和列请求元数据接口用的是同一套分页参数。很多人第一次接批只取了第一页就以为拿全了,这是最常见的静默丢数据方式。
媒体类结果还有一条时效约束:官方说明图像和视频结果返回的是签名 URL,会在官方给出的时限后失效,要求取回结果后尽快下载。这意味着「先把结果 ID 存下来,等以后再下载」的做法在这里行不通,必须在消费结果的同一个流程里把文件落盘。
成本和取消:两个收尾动作
成本这块,官方提供了批级的成本明细。批对象上有 cost_breakdown,里面的总成本字段是 total_cost_usd_ticks;单条结果的用量里也有对应的成本字段 cost_in_usd_ticks。这两个字段返回的都是「ticks」这个极小整数单位而不是浮点金额,官方的说法是为了精度。换算系数、以及批处理相对标准价的计费比例,都以官方定价页为准,这篇不列数字。
这个设计对做成本归集的人其实挺友好:你可以按 batch_request_id 把成本摊回到自己的业务对象上,做出真正按业务维度的成本表,而不是只有一个月度总额。想把这件事系统化,可以接着看API 成本监控该怎么搭。
取消批的语义也要记准:官方说明取消之后,已经处理完的请求结果仍然保留可取,但排队中的请求不会再被处理,并且不能再往一个已取消的批里加请求。所以取消是个「止损」动作而不是「回滚」动作,已经花掉的成本不会退回来。真要控制预算,得在提交前就把批切小,靠分批提交来设检查点。
最后:几条别踩的
回过头看,Grok Batch 这套接口里最容易出事的不是代码写错,而是几个前提判断错了:
第一,没确认模型支不支持批处理就开工,等到请求被拒才发现,整个流水线要返工。开工前去模型页面查一眼,成本是一分钟。
第二,把 Batch 塞进有人等结果的链路。官方已经把完成时间写成尽力而为、不做保证了,你却按最好情况设计交付节奏,出事只是时间问题。真需要低延迟又想省事,官方给的是另一条路——优先处理通道,那是和 Batch 反方向的选择,值不值得开是另一个话题,可以看Grok 优先处理是什么。
第三,不做分页、不查 num_error。这两个都属于「看起来跑通了、实际上少了一截」的失败模式,不会报错,只会让下游数据莫名其妙地缺。
第四,依赖服务端生成的请求 ID。批一大你就会后悔,用自己的业务主键当 batch_request_id,顺带把幂等性也拿到手。
下一步建议这么走:先拿一个小批(几十条量级)把「建批—加请求—轮询—分页取结果—成本归集」这条链路完整跑通一遍,把 num_error 和分页边界都验一遍,再把量放上去。别一上来就提交一个巨型批——批一封口,中途发现请求体写错了就只能整批作废重来。