AI 出图 API 一旦遇到超时或 5xx 报错就自动重试,很容易把同一次生成请求当成新任务重复扣费;正确做法是给每次请求带一把 Idempotency-Key,超时和 5xx 重试沿用同一把 Key,只有全新请求才换新 Key。Flux Art 的 OpenAPI 已经把这套机制做成必填字段,本文按官方返回码逐一讲清楚什么错该重试、什么错不该重试。
这篇文章写给同样要把 AI 出图或出视频接口接进自己系统的后端和全栈工程师看,尤其是刚开始对接 Flux Art OpenAPI 或类似异步任务型接口的同学——网络抖动、超时、5xx 这些问题迟早会撞上,提前把幂等和重试的边界搞清楚,能少踩很多次重复扣费的坑。

先搞懂:为什么重试会重复扣费
要搞清楚这件事,先得弄明白 AI 出图 API 的两个基本设计:一是任务创建就扣费,二是创建走的是异步流程。调用生成接口成功后,服务端立刻返回 201,任务状态是 queued,积分在这一刻已经被扣走,后续再靠轮询 GET /tasks/{task_id} 拿最终结果。这个设计本身没问题,问题出在"客户端没收到响应"和"服务端到底有没有处理这次请求"其实是两件独立的事。
把客户端可能遇到的异常情况拆成三类,处理方式完全不一样:
第一类是网络层超时——请求包可能已经到达服务端并成功创建了任务,只是响应在回来的路上丢了或者客户端等待时间到了。这种情况下如果客户端认为"没收到响应=没成功",就重新发一次一模一样的请求,服务端会把它当成一次新的业务请求,再创建一个任务、再扣一次积分。
第二类是服务端 5xx,比如 internal_error 或 service_unavailable。这类错误的语义是"服务端这边确实出问题了",但具体是完全没处理,还是处理到一半,客户端同样没法从错误本身判断,处理原则和超时类似。
第三类是客户端参数错误,比如 400 invalid_request、400 invalid_media_url、422 validation_error。这类错误的语义很明确:这次请求本身有问题,服务端根本没有真正进入生成流程,重试同样的参数只会一直拿到同一个错误,不会有任何变化。
Idempotency-Key 存在的意义,就是在第一类和第二类"结果不确定"的场景里,让服务端能识别出"这次重试和上一次是不是同一个业务请求"——只要 Key 一样,服务端就直接把之前那次的任务结果原样返回,不会再创建新任务、也不会再扣一次积分。
什么错该重试、什么错不该重试
把接口实际会返回的错误码按"该不该重试"和"Idempotency-Key 该怎么处理"整理成一张表,接手项目的同事直接照着这张表写重试逻辑就行,不用每次都重新判断一遍。
| 遇到的情况 | 是否应该重试 | Idempotency-Key 怎么处理 | 依据 |
|---|---|---|---|
| 请求超时、无响应 | 应该重试 | 沿用同一把 Key | 服务端可能已创建任务,同 Key 重试会直接拿回原结果 |
| 5xx internal_error / service_unavailable | 应该重试,建议指数退避 | 沿用同一把 Key | 服务端临时故障,同 Key 保证不重复创建任务 |
| 429 rate_limit / concurrent_limit | 应该重试,按 Retry-After 等待 | 沿用同一把 Key | 限流不等于任务失败,仍是同一次业务意图 |
| 400 invalid_request / invalid_media_url | 不要重试 | 先改参数,改完再决定要不要换新 Key | 参数本身有问题,原样重试只会一直报同一个错 |
| 422 validation_error | 不要重试 | 看 details 改好参数后再发起新请求 | 校验失败会自动退还积分(usage.points_refunded) |
| 一次全新的业务请求 | — | 必须换新 Key | 复用旧 Key 到不同请求会报 409 idempotency_key_reused |

