图片 API 对接文档从灵感,到可交付的图像。
用一套清晰的接口完成文字生成、参考图编辑、异步交付与 CDN 分发。从第一次调用,到稳定集成,你需要的细节都在这里。
开始接入
从一张图片开始,逐步接入上传、编辑和异步生成。
本文介绍 magic·art 图片接口。使用 HTTP + JSON 调用;文件上传使用 multipart/form-data。示例模型为 gpt-image-2,实际可用模型以接入方提供的模型列表为准。
| 场景 | 接口 | 返回方式 |
|---|---|---|
| 文生图 |
POST /v1/images/generations
|
同步 JSON、SSE 或异步任务 |
| 图生图 / 图片编辑 |
POST /v1/images/edits
|
同步 JSON、SSE 或异步任务 |
| 上传参考图 |
POST /v1/images/uploads
|
直接返回公开图片 URL |
| 查询异步任务 |
GET /v1/images/tasks/{request_id}
|
任务状态及结果 |
| 获取模型列表 |
GET /v1/models
|
当前可用模型 |
| Chat 格式生图 |
POST /v1/chat/completions
|
Markdown 图片 / SSE |
获取 Base URL 与 API Key
根据你使用的控制台,复制接口地址和完整密钥。下面两种界面选择对应的一种即可。
New API 控制台
- 进入左侧 API 密钥。
- 点击右上角 创建 API 密钥,按平台要求填写名称、分组及额度等信息并保存。已有可用密钥可直接使用。
- 在目标密钥右侧点击 … 更多菜单,选择复制连接信息,获取该密钥对应的接口地址和连接信息。
- 也可点击 API 密钥右侧的复制图标获取完整 Key。列表里的星号仅为脱敏显示,不是实际 Key。
Sub2API 控制台
- 进入左侧 API Keys。
- 点击右上角 Create API Key 创建密钥;已有密钥则找到对应行。
- 在列表上方 API Endpoints 区域点击地址旁的复制图标,获取 Base URL。
- 点击目标 API Key 旁的剪贴板图标,复制完整密钥。请确认所选分组与模型权限符合你的生图需求。
不同版本的菜单名称可能略有差异。Base URL 请以控制台的连接信息 / API Endpoints 为准,不要直接复制管理后台页面的网址。
地址与鉴权
所有示例只使用占位地址,请替换为分配给你的 API 地址。
export BASE_URL="https://api.example.com"
export UPLOAD_BASE_URL="https://uploads.example.com"
export API_KEY="YOUR_API_KEY"
BASE_URL 与 UPLOAD_BASE_URL 均不含末尾 /v1。若 SDK 要求填写 base_url,则使用 https://api.example.com/v1,不要重复拼接两次。
| 操作 | 是否需要 Key | 说明 |
|---|---|---|
| 生图、编辑、模型列表 | 是 |
Authorization: Bearer YOUR_API_KEY
|
| 公开图片上传 | 否 | 无需 Authorization;携带的 Authorization 不影响公开上传 |
| 读取上传链接 / 生成图片链接 | 否 | 持有完整 URL 即可读取,链接应妥善保管 |
| 查询图片任务 | 否 | 持有完整任务 UUID 即可查询 |
| 取消 / 重试 / 重新提交任务 | 是 | 必须使用任务所属账号的 Key |
查询可用模型
接入前先用 Key 验证连通性,再选择支持生成或编辑的模型。
/v1/models
curl "$BASE_URL/v1/models" -H "Authorization: Bearer $API_KEY"
{
"object": "list",
"data": [
{
"id": "gpt-image-2",
"object": "model",
"actions": [
"generate",
"edit"
]
}
]
}
上例仅展示关键字段。generate 表示文生图能力,edit 表示图片编辑能力。模型可用性可能变化;中间接入平台也可能过滤模型或使用不同的能力字段,请以实际列表及平台配置为准。
文生图
根据文字描述生成图片。没有参考图时使用此接口。
/v1/images/generations
curl --max-time 420 "$BASE_URL/v1/images/generations" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: illustration-order-001" \
-d '{
"model": "gpt-image-2",
"prompt": "一只橘猫坐在窗边看雨,柔和自然光,水彩插画",
"n": 1,
"size": "1024x1024",
"quality": "auto",
"response_format": "url",
"output_format": "png"
}'
同步成功响应 · HTTP 200
{
"created": 1790000000,
"status": "completed",
"stage": "completed",
"request_id": "11111111-1111-4111-8111-111111111111",
"data": [
{
"url": "https://api.example.com/v1/images/assets/22222222-2222-4222-8222-222222222222"
}
]
}
data 是数组,不能假设永远只有一张图片。返回 usage 或 revised_prompt 时可按需记录,这些字段不保证每次都有。任务标识也会出现在响应头 X-Request-Id 中。
参数与尺寸
文生图和图生图共享大部分参数。不要把输出格式与响应格式混淆。
| 参数 | 类型 / 默认 | 说明 |
|---|---|---|
model
|
string · 建议必填 | 使用模型列表中的模型 ID。示例为 gpt-image-2 |
prompt
|
string · 必填 | 非空文字描述;图生图请明确修改目标和需保留的内容 |
n
|
integer · 1 | 支持 1–12;模型可能有更严格限制。请传合法整数,不依赖兼容回退 |
size
|
string · 可选 | 像素尺寸如 1024x1024、1086x1448;或 auto;或预设比例 3:4 |
resolution
|
string · 可选 | 比例预设分辨率,如 1k / 2k / 4k,须为已配置组合 |
aspect_ratio
|
string · 可选 | 与 resolution 配合,例如 3:4。建议直接使用明确像素 size |
output_resolution
|
string · 可选 | resolution 的兼容字段;同时提供时优先级更高 |
quality
|
string · 可选 | 示例 auto;具体质量档位与效果由模型支持情况决定 |
response_format
|
string · url | url 返回链接;b64_json 返回原始 Base64 字符串 |
output_format
|
string · 可选 | png / jpeg / webp;jpg 等同 jpeg,大小写兼容;省略保留原图格式 |
stream
|
boolean · false | true 开启 SSE,成功图片以 Base64 返回 |
callback_url
|
string · 可选 | 非流式请求携带此字段即进入异步模式;填写真实 HTTP(S) 回调地址 |
cdn_key
|
string · 可选 | 已创建且授权的 CDN 交付 Key,例如 cdn_live_…;与 response_format=url 配合。详见 CDN 配置 |
client_reference
|
string · 可选 | 业务关联标识,可在同步、异步受理和查询响应中读取;不替代幂等键 |
background / moderation / input_fidelity
|
模型扩展字段 | 可随请求传递;是否生效由模型决定,不代表网关保证支持所有取值 |
output_compression
|
当前不生效 | 不要依赖此参数控制文件压缩率 |
尺寸选择
{
"size": "3:4",
"resolution": "2K"
}
比例写在 size 时优先于 aspect_ratio;未给分辨率时按 1k 预设处理。显式像素尺寸或 auto 优先于预设字段。输出尺寸单边不超过 4096 像素,还受总像素上限约束;并非所有模型都支持每种尺寸。
预设映射以服务配置为准,历史预设可能是近似比例。需要明确画布尺寸时直接写 宽x高。未配置的比例组合会返回 400,不要假设支持任意比例或 8K。
尺寸速查与写法
14 种常用比例,覆盖 1K、2K 和 4K;超出当前接口限制的组合明确标注。
14 种比例 × 1K / 2K / 4K 对照表
下表来自当前已核对的尺寸预设。每个像素值都可以直接填写为 size;比例写法则配合 resolution 选择对应档位。K 是预设档位名称,不代表每种画布都严格等于 1024、2048 或 4096 像素。
| 比例 | 常见用途 | 1K size | 2K size | 4K size |
|---|---|---|---|---|
1:1
|
方形头像 / 产品图 |
1024x1024
|
2048x2048
|
3840x3840
|
4:3
|
横向插画 / 展示图 |
1152x896
|
2048x1536
|
3840x2880
|
3:4
|
竖向海报 / 人像 |
896x1152
|
1536x2048
|
2880x3840
|
16:9
|
横屏封面 / 幻灯片 |
1536x864
|
2048x1152
|
3840x2160
|
9:16
|
竖屏封面 / 手机背景 |
864x1536
|
1152x2048
|
2160x3840
|
3:2
|
横向摄影 |
1536x1024
|
2048x1360
|
3840x2560
|
2:3
|
竖向摄影 |
1024x1536
|
1360x2048
|
2560x3840
|
5:4
|
横向商品展示 |
1280x1024
|
2560x2048
|
3840x3072
|
4:5
|
竖向商品 / 社媒图 |
1024x1280
|
2048x2560
|
3072x3840
|
21:9
|
宽幅场景 / 横幅 |
1792x768
|
2048x880
|
3840x1648
|
1:4
|
窄长竖幅 |
512x2048
|
1024x4096
|
暂不支持(超出单边限制) |
4:1
|
窄长横幅 |
2048x512
|
4096x1024
|
暂不支持(超出单边限制) |
1.414:1
|
近似 A 系列横向纸张 |
1440x1024
|
2048x1456
|
3840x2720
|
1:1.414
|
近似 A 系列纵向纸张 |
1024x1440
|
1456x2048
|
2720x3840
|
部分历史预设是近似比例,例如 1K 的 4:3 / 3:4;若业务要求严格比例,应使用明确像素尺寸并核验最终图片。模型仍可能对尺寸作适配,返回图片的真实宽高以实际文件为准。1:8 / 8:1 属于额外长幅预设,未纳入这 14 种常用比例,不能据此推断它们在所有档位都可用。
size 的三种正确写法
| 写法 | 请求片段 | 用途 |
|---|---|---|
| 明确像素(推荐) |
{"size":"1536x2048"}
|
宽 1536、高 2048;最直观,避免预设差异 |
| 比例 + 分辨率 |
{"size":"3:4","resolution":"2k"}
|
读取 2K / 3:4 预设 |
| 分辨率 + 比例字段 |
{"resolution":"2k","aspect_ratio":"3:4"}
|
与上一行对应相同预设 |
| 自动尺寸 |
{"size":"auto"}
|
让模型按其能力选择,不承诺固定宽高 |
同一比例的三档示例
[
{
"size": "3:4",
"resolution": "1k"
},
{
"size": "3:4",
"resolution": "2k"
},
{
"size": "3:4",
"resolution": "4k"
}
]
上面是三个独立请求的参数片段,不是一次提交三个尺寸的完整请求体。文生图 / 图生图 JSON 与 multipart 均可使用:
# JSON 中填写
"size": "1536x2048"
# multipart 中填写
-F "size=3:4" -F "resolution=2k"
| 容易出错的写法 | 正确处理 |
|---|---|
size:"2k" / size:"4K"
|
K 档位填 resolution,size 填像素或比例 |
size:"1536×2048"
|
使用小写英文字母 x:1536x2048,不使用乘号 × |
size:[1536,2048]
|
size 是字符串,不是数组 |
size:"wide"
|
使用具体比例,例如 16:9 |
| 同时传多套冲突的尺寸字段 | 优先仅保留 size;显式像素 size 或 auto 优先 |
| 只给 aspect_ratio,不给 resolution | 不能保证唯一匹配;同时给分辨率或直接给像素 |
| 输出比例不符合预期 | 不要依赖近似预设;明确宽高并检查下载后文件尺寸 |
上传参考图片
公开上传,无需 API Key。返回的 URL 可供同一或另一生图入口读取。
/v1/images/uploads
无需鉴权
方式一:multipart 文件上传
curl "$UPLOAD_BASE_URL/v1/images/uploads" \
-F "file=@reference.png"
字段名必须为 file,一次只能上传一张。不要手工填写 multipart 的 Content-Type,cURL / SDK 会自动生成 boundary。系统依据文件实际字节识别格式,而非文件名。
方式二:直接发送图片字节
curl "$UPLOAD_BASE_URL/v1/images/uploads" \
-H "Content-Type: image/jpeg" \
--data-binary @reference.jpg
支持 image/png、image/jpeg、image/webp、image/gif。原始字节方式要求 Content-Type 与实际格式一致。GIF 接受为输入不表示输出支持动画。
成功响应 · HTTP 200
{
"id": "33333333-3333-4333-8333-333333333333",
"state": "ready",
"url": "https://uploads.example.com/v1/images/uploads/44444444-4444-4444-8444-444444444444",
"byte_size": 245760,
"content_type": "image/png",
"width": 1024,
"height": 1024,
"sha256": "示例:64位十六进制内容摘要"
}
| 字段 | 说明 |
|---|---|
url
|
可直接用于 images[].image_url;下载无需 Key |
id
|
上传素材 ID;不是图片生成任务 ID,也不是生图账号的私有素材 ID |
state
|
ready 表示可用 |
byte_size
|
图片文件字节数 |
content_type
|
识别出的实际 MIME |
width / height
|
像素宽高 |
sha256
|
图片内容摘要 |
跨入口与链接有效期
上传后,将完整 URL 原样传给生图入口。生图服务必须能从服务端下载该 URL;不需要两个入口共享 API Key 或存储。不要使用需要 Cookie、登录、Referer 防盗链或只能在你本机访问的地址。
上传文件沿用参考图存储与清理规则:默认保留 24 小时,按最初创建时间计算,实际删除受清理周期影响。同内容重复上传可能复用旧 URL,不会刷新保留时间。清理后链接返回 404。任务已成功导入的参考图独立保留,不会因公开上传文件随后到期而直接丢失。
图生图与多图编辑
把上传得到的 URL 与修改说明一起提交。最多 16 张参考图,可选 1 张蒙版。
/v1/images/edits
curl --max-time 420 "$BASE_URL/v1/images/edits" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: edit-order-001" \
-d '{
"model": "gpt-image-2",
"prompt": "将图1的人物放在图2的场景中,保留人物面部特征和服装",
"images": [
{"image_url": "https://uploads.example.com/v1/images/uploads/IMAGE_ID_1"},
{"image_url": "https://uploads.example.com/v1/images/uploads/IMAGE_ID_2"}
],
"n": 1,
"size": "1086x1448",
"output_format": "jpg",
"response_format": "url"
}'
直接上传文件编辑
curl --max-time 420 "$BASE_URL/v1/images/edits" \
-H "Authorization: Bearer $API_KEY" \
-F "model=gpt-image-2" \
-F "prompt=保留人物,将背景替换为雪山" \
-F "image[]=@person.png" \
-F "image[]=@landscape.jpg" \
-F "size=1024x1024" \
-F "output_format=png"
可接受的 JSON 图片形式
| 形式 | 示例 |
|---|---|
| URL 对象(推荐) |
{"image_url":"https://uploads.example.com/source.png"}
|
| URL 字符串 |
"https://uploads.example.com/source.png"
|
| 嵌套 URL 对象 |
{"image_url":{"url":"https://uploads.example.com/source.png"}}
|
| Data URL |
"data:image/png;base64,iVBORw0..."
|
| Base64 对象 |
{"b64_json":"iVBORw0..."}
|
| 原始 Base64 |
"iVBORw0..."
|
图片字段兼容 images、image、image_url、image_urls、image[]、images[]。建议统一采用 images 数组,避免混用。多字段按上述顺序合并并去除跨字段重复地址;同一数组的顺序保留。
mask 可传单张 URL、Base64 对象或 multipart 文件。具体蒙版尺寸、透明区含义及编辑效果由模型决定。参考图和蒙版均需能下载并通过图片校验。
CDN 配置与接入
将生成结果交付到自己的存储和 CDN,改善终端用户下载大图的体验。
接入 CDN 后,图片生成完成会先上传到你配置的存储,再返回 CDN 访问链接。这样终端用户不必反复从海外源站下载大图。CDN 加速的是图片分发,不会直接缩短模型的生成时间,也不会自动加速网站或 API 请求。
一、准备对象存储与访问域名
当前开放平台配置界面支持七牛云与雨云对象存储。其他 S3 兼容存储需由接入方确认配置支持,不能直接在生图请求里填写存储凭据代替。
- 在存储服务商创建 Bucket,准备具备目标目录上传权限的 Access Key / Secret Key。
- 绑定能公开读取图片的访问域名,并按服务商要求配置 DNS、回源与 HTTPS。
- 确认目标用户所在地区能稳定访问该域名。存储域名和 CDN 加速域名不是同一概念;只填写一个海外对象存储域名不保证国内下载加速。
- 配置合适的缓存与文件保留策略。若源 Bucket 私有,请由服务商 CDN 完成授权回源,并保证返回的最终 URL 可访问;本接口不自动给自定义 CDN 链接生成访问签名。
二、在开放平台添加 CDN 配置
登录接入方提供的开放平台,进入 添加 CDN 配置,填写下列字段。平台地址请向接入方获取,本文不展示实际站点地址。
| 界面 / 字段 | 填写内容 | 注意事项 |
|---|---|---|
| 服务商 / provider | 七牛云 qiniu 或雨云 rainyun | 按真实存储服务商选择 |
| 配置名称 / name | 例如“生产图片 CDN” | 用于区分用途 |
| Access Key / access_key | 存储服务商 AK | 不是生图 API Key,不要放进前端或生图请求 |
| Secret Key / secret_key | 存储服务商 SK | 仅在可信配置页面填写;查询不回显明文 |
| Bucket / bucket | 图片存储空间名称 | 与密钥权限和 Region 一致 |
| API Endpoint / endpoint | 雨云对象存储的 API Endpoint | 雨云必填;七牛由其上传区域机制处理,不填写任意 CDN 域名作为 Endpoint |
| Region / region | 存储空间所在区域 | 按服务商实际配置填写;错误区域可能导致上传失败 |
| 公开访问域名 / domain |
https://cdn.example.com
|
仅域名 / origin,不带 /images 路径、查询参数、账号密码 |
| Key Prefix / key_prefix |
generated 或 images
|
对象路径前缀在这里填写,不拼进 domain |
| HTTPS / use_https | 建议开启 | 域名证书须有效;HTTPS 页面不要引用 HTTP 图片 |
保存后在 我的 CDN 配置 中核对 Bucket、访问域名及启用状态;这一步保存配置,不代表上传链路已验证成功。
三、创建 CDN Key 并授权
- 进入 创建 CDN Key,选择刚才保存的 CDN 配置,填写 Key 名称并创建。
- 在 我的 CDN Key 中复制生成的
cdn_live_…标识。 - 让接入方将该 CDN Key 授权给你的生图账号 / 接入链路。创建 Key 不等于完成生图授权;未经授权会返回
cdn_key_not_authorized。 - 先发起 n=1 的小尺寸图片验证请求,确认成功响应 data[].url 指向自己的 CDN 域名,并实际下载检查图片。
四、文生图携带 CDN Key
curl --max-time 420 "$BASE_URL/v1/images/generations" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: cdn-generation-001" \
-d '{
"model": "gpt-image-2",
"prompt": "白色背景的陶瓷花瓶产品摄影",
"n": 1,
"size": "1024x1024",
"response_format": "url",
"output_format": "jpeg",
"cdn_key": "cdn_live_YOUR_CDN_KEY"
}'
图生图携带 CDN Key
curl --max-time 420 "$BASE_URL/v1/images/edits" \
-H "Authorization: Bearer $API_KEY" \
-F "model=gpt-image-2" \
-F "prompt=保留产品主体,将背景替换为浅灰色" \
-F "image=@reference.png" \
-F "size=1024x1024" \
-F "response_format=url" \
-F "output_format=jpeg" \
-F "cdn_key=cdn_live_YOUR_CDN_KEY"
JSON 图生图同样在请求顶层加 cdn_key,不要放在 images 数组元素内。异步模式在同一请求增加 callback_url 即可;建议配合 URL 响应使用:
{
"model": "gpt-image-2",
"prompt": "一只坐在花园里的橘猫",
"size": "1024x1024",
"output_format": "jpeg",
"cdn_key": "cdn_live_YOUR_CDN_KEY",
"callback_url": "https://your-app.example.com/webhooks/images",
"client_reference": "cdn-order-001"
}
五、确认交付结果
任务通常经过 queued → running / generating → running / delivering → completed。配置 CDN 后,只有图片全部上传成功,才进入 completed 并返回 CDN URL;生成耗时与 CDN 上传耗时要分别观察。
{
"status": "completed",
"data": [
{
"url": "https://cdn.example.com/generated/11111111-1111-4111-8111-111111111111/0"
}
]
}
上例只展示关键字段。URL 可能没有文件扩展名,以 Content-Type 为准。目标对象路径一般为 {key_prefix}/{request_id}/{图片序号},重试同一图片交付会复用该路径。
| 情况 | 说明与处理 |
|---|---|
| 返回的仍是源图片地址 | 确认使用 response_format=url、cdn_key 在请求顶层、接入层未丢字段,并使用新的幂等键提交新的测试请求 |
| 出现 cdn_key_not_authorized | 请接入方完成授权;不要无限重试同一未授权 Key |
| 生成结束但一直 delivering | 检查 AK/SK、Bucket、区域、Endpoint、对象上传权限与连通性;图片上传仍未完成 |
| 上传成功但 CDN 链接 403 / 404 | 检查 DNS、域名绑定、回源授权、路径前缀、防盗链和缓存;不要立即重新生图 |
| CDN 交付失败 / delivery_review | 保留 request_id,请接入方核查交付;修复后重试交付,不必盲目重新生成 |
| Base64 / SSE 仍然很慢 | 图片字节仍要通过 API 响应传到客户端;要让浏览器直接走 CDN,使用 url 响应并加载返回的 URL |
异步任务
先受理,再通过轮询或回调获取结果。适合长任务和业务后端。
curl "$BASE_URL/v1/images/generations" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: async-order-001" \
-d '{
"model": "gpt-image-2",
"prompt": "极简白色陶瓷花瓶产品摄影",
"n": 1,
"size": "1024x1024",
"output_format": "webp",
"callback_url": "https://your-app.example.com/webhooks/images",
"client_reference": "order-001"
}'
受理响应 · HTTP 202
{
"request_id": "11111111-1111-4111-8111-111111111111",
"id": "11111111-1111-4111-8111-111111111111",
"status": "queued",
"stage": "queued",
"offline_task": true,
"status_url": "https://api.example.com/v1/images/tasks/11111111-1111-4111-8111-111111111111",
"task_url": "/v1/tasks/11111111-1111-4111-8111-111111111111",
"client_reference": "order-001"
}
保存 request_id、status_url 和业务关联信息。202 仅代表任务已受理,不代表图片生成成功。图生图同样支持 callback_url,包括 JSON 与 multipart 请求。
查询与任务状态
优先使用受理响应返回的 status_url,尤其在多入口部署时。
/v1/images/tasks/{request_id}
无需鉴权
curl "$BASE_URL/v1/images/tasks/$REQUEST_ID"
{
"request_id": "11111111-1111-4111-8111-111111111111",
"parent_request_id": null,
"created": 1790000000,
"status": "completed",
"stage": "completed",
"gateway_status": "completed",
"offline_task": true,
"status_url": "https://api.example.com/v1/images/tasks/11111111-1111-4111-8111-111111111111",
"client_reference": "order-001",
"data": [
{
"url": "https://api.example.com/v1/images/assets/22222222-2222-4222-8222-222222222222"
}
],
"final_status": "success"
}
| status | 含义 | 客户端处理 |
|---|---|---|
| queued | 等待执行 | 继续等待 |
| running | 生成中或交付中 | 继续等待;stage 可为 generating / delivering |
| completed | 成功 | 读取 data[].url 并保存图片 |
| failed | 失败或需要人工检查 | 记录 error,不要无限轮询或盲目重新生图 |
| cancelled | 已取消 | 停止轮询 |
HTTP 200 表示查询成功,不代表生图成功;必须检查 status。失败时会有 error 对象。gateway_status 是更细的诊断状态,业务逻辑以 status 为主。offline_task:true 是查询响应标记,不用于区分最初是否同步提交。
建议每 2–5 秒查询一次并设置总等待上限;遇到短暂网络错误或繁忙响应逐步退避。任务需在创建它的入口查询,不能任意更换 BASE_URL;图片下载链接则可以跨入口使用。
回调通知与验签
接收终态通知,按事件 ID 去重,尽快返回 2xx。
回调以 HTTP POST 发送 JSON。回调可能重复或迟到,网络成功也不等于你的业务事务成功。先持久化事件再异步处理,避免长时间占用回调连接。
{
"event_id": "55555555-5555-4555-8555-555555555555",
"event": "image.completed",
"request_id": "11111111-1111-4111-8111-111111111111",
"status": "completed",
"offline_task": true,
"data": [
{
"url": "https://api.example.com/v1/images/assets/22222222-2222-4222-8222-222222222222"
}
],
"usage": null,
"error": null,
"completed_at": 1790000060
}
| 字段 / Header | 说明 |
|---|---|
| event | image.completed / image.failed / image.cancelled |
| event_id / X-Image-Event-Id | 事件去重键;重试保持事件身份 |
| request_id | 关联创建任务时保存的业务订单 |
| data | 成功图片列表;失败或取消可能为空 |
| error | 失败提示可能为字符串或 null,不要假设与轮询 error 结构一致 |
| usage | 可能为 null,仅在上游提供时存在有效统计 |
| X-Image-Timestamp | 启用签名时提供的秒级 Unix 时间戳 |
| X-Image-Signature | 启用签名时为 v1= 后接 HMAC-SHA256 十六进制摘要 |
验签公式(签名功能需预先配置)
HMAC_SHA256(secret, timestamp + "." + raw_request_body)
import hashlib
import hmac
import time
def verify_callback(raw_body: bytes, timestamp: str, signature: str,
secret: str) -> bool:
try:
if abs(int(time.time()) - int(timestamp)) > 300:
return False
except (TypeError, ValueError):
return False
expected = "v1=" + hmac.new(
secret.encode(), timestamp.encode() + b"." + raw_body,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature or "")
必须使用原始 HTTP body 字节验签,不要先解析 JSON 再重新序列化。签名为可选能力:未配置回调签名密钥时,只发送事件 ID,不发送签名头。签名密钥不等于 API Key。未启用签名时,可在收到通知后主动查询 status_url 确认状态,再执行业务入账。
回调失败会按服务策略有限次数重试,达到上限后不再自动发送。业务应保留轮询兜底;回调失败不自动表示图片生成失败。client_reference 不保证包含在回调体内,请自行按 request_id 关联。
结果、Base64 与流式响应
根据应用场景选择传输方式,及时将成功图片保存到自己的存储。
Base64 响应
设置 response_format:"b64_json"。同步成功时 data[].b64_json 是不带 Data URL 前缀的字符串。
import base64
from pathlib import Path
# 当请求 output_format="png" 时
Path("result.png").write_bytes(base64.b64decode(result["data"][0]["b64_json"]))
文件后缀应与实际输出格式一致。不要仅凭 URL 后缀判断格式,公开图片链接可能没有扩展名;以响应 Content-Type 或实际字节为准。异步轮询与回调使用 URL,不依赖提交时的 response_format 返回 Base64。
SSE 图片生成
curl -N --max-time 420 "$BASE_URL/v1/images/generations" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"gpt-image-2","prompt":"海边日出","stream":true,"output_format":"png"}'
: working
data: {"b64_json":"iVBORw0...","type":"image_generation.completed","created_at":1790000060}
data: [DONE]
图生图完成类型为 image_edit.completed。等待期间发送保活注释,图片完成后才返回完整结果,不是逐像素预览。流式会强制使用 Base64,忽略 response_format=url。客户端须按 SSE 空行边界解析事件,不要把每个 TCP 分片直接当作完整 JSON。
连接建立后的失败使用 event: error + error JSON,并发送 [DONE]。因此即使初始 HTTP 为 200,也需要检查流中的错误。断流后使用请求 ID 查询结果。
Chat 格式兼容
已有 Chat Completions 客户端也可以提交图片任务。
/v1/chat/completions
{
"model": "gpt-image-2",
"messages": [
{
"role": "user",
"content": [
{
"type": "text",
"text": "将人物背景替换为海边,保留服装和面部特征"
},
{
"type": "image_url",
"image_url": {
"url": "https://uploads.example.com/source.png"
}
}
]
}
],
"size": "1024x1024",
"output_format": "png",
"stream": false
}
没有图片内容时执行文生图,有 image_url 时执行图生图。普通响应在 choices[0].message.content 返回 Markdown 图片;多张图合并在一条 assistant 回复中。支持 system / developer / user / assistant 消息,最多 128 条,至少一条 user 消息及非空描述。
{
"object": "chat.completion",
"choices": [{
"index": 0,
"message": {"role": "assistant", "content": ""},
"finish_reason": "stop"
}]
}
上例仅展示关键字段。stream:true 返回 chat.completion.chunk 和 [DONE],生成完成后发送完整图片 Markdown,图片使用 Base64 Data URL。它不是通用文字聊天服务,不支持工具调用、音频或异步 callback;异步请使用 Images 接口。不会伪造 token usage。
幂等、取消与重试
把“查已有结果”与“重新生成一张图”区分开。
幂等提交
在创建请求添加 Idempotency-Key,建议使用业务请求 UUID。相同账号、相同键、相同接口及相同请求内容复用已有任务;同一键修改内容会冲突。不要每次网络重试都生成新键。没有 Idempotency-Key 时可使用 X-Request-Id 作为兼容键;都不提供则自动产生新键。
| 操作 | 路径 | 语义 |
|---|---|---|
| 取消 |
POST /v1/images/tasks/{id}/cancel
|
仅排队任务可取消,已运行不可强行中断 |
| 恢复 / 重试交付 |
POST /v1/images/tasks/{id}/retry
|
用于恢复待检查任务或图片交付;不用于重发已完成任务的失败回调。不是重新生图接口,状态不支持时返回冲突 |
| 重新生成 |
POST /v1/images/tasks/{id}/resubmit
|
明确创建新任务,使用新的幂等键;可能产生新的用量 |
curl -X POST "$BASE_URL/v1/images/tasks/$REQUEST_ID/resubmit" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{"idempotency_key":"new-generation-order-002"}'
结果不明的任务不能直接当作失败重新生成。先查状态,必要时联系接入支持。素材已过期的旧任务可能无法重新提交,需要重新上传并创建请求。上述修改接口需要任务所属账号的 Key。
错误处理
同时记录 HTTP 状态、error.code、error.message 和请求 ID。
{
"error": {
"message": "图片格式无效或尺寸超过限制,请检查上传文件",
"type": "invalid_request_error",
"param": null,
"code": "invalid_image"
}
}
| HTTP | 常见原因 | 处理建议 |
|---|---|---|
| 400 | 参数、图片格式、内容策略或上游明确失败 | 阅读具体 code / message,修改输入再重试;不是所有 400 都只是客户端拼写错误 |
| 401 | Key 缺失或错误 | 核对 Authorization 与接入账号 |
| 404 | 模型 / 任务 / 图片不存在或已过期 | 检查入口地址、ID、模型列表与链接有效期 |
| 409 | 幂等冲突、素材未就绪、任务状态不允许操作 | 核对原请求与当前任务状态 |
| 413 | 请求体或单文件超出限制 | 减小输入;优先 URL 方式,避免 Base64 膨胀 |
| 415 | 不支持的上传 Content-Type | 使用支持的图片 MIME 或 multipart |
| 408 / 504 | 请求读取、图片读取或同步等待超时 | 若已受理,先查 request_id,避免重复生成 |
| 466 | 服务暂不可用(兼容状态码) | 退避重试或查询已有任务 |
| 477 | 上游 / 结果处理失败(兼容状态码) | 保留具体错误和 request_id 后排查 |
| 488 | 并发 / 队列限额(兼容状态码) | 降低并发并指数退避 |
| 499 | 访问限制(兼容状态码) | 检查账号权限与资源归属 |
中间接入层可能返回标准 429 / 502 / 503 等状态。不要只接受固定的 4xx / 5xx 列表;所有非 2xx 都应读取响应体并作失败处理。部分错误的 param 不存在或为 null。
| error.code | 说明 |
|---|---|
| invalid_output_format | 仅支持 png / jpeg / jpg / webp |
| source_fetch_failed / source_network_not_authorized | 服务端无法下载输入图片或来源不允许 |
| exactly_one_file_required | 公开上传必须且仅有一个 file 字段 |
| image_content_type_mismatch | 声明 MIME 与文件字节不一致 |
| asset_size_limit / request_too_large | 文件或请求体过大 |
| asset_not_found | 图片不存在或已清理 |
| task_still_running_query_by_request_id | 同步等待结束,但任务可能仍在运行 |
| content_policy_violation | 模型策略拒绝;请修改描述或参考图 |
完整接入示例
服务端先上传参考图,再异步编辑并轮询。示例不包含任何真实地址或密钥。
Python · requests
安装 requests,并配置 BASE_URL、UPLOAD_BASE_URL、API_KEY、CALLBACK_URL 环境变量。CALLBACK_URL 应指向你真实可用的回调服务。下面的轮询即使收到回调也可作为兜底。
import os, time, uuid
from pathlib import Path
import requests
base = os.environ["BASE_URL"].rstrip("/")
upload_base = os.environ["UPLOAD_BASE_URL"].rstrip("/")
headers = {"Authorization": "Bearer " + os.environ["API_KEY"]}
with open("reference.png", "rb") as f:
r = requests.post(upload_base + "/v1/images/uploads",
files={"file": ("reference.png", f, "image/png")},
timeout=(10, 120))
r.raise_for_status()
image_url = r.json()["url"]
# 将 request_key 保存到业务数据库;同一次提交重试须复用此键和请求体。
request_key = str(uuid.uuid4())
payload = {
"model": "gpt-image-2", "prompt": "保留主体,将背景替换为森林",
"images": [{"image_url": image_url}], "n": 1,
"size": "1024x1024", "output_format": "png",
"callback_url": os.environ["CALLBACK_URL"],
"client_reference": "order-001"
}
r = requests.post(base + "/v1/images/edits", json=payload,
headers={**headers, "Idempotency-Key": request_key},
timeout=(10, 120))
r.raise_for_status()
if r.status_code != 202:
raise RuntimeError("Expected async acceptance: " + r.text)
accepted = r.json()
status_url = accepted["status_url"] # 保存它,不要换到另一入口查询
print("request_id:", accepted["request_id"])
deadline = time.monotonic() + 900
while time.monotonic() < deadline:
time.sleep(3)
try:
r = requests.get(status_url, timeout=(10, 30)) # 不需要 API Key
except (requests.Timeout, requests.ConnectionError):
continue
if r.status_code in (429, 466, 477, 488, 502, 503, 504):
time.sleep(5)
continue
r.raise_for_status()
task = r.json()
if task["status"] == "completed":
for i, item in enumerate(task["data"]):
image = requests.get(item["url"], timeout=(10, 120))
image.raise_for_status()
Path(f"result-{i}.png").write_bytes(image.content)
break
if task["status"] in ("failed", "cancelled"):
raise RuntimeError(task.get("error") or task["status"])
else:
raise TimeoutError("本地等待结束,任务未必失败;继续使用已保存的 status_url 查询")
JavaScript · Node.js 20+ · 上传后同步编辑
import { readFile, writeFile } from "node:fs/promises";
const base = process.env.BASE_URL.replace(/\/$/, "");
const uploadBase = process.env.UPLOAD_BASE_URL.replace(/\/$/, "");
const form = new FormData();
form.append("file", new Blob([await readFile("reference.png")],
{ type: "image/png" }), "reference.png");
async function jsonResponse(response) {
const text = await response.text();
if (!response.ok) throw new Error(`HTTP ${response.status}: ${text}`);
return JSON.parse(text);
}
const uploaded = await jsonResponse(await fetch(uploadBase + "/v1/images/uploads", {
method: "POST", body: form, signal: AbortSignal.timeout(120000)
}));
const requestKey = crypto.randomUUID(); // 持久化;网络重试时复用
const response = await fetch(base + "/v1/images/edits", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": requestKey
},
body: JSON.stringify({
model: "gpt-image-2", prompt: "保留主体,背景改为蓝天",
images: [{ image_url: uploaded.url }], n: 1,
size: "1024x1024", output_format: "png", response_format: "url"
}),
signal: AbortSignal.timeout(420000)
});
console.log("request_id:", response.headers.get("x-request-id"));
const result = await jsonResponse(response);
for (const [index, image] of result.data.entries()) {
const download = await fetch(image.url, { signal: AbortSignal.timeout(120000) });
if (!download.ok) throw new Error(`Download HTTP ${download.status}`);
await writeFile(`result-${index}.png`, Buffer.from(await download.arrayBuffer()));
}
下载速度与访问排查
生成快但图片显示慢,通常需要把生成阶段与图片传输阶段分开诊断。
为什么显示生成 40 秒,图片却几分钟才出现?
生成耗时不等于页面可见耗时。图片生成完成、API 返回 URL、浏览器下载完大图,是不同的时间点。若任务已经 completed、图片 URL 已返回,但页面继续转圈或缓慢加载,优先检查图片下载链路;如果状态仍是 generating / delivering,则还可能在生成或上传,不能一概归因于浏览器下载。
页面完整可见时间 ≈ 排队 + 生成 +(可选 CDN 交付)+ 图片下载 + 解码 / 渲染
示例:生成 40 秒,图片文件 15 MB,实际下载速度只有 100 KB/s
仅下载就约需 150 秒,再加生成约 190 秒,页面可能 3 分钟以上才显示完整图片。
以上是说明时间差的示例,不是测速结果或服务时限承诺。接口返回 URL 很快,不意味着对应图片字节也已传到浏览器;Base64 响应还会增加约三分之一的传输体积。
海外链路的三种接入方式
| 方式 | 适合谁 | 怎么做 / 局限 |
|---|---|---|
| 稳定的海外代理线路 | 本地调试、浏览器访问或低频使用 | 让网页与图片请求实际走稳定海外线路。只给 API 请求配代理、图片仍直连,页面依然可能加载很慢;浏览器代理不一定影响服务器、SDK 或 cURL |
| 海外服务器接收并转存 | 后端批量处理、任务系统 | 用海外后端接收回调并主动下载图片,保存到自己的存储 / CDN。只在海外接收包含 URL 的回调,不会自动让国内浏览器下载变快 |
| 对接用户就近访问的 CDN | 面向终端用户的网站 / 应用 | 使用 cdn_key 或自己的转存流程,让浏览器加载 CDN URL。选择目标地区连通性好的 CDN;首次回源仍可能较慢 |
服务链路包含海外资源,部分地区直连可能不稳定。需要正常访问速度时,优先使用稳定的海外代理线路,或将调用与接收部署在海外服务器。面向大量终端用户时,更适合把最终图片交付到可稳定访问的 CDN。
区分慢在哪里
| 观察结果 | 更可能的阶段 | 下一步 |
|---|---|---|
| 任务仍 queued | 排队 | 降低突发并发,继续轮询 |
| 任务仍 generating | 模型生成 | 检查任务状态和错误,等待或联系接入方 |
| 任务 delivering | CDN 上传 / 交付 | 核对存储配置、上传网络及服务端日志 |
| JSON 已返回 URL,图片 GET 很久才结束 | 下载链路 | 切换代理 / 海外服务器测速,检查 CDN |
| 图片 GET 完成后仍卡顿 | 客户端解码 / 渲染 | 降低同时加载张数、检查超大图与内存占用,使用缩略图预览 |
| 只首次访问 CDN 图片慢 | 缓存未命中或回源慢 | 检查缓存规则与回源链路,不用重新生成图片 |
下载测速示例(不会触发生图)
curl -L --max-time 180 -o /dev/null -sS \
-w 'HTTP=%{http_code} DNS=%{time_namelookup}s CONNECT=%{time_connect}s TTFB=%{time_starttransfer}s TOTAL=%{time_total}s BYTES=%{size_download} SPEED=%{speed_download}B/s\n' \
"$IMAGE_URL"
在本地直连、代理线路、海外服务器分别测试同一张图片。TTFB 是到首字节的累计时间,TOTAL 是整次下载时间;这条命令测的是下载,不包含浏览器解码。公开图片 URL 不需要生图 API Key,不要将 Key 发给图片域名。
网站打不开或访问卡顿
先尝试稳定海外代理线路访问,确认代理不仅开启,而且覆盖目标网页与图片请求。若更换线路后恢复,通常与当前直连网络或跨境链路有关。若代理和海外服务器访问也失败,应检查网站状态、DNS、TLS 证书、403 访问拦截或 5xx 服务错误,不能把所有故障都视为“缺少代理”。
网页能打开但图片卡,重点检查图片请求;网页本身打不开,先检查网页域名。CDN 接入只影响配置的图片分发,不会自动解决控制台页面、登录请求或 API 本身的网络问题。
接入检查与常见问题
上线前,用最小请求逐步验证整个链路。
- 使用正确的 BASE_URL 和 Key 拉取
/v1/models。 - 以 n=1 完成一次文生图,确认 data 中的图片可下载。
- 免 Key 上传 PNG,确认返回的 URL 可由服务器访问。
- 将 URL 放入 images 数组调用 edits,验证图生图。
- 配置真实回调地址,保存 202 中的 request_id 与 status_url。
- 验证成功、失败、超时后的轮询及回调去重。
- 以同一幂等键重发同一请求,确保业务不会重复创建任务。
- 保存最终图片到自己的存储,不长期依赖临时链接。
- 配置 CDN 时确认 cdn_key 已授权,并验证返回域名、实际下载和 Content-Type。
- 分别在本地、代理线路与海外服务器检查图片下载,区分生成、传输和解码耗时。
- 按尺寸表验证需要的比例,避开明确标为不支持的组合。
上传和生图可以使用不同的地址吗?
可以。上传链接只要能被生图服务从服务端公开访问即可;上传无需生图 Key。任务查询则应使用创建任务时返回的 status_url。
收到 202,为什么还没有图片?
202 表示受理。等待 status=completed 后读取 data;或处理 image.completed 回调。HTTP 200 的任务查询也可能是 running 或 failed。
可以不提供 callback_url,直接 async=true 吗?
当前 Images 接口通过 callback_url 开启异步。没有此字段且 stream=false 时会同步等待。请配置真实回调地址,再使用轮询作为结果读取或兜底。
为什么 URL 在浏览器能打开,图生图却失败?
浏览器可能带了登录 Cookie,或源站有防盗链、IP 限制。确认无需任何认证即可 GET 到实际图片字节,而非 HTML 页面;检查图片是否过期、格式和大小是否合规。
超时后可以立即重新生成吗?
先使用 request_id 查询。超时只意味着客户端没有及时拿到结果,任务可能仍在执行。没有拿到任务 ID 时,复用原幂等键及请求体,避免换键产生重复任务。
网页文档会存储我的 Key 或发送测试请求吗?
不会。此页只提供说明和代码复制,不收集 API Key,也不会自动调用生成或上传接口。
为什么图片下载很慢,必须用代理吗?
海外资源在部分网络直连速度较差。可用稳定海外代理线路改善本地访问,或由海外后端下载后转存 CDN。是否必须用代理取决于实际链路;面向用户的产品建议提供可就近访问的 CDN 链接。详见下载排查。
为什么生图只要 40 秒,网页却几分钟才渲染?
生成时间通常不包含浏览器下载和解码。任务已完成后,低速下载大图可能花数分钟。先看图片请求的 TOTAL、下载字节数与速度;若图片已下载完才卡,再查浏览器内存和渲染性能。
网站打不开、网站卡顿怎么办?
先尝试稳定海外代理,确认网页请求确实走代理。若仅直连慢,重点处理网络线路;若海外线路也不通,检查 DNS、证书、HTTP 状态及服务可用性。不要反复刷新或重复提交生图来测试网站。
创建 CDN Key 后为什么还是不能用?
需确认 CDN 配置启用、Key 启用,以及接入方已给生图账号授权。出现 cdn_key_not_authorized 时先处理授权;存储密钥正确也不能替代这一步。
用了 CDN,为什么第一次访问还是慢?
第一次可能需要回源;跨区域网络、源站带宽与文件大小仍影响速度。确认返回的是自己的 CDN 域名,并检查缓存命中情况。只填海外存储原始域名,不一定获得就近加速。
能用 CDN 加快模型生成吗?
CDN 主要改善生成结果的下载与分发,不直接缩短模型生成。配置 CDN 还会增加一次交付步骤;应分别衡量生成时间、CDN 上传时间与最终用户下载时间。
4K 必须写 4096x4096 吗?
不是。4K 是档位名称,当前正方形预设为 3840x3840,不同比例宽高不同。请按14 种比例表选择,不要把 size 写成字符串 4k。
为什么 1K 的 3:4 不是严格 3:4?
历史预设映射为 896x1152,是近似比例。需要严格画布比例时写明确像素尺寸,例如 768x1024,并确认模型输出尺寸;不要只依据预设标签推断最终宽高。
为什么生成后又上传到我的 CDN,链接没有 .png?
对象按任务 ID 与图片序号保存,URL 不必带文件扩展名。浏览器依赖 Content-Type 识别格式。下载保存时根据实际类型设置文件名即可。
返回 b64_json 会比 URL 更快吗?
不一定。Base64 体积通常比原始图片大约三分之一,还需解析与解码。面向浏览器展示时,URL + CDN 更容易缓存和按需加载。
上传 URL 过期后,换一个生图接口就能恢复吗?
不能。链接过期是素材已经清理,需要重新上传原文件。跨入口只改变谁下载图片,不会恢复已删除素材;同内容重复上传也不承诺刷新旧素材的保留时间。
浏览器报 CORS,但后端调用正常怎么办?
检查实际接入地址是否允许你的网页来源和请求头;图片 CDN 的下载 / Canvas 使用也可能需要单独配置 CORS。建议用自己的后端调用生图接口,不要为了调试把 Key 暴露到网页源码。
提示 401、额度不足或模型不存在怎么办?
401 核对 Key 和 Base URL 是否来自同一平台;额度与分组限制到控制台查看;模型不存在先请求 /v1/models。不要把分组显示名、模型别名或截图中的脱敏 Key 直接用于请求。
回调收不到,可以直接重新生图吗?
先用 status_url 查询已有任务。检查回调地址连通性、HTTPS、接口是否接受 POST,以及是否快速返回 2xx。回调中断不表示图片生成失败,重复生成可能产生额外用量。