APIMaster.ai

图片幂等提交与原请求恢复

图片原响应保留 10 小时;提交响应丢失后,可通过只读查询恢复原任务或结果,避免重复生成。

图片幂等提交与原请求恢复

第一次提交就携带 Idempotency-Key,并在发送前保存它。 无法给先前未携带 key 的请求补加幂等保护。提交响应丢失后,使用同一个 key 做认证 GET 查询,不要创建替代生成。

覆盖范围

按接口接入,不维护模型白名单:当前和未来使用以下接口的图片模型自动获得保护,包括 Gemini、GPT Image、Seedream、Grok 和 Midjourney。幂等保护不会让原本不支持的模型/接口组合变成可用。

  • POST /v1/images/generations、/v1/images/generations/async。
  • POST /v1/images/edits、/v1/images/edits/async、/v1/edits、/v1/images/variations。
  • /v1/midjourney/generations 及其 /imagine 别名、旧版 /mj/submit/* 路径。
  • 为覆盖多模态出图,带 key 的 POST /v1/chat/completions、/v1/responses,以及 /v1/models/{model}:generateContent 原生 Gemini 路径也采用相同规则,包括 :predict、:streamGenerateContent。这些接口的纯文本请求携带 key 时同样生效。流式输出会缓冲后持久化再返回,不再逐段推送,建议使用非流式请求。

不携带该请求头时保留原有行为。Seedance 使用独立的视频幂等契约。

请求头、作用域与冲突

Authorization: Bearer YOUR_API_KEY
Idempotency-Key: image-order-123-attempt-1
Content-Type: application/json

key 为 8–128 个可见 ASCII 字符,不含空格。作用域是提交账户,覆盖全部受保护接口。同账户另一把有效 API key 可以找回回执和原响应;仍检查账户状态、密钥吊销/过期与 IP 限制。额度耗尽的 key 仍能恢复原请求,首次执行仍须通过正常额度检查。APIMaster 与其他服务的账户、key 和回执相互独立,跨服务不会去重。

同 key、同请求并发或重发只允许一次原执行,不会再次生成和计费。已有成功响应且尚未过期时原样回放并带 Idempotency-Replayed: true;成功响应已过期时返回 HTTP 410 submission_response_expired 与原回执,不重新执行;处理中或结果不明时返回 HTTP 202 与原回执。

同 key 改变接口路径、模型、正文、有效分组或显式固定渠道,返回 HTTP 409、error.code = idempotency_conflict。JSON 正文按字节精确比较,包括空白、字段顺序、数字写法和省略/显式字段;请保留首次正文。GET 恢复不需要正文。

multipart 允许 boundary 和文件名变化;表单值、上传字段名、文件字节、同字段的文件顺序必须一致。重复的普通字段会被拒绝。不得修改参考图后复用原 key。

原响应正文自保存起保留 10 小时,之后自动清理;此规则同样适用于历史响应。回执、请求指纹、执行及账务状态继续保留,不自动复用 key。正文到期不会授权重新执行。原响应中的图片 URL 有独立有效期,请及时下载。

取回接口保留原 HTTP 状态码和完整响应内容(包括 Base64 图片字段),无需处理额外的存储链接。若暂时返回 HTTP 503 submission_storage_unavailable,请重试同一只读查询,不要因此新建生成请求;保留期到期仍返回 HTTP 410。

平台先持久化账户/key 占位,再进入生成和计费。带 key 的请求关闭自动换渠道重试和慢任务并发补发;结果不明时不会内部创建替代生成。这是可选能力:不携带 Idempotency-Key 的请求保留原有 fallback 行为,不具备本页的原提交恢复保证。

不再 POST,直接找回原任务或结果

# 只读查询原提交回执
curl --fail-with-body 'https://apimaster.ai/v1/image-submissions/by-key' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Idempotency-Key: image-order-123-attempt-1'

# 取回已保存的原 HTTP 正文和状态码
curl --fail-with-body 'https://apimaster.ai/v1/image-submissions/by-key/response' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Idempotency-Key: image-order-123-attempt-1'

两种 GET 都要求认证,且只读:不创建任务、不生成、不轮询上游、不结算或退款、不重新提交。返回 Cache-Control: no-store、X-Image-Submission-Id 和 X-Original-Request-Id。

回执位于 data。/response 在可用时返回原响应正文和原 HTTP 状态码,尚未就绪时返回 202 与回执;已过期则返回 HTTP 410、error.code = submission_response_expired,并附带原回执。response_available 表示正文当前可读取,response_expired 和 response_expires_at 表示过期状态及时间。过期不代表未执行或可安全重发。原响应可能是 data[0].task_id、同步图片 data[].url / b64_json、Midjourney 批次或原生多模态结果。得到原任务 ID 后使用该模型原有的任务 GET 接口和原模型 ID 继续查询。恢复接口不转换结果格式,也不延长原图片链接有效期。

什么情况下可以创建新尝试

data.status 含义 是否允许替代生成
pending、submitting 原请求仍在执行 不允许,继续查询原回执
accepted 已保存原成功响应;异步任务可能仍在运行 不允许,取回原响应并查询原任务
acceptance_unknown 尚无法确认上游是否受理 不允许,等待最终重试决定或联系支持
not_accepted 在进入生成、业务和计费逻辑之前已拒绝 安全重试字段成立后,修正原因并使用新 key
closed_retry_allowed 平台已确认无预扣或完成退款,关闭这笔受理不明的图片请求 允许使用新 key;原请求最终净扣费为零

创建新尝试前,必须同时确认 new_submission_safe = true、retry_safe = true、billing_final = true、net_charge = 0。not_accepted 同时返回 acceptance_final = true、billing_status = final_zero。closed_retry_allowed 则返回 acceptance_final = false、upstream_acceptance = unknown,其 billing_status 为 refunded 或 final_zero。允许重试是平台的账务承诺,不代表上游确定没有执行原请求。关闭后即使上游产生费用,也由 APIMaster 承担,不再向客户追扣原请求费用。

该关闭机制覆盖图片生成、编辑、变体及 Midjourney 接口上的所有模型;混合文字/图片入口根据已配置的图片定价、明确的图片输出模态或图片生成工具识别,纯文字请求不适用。首次提交必须携带 Idempotency-Key,才能建立持久回执。

对于原响应已保存、没有已知任务 ID的受理不明图片请求,默认核实窗口为原响应保存后 10 分钟,后台约每分钟检查一次。后台根据可取得的上游账务证据与挂账状态核实,完成必要退款,并在有持久化零净扣费证据后关闭回执。这不是“10 分钟必定结束”的时限:数据库或退款失败、缺少账务证据时,仍保持 retry_safe = false。缺少账务证据的历史记录、未保存完整响应的中断执行,以及已有异步任务 ID 的请求,不会仅因时间过去就允许重试;已有任务请继续查询原任务。

{
  "data": {
    "status": "closed_retry_allowed",
    "acceptance_final": false,
    "upstream_acceptance": "unknown",
    "retry_safe": true,
    "new_submission_safe": true,
    "billing_status": "refunded",
    "billing_final": true,
    "net_charge": 0,
    "next_action": "retry_with_new_key"
  }
}

待处理或受理不明状态仍返回 billing_status = unverified、billing_final = false、net_charge = null。正数 retry_after_seconds 表示初始核实窗口的剩余秒数,归零不代表退款或关闭必定完成。

旧 key 永久关联旧回执。 用旧 key 重发相同请求只返回 HTTP 202 和已关闭回执,不再次生成或扣费。下一次生成使用新 key。/response 在 10 小时保留期内返回原 HTTP 正文与状态码,因此可能返回原来的 500 或 524;到期后返回 410 submission_response_expired;当前是否允许重试以 /by-key 回执为准。两个 GET 接口都不会触发生成、对账或退款。

查不到回执仍返回 404、error.code = submission_not_found、error.retry_safe = false,不等于 not_accepted,也不授权重试。请使用同一服务、原提交账号,并在 Idempotency-Key 请求头中传入完全一致的原 key,不要仅放在查询参数中。同账号的另一把有效 API key 也可查询;持续 404 时请保留原 request ID 联系支持。

结构化错误

  • invalid_idempotency_key:key 格式不合法;不说明另一笔原提交的结果。
  • idempotency_conflict:key 对应的请求不同,恢复原请求,不要覆盖。
  • submission_not_found:暂未查到回执,不授权重发。
  • submission_storage_unavailable、submission_claim_unavailable:临时存储或执行占位故障,继续 GET 查询原 key。
  • submission_response_expired:原响应已超过 10 小时保留期,正文不再提供;原执行及账务状态仍可查询,同 key 不会重新执行。
  • submission_response_too_large:原响应超过 64 MiB 恢复缓冲上限,使用已知原任务 ID 或联系支持,不重新生成。
  • 适用适配器中的 do_request_failed、read_response_body_failed、image_generation_timeout:传输、读取或等待故障,不能证明未接受。
  • get_channel_failed:渠道选择失败;也可能发生在前一渠道尝试之后的 fallback 选择阶段,不能仅凭此代码保证此前未提交。
  • model_not_found 且消息含 no non-exclusive channel available:筛选时没有路由同时满足模型、请求能力和用户渠道偏好,不一定是模型 ID 不存在;代码本身不证明最终受理或账务状态。
  • image_channels_exhausted:至少尝试过一个图片渠道后,可用渠道已耗尽;原始上游错误保留在使用日志中,不能认定请求从未到达上游。

HTTP 408/429/5xx 或上游特有错误均不是统一的安全重试信号。响应丢失后,保留原 key、正文、UTC 时间、模型、接口及已收到的 request ID;不要向支持人员发送 API key。

错误日志中的 quota: 0 只描述该日志行,不证明上游未创建任务,也不证明所有预扣和结算已最终完成。上游 HTTP 500 或 524 没有返回任务 ID 时,不能仅据此改成 not_accepted。回执查询即使返回 HTTP 200,正文状态仍可能为 acceptance_unknown。

登录后的图片体验页代理

/api/playground/image 会透传调用方提供的 Idempotency-Key 和原回执标识。携带同一个请求头访问 GET /api/playground/image 可查回执;增加 ?submission_response=1 可取原响应正文。带 task_id 的 GET 仍按原任务查询处理。此代理要求登录,不会给未携带 key 的请求自动补建 key。