你是哪种情况?对号入座
| 你的场景 | 最头疼的环节 | 在 Flux Art 上怎么做 | 推荐主力模型 |
|---|---|---|---|
| 电商详情页脚本批量生成主图,网络偶发超时 | 超时后不确定任务有没有真的创建,重试怕重复扣费 | 给每张图的生成请求生成一把 8-128 位的 Idempotency-Key(用商品 SKU 加版本号拼接最省事),超时后原样带同一把 Key 重试 POST /images/generations,服务端识别到同一把 Key 会直接把之前的任务结果回传,响应头带 Idempotent-Replayed,不会再扣一次积分 | gpt-image-2 |
| 短视频团队并发跑分镜片段,脚本经常撞到 429 | 限流报错后要不要重试、重试算不算新任务 | 收到 429 先读 Retry-After 头等对应秒数,用同一把 Idempotency-Key 按指数退避重试,不要因为限流就误以为任务失败去换新 Key 发新请求 | doubao-seedance-2-0-260128 |
| 后端偶发收到 5xx,分不清服务端到底处理没处理 | 拿不准要不要重试、重试会不会造出重复任务 | 5xx 明确允许重试,重试时原样带上第一次用的 Idempotency-Key,服务端凭这把 Key 保证同一次业务意图只落一次账;拿不准也可以先 GET /tasks/{task_id} 查一下任务是否已存在 | gemini-3-pro-image-preview |
| 提示词或参考图 URL 传错,接口一直返回 400 | 团队里的通用重试中间件对所有报错都无脑重试三次 | 400 invalid_request / invalid_media_url 属于参数问题,不是可重试错误,要先按错误体里的 message 改好 prompt 或 image_urls,此时才需要换一把新的 Idempotency-Key,不能让通用重试逻辑对 4xx 也一视同仁地重放 | 与具体模型无关 |
| 批量任务偶发 422 validation_error,团队担心积分白扣 | 搞不清创建时扣的积分到底退没退 | 看响应里的 usage.points_refunded 字段,校验失败会自动退还这部分积分,不用手工申诉;按 details 改好参数后正常发起新请求,不要对 422 做自动重试 | 与具体模型无关 |

五步实操:把幂等和重试封装进业务代码
第一步:注册 Flux Art 账号,新用户注册即送 500 积分(约可生成 30+ 张 GPT Image 2 图,以官网当前为准),官网入口 https://flux-art.ai 均可完成注册,登录后升级到 Pro 及以上计划(Open API Support 随 Pro / Max / Ultra 计划开放)。
第二步:在控制台的 API Key 页面创建一把 fa_live_ 开头的 Key,只存在服务端环境变量或密钥管理器里,不要写进前端代码、App 包或提交到公开仓库;接口基址是 https://open-api.flux-art.ai/openapi/v1,注意这是唯一的接口域,不存在 .ai 接口地址,控制台入口则是 https://flux-art.ai 两个都能进。
第三步:在业务代码里给每一次"用户发起的生成请求"生成一把独立的 Idempotency-Key,8-128 字符,只能用字母、数字、句点、下划线、冒号、连字符,实践中用"业务 ID 加时间戳"或直接用 UUID 都可以,随请求头一起带上。
第四步:封装一层统一的重试判断逻辑:收到超时、5xx、429 时用同一把 Key 重试(429 按 Retry-After 等待,5xx 按指数退避加一点随机抖动);收到 400、401、402、404、409、422 时一律不重试,先按错误体里的 code 和 message 定位问题再决定下一步。
第五步:重试成功后检查响应头里有没有 Idempotent-Replayed,有就说明这次命中了之前的任务结果,没有产生新扣费;再定期用 GET /tasks 拉一遍任务列表,把 usage.points_charged 和 usage.points_refunded 对上账,确认没有和实际任务数对不上的情况。
自查清单
- 每一次"用户发起的生成请求"是否都生成了独立的 Idempotency-Key?
- Key 长度是否在 8-128 字符之间,只用了字母、数字、句点、下划线、冒号、连字符?
- 超时和 5xx 重试是否原样复用同一把 Key,而不是自动换新的?
- 429 重试是否读取了响应头里的 Retry-After,而不是自己硬编码一个固定等待时间?
- 400、401、402、404、422 是否都被明确排除在自动重试之外?
- 收到 409 idempotency_key_reused 时,有没有检查是不是误把同一把 Key 复用到了不同请求上?
- 是否核对过响应头里的 Idempotent-Replayed,用来判断这次调用是不是命中了重放?
- 定期对账时,是否用 usage.points_charged 和 usage.points_refunded 核对实际扣费,而不是只看任务数量?
- API Key 是否只存在服务端环境变量或密钥管理器里,没有出现在前端代码、日志或公开仓库?
边界诚实:幂等机制解决不了什么
幂等键能保证的是"同一把 Key 不会被重复扣费",但它解决不了所有和重试相关的问题。官方目前没有公开图像 size 字段的具体枚举值、视频 duration 的完整支持范围、各模型 quality 的枚举、账户并发上限的具体数值,也没有公开 Webhook 或回调机制——现阶段只能靠轮询 GET /tasks/{task_id} 拿结果,这些都要以官网 / 控制台当前公开的文档为准,不要按经验猜一个数字写死在代码里。另外,幂等键只保证接口层面不会因为网络重试而重复创建任务,业务层面比如"这张图到底要不要再生成一次"这种产品判断,还是要在自己的业务代码里做,接口不会替你做这个决定。
把这套机制落到位,日常联调时遇到超时或偶发 5xx 就不用再手忙脚乱地去核对积分对不对得上;真正需要盯的,是那些 400、401、402、409、422 这类"重试也没用"的错误,越早在代码里把它们和"该重试"的错误分开处理,账单越干净。注册 Flux Art 领 500 积分先跑通一遍幂等重试逻辑,官网入口 https://flux-art.ai 均可访问,具体福利以官网当前为准。