GPT Image 图片编辑 API
通过 BetterToken 的 OpenAI 兼容接口调用 GPT Image,根据参考图片生成或编辑图片。
POST /v1/images/edits
图生图接口使用 multipart/form-data 请求体。上传一张或多张参考图片,并通过表单字段提交提示词和参数。普通参考图编辑不需要提供 mask。
推荐请求值
以下为起步建议,并非固定要求。建议先使用 n=1;接口接受 1 到 10 的整数,但实际返回图片数可能更少。
model=gpt-image-2n=1response_format=b64_jsonoutput_format=png单参考图使用字段名 image。多参考图使用可重复的字段名 image[]。
可选择 gpt-image-2、gpt-image-2.5-flare 或 gpt-image-2.5-sunburst;三款模型均已有普通参考图编辑成功结果。当前可用性请查看模型广场。
quality 为可选字段。gpt-image-2 接受 auto、low、medium、high;两款 2.5 模型还接受 xhigh 和 max。传入时必须使用非空字符串;不设置偏好时直接省略字段。size 同样可以省略,但不能传空字符串。质量参数被接受不保证画面质量随档位提高。
单参考图
准备真实参考图 reference.png,将 YOUR_API_KEY 替换为你的 BetterToken API Key。一个 Key 可调用所有可用模型。使用 curl -F 或 Python files 时,让客户端自动生成包含 boundary 的 Content-Type,不要手动覆盖。
提交这些表单字段:
| 表单字段 | 示例值 |
|---|---|
model | gpt-image-2 |
prompt | 参考这张图片,生成一张更精致的方形产品主视觉,保持主体风格一致。 |
n | 1 |
size | 1024x1024 |
response_format | b64_json |
output_format | png |
image | 选择本地文件 reference.png |
curl --max-time 300 'https://bettertoken.ai/v1/images/edits' \ -H 'Authorization: Bearer YOUR_API_KEY' \ -F 'model=gpt-image-2' \ -F 'prompt=参考这张图片,生成一张更精致的方形产品主视觉,保持主体风格一致。' \ -F 'n=1' \ -F 'size=1024x1024' \ -F 'response_format=b64_json' \ -F 'output_format=png' \ -F 'image=@reference.png'多参考图
提交这些表单字段:
| 表单字段 | 示例值 |
|---|---|
model | gpt-image-2 |
prompt | 综合这些参考图,生成一张统一风格的宣传海报。 |
n | 1 |
size | 1024x1024 |
response_format | b64_json |
output_format | png |
image[] | 选择本地文件 reference-1.png |
image[] | 选择本地文件 reference-2.jpg |
image[] | 选择本地文件 reference-3.png |
curl --max-time 300 'https://bettertoken.ai/v1/images/edits' \ -H 'Authorization: Bearer YOUR_API_KEY' \ -F 'model=gpt-image-2' \ -F 'prompt=综合这些参考图,生成一张统一风格的宣传海报。' \ -F 'n=1' \ -F 'size=1024x1024' \ -F 'response_format=b64_json' \ -F 'output_format=png' \ -F 'image[]=@reference-1.png' \ -F 'image[]=@reference-2.jpg' \ -F 'image[]=@reference-3.png'按蒙版局部编辑
需要引导模型修改指定区域时,可以增加一个可选文件字段 mask,不要重复提交 mask[]。Mask 编辑的可用性取决于模型和尺寸组合,普通编辑成功不代表 Mask 编辑也可用。
- 原图和 mask 必须使用相同的文件格式和像素尺寸,每个文件必须小于 50 MB。为了获得最佳兼容性,建议原图和 mask 都使用带 Alpha 通道的 PNG。
- mask 必须实际包含 Alpha 通道。完全透明像素(
Alpha 0)表示允许模型重绘的区域;完全不透明像素(Alpha 255)用于引导模型保留该区域。 - 普通 RGB 黑白图不是有效 mask,除非文件实际包含 Alpha 通道。
- 如果提交多个
image[]字段,mask 会应用于第一张输入图。 - mask 是生成引导,不保证区域外的主体或像素保持不变。GPT Image 2 和 Flare 的实测均出现过非目标区域明显变化;Sunburst 的局部区域控制效果尚未验证。用于商品、标识或精确修图时,请检查非编辑区域后再采用结果。
curl --max-time 300 'https://bettertoken.ai/v1/images/edits' \ -H 'Authorization: Bearer YOUR_API_KEY' \ -F 'model=gpt-image-2' \ -F 'prompt=Replace only the masked area with a blue circle. Preserve everything else.' \ -F 'n=1' \ -F 'size=1024x1024' \ -F 'response_format=b64_json' \ -F 'output_format=png' \ -F 'image=@original.png' \ -F 'mask=@mask.png'使用 Python 按蒙版编辑
准备 original.png 和符合下方要求的 mask.png。Python 示例需要 requests;文件检查示例还需要 Pillow。在同一个 with 语句中打开两个文件,确保请求结束后正确关闭文件:
import requests
url = "https://bettertoken.ai/v1/images/edits"headers = {"Authorization": "Bearer YOUR_API_KEY"}data = { "model": "gpt-image-2", "prompt": "Replace only the masked area with a blue circle. Preserve everything else.", "n": "1", "size": "1024x1024", "response_format": "b64_json", "output_format": "png",}
with open("original.png", "rb") as image_file, open("mask.png", "rb") as mask_file: files = { "image": ("original.png", image_file, "image/png"), "mask": ("mask.png", mask_file, "image/png"), } response = requests.post(url, headers=headers, data=data, files=files, timeout=300)
response.raise_for_status()result = response.json()上传前检查文件
下面的最小 Pillow 检查会验证文件格式、尺寸和 Alpha 通道这些强制要求。对于常见的局部 mask,如果 Alpha 通道没有同时包含完全透明和完全不透明的区域,代码还会输出提醒:
from pathlib import Pathfrom PIL import Image
image_path = Path("original.png")mask_path = Path("mask.png")
if image_path.stat().st_size >= 50 * 1024 * 1024 or mask_path.stat().st_size >= 50 * 1024 * 1024: raise ValueError("Each file must be smaller than 50 MB")
with Image.open(image_path) as image, Image.open(mask_path) as mask: if image.format != mask.format: raise ValueError("The image and mask formats must match") if image.size != mask.size: raise ValueError("The image and mask dimensions must match") if "A" not in mask.getbands(): raise ValueError("The mask must contain an Alpha channel") alpha_min, alpha_max = mask.getchannel("A").getextrema() if alpha_min > 0 or alpha_max < 255: print("Warning: for a typical local mask, include fully transparent and fully opaque areas")Python 示例
此示例需要 requests。准备 reference.png 并替换 API Key 后运行;它会提交普通编辑请求,并按实际格式保存返回图片。
import base64from pathlib import Path
import requests
url = "https://bettertoken.ai/v1/images/edits"headers = { "Authorization": "Bearer YOUR_API_KEY",}
data = { "model": "gpt-image-2", "prompt": "参考这张图片,生成一张更精致的方形产品主视觉,保持主体风格一致。", "n": "1", "size": "1024x1024", "response_format": "b64_json", "output_format": "png",}
with open("reference.png", "rb") as image_file: files = { "image": ("reference.png", image_file, "image/png"), } response = requests.post(url, headers=headers, data=data, files=files, timeout=300)
response.raise_for_status()result = response.json()for index, item in enumerate(result["data"], start=1): encoded = item["b64_json"] if "," in encoded and "base64" in encoded.split(",", 1)[0]: encoded = encoded.split(",", 1)[1] image_bytes = base64.b64decode(encoded) if image_bytes.startswith(b"\x89PNG\r\n\x1a\n"): extension = "png" elif image_bytes.startswith(b"\xff\xd8\xff"): extension = "jpg" elif image_bytes.startswith(b"RIFF") and image_bytes[8:12] == b"WEBP": extension = "webp" else: raise ValueError("Unsupported image signature") Path(f"output-{index}.{extension}").write_bytes(image_bytes)推荐尺寸
size | 比例 | 适用场景 |
|---|---|---|
auto | 自动 | 自动选择尺寸 |
1024x1024 | 1:1 | 方图,通用头像、封面、素材图 |
1536x1024 | 3:2 | 横图,海报、banner、场景图 |
1024x1536 | 2:3 | 竖图,移动端封面、人物海报 |
1536x1152 | 4:3 | 标准横图,商品图、内容配图、横版素材 |
1152x1536 | 3:4 | 标准竖图,移动端封面、竖版海报、人物图 |
2048x2048 | 1:1 | 高清方图 |
2048x1152 | 16:9 | 高清横图 |
3840x2160 | 16:9 | 4K 横图 |
2160x3840 | 9:16 | 4K 竖图 |
表格列出常用示例,并非封闭清单。显式尺寸必须为正数且是 16 的倍数,宽高各不超过 3840,宽高比不超过 3:1,像素总数为 655360 到 8294400。例如 2080x2080 是合法尺寸。实际交付尺寸可能不同;请读取图片真实尺寸,不要强行裁切。
保存返回图片
成功响应为 OpenAI 兼容图片响应结构。下面的金额用于展示回执格式,不代表固定价格:
{ "created": 1710000000, "data": [ { "b64_json": "iVBORw0KGgoAAAANSUhEUgAA...(truncated)", "revised_prompt": "Optional revised prompt" } ], "bettertoken_image_billing": { "currency": "CNY", "settled_amount": "0.15", "outputs": [ { "index": 0, "width": 1254, "height": 1254, "billing_resolution": "2K", "currency": "CNY", "amount": "0.15" } ] }}客户端应读取 data[].b64_json 中的每张图片,并允许 revised_prompt 等额外字段。BetterToken 在根级 bettertoken_image_billing 对象中返回计费明细:settled_amount 和每张图片的 amount 是十进制字符串,outputs[].index 从 0 开始。
保存图片时请检查图片字节并使用匹配的文件扩展名。业务需要固定编码时再按需转码;仅修改扩展名不会转换图片。文生图页面提供识别 PNG、JPEG、WebP 并保存每张图片的 Python 和 Node.js 标准库示例。
读取实际费用
bettertoken_image_billing 随生成结果一起返回,无需额外调用费用查询接口。
| 字段 | 含义 |
|---|---|
currency | 本次结算币种。不要假定始终为 USD 或 CNY。 |
settled_amount | 本次请求实际结算总额,使用十进制字符串表示。 |
requested_resolution | 根据请求识别的尺寸档位。 |
outputs[].index | 图片编号,从 0 开始,对应 data[]。 |
outputs[].width、height | 对应图片的实际宽、高,单位为像素。 |
outputs[].billing_resolution | 对应图片最终使用的计费档位。 |
outputs[].currency、amount | 对应图片的结算币种和金额。 |
请求尺寸与计费档位可能不同。例如,已有请求 1024×1024、实际返回 1254×1254 并按 2K 结算的结果。请读取回执中的实际金额;当前单价在模型广场查看。
下面的代码接在 result = response.json() 后使用。金额保留为字符串展示;需要计算时使用十进制类型,避免浮点误差。缺少回执时不能把费用记为零。
from decimal import Decimal
billing = result.get("bettertoken_image_billing")if billing is None: print("Billing receipt unavailable; check your usage records.")else: total = Decimal(billing["settled_amount"]) print(f"Total: {billing['currency']} {total}") for output in billing.get("outputs", []): print( f"Image {output['index'] + 1}: " f"{output['width']}x{output['height']}, " f"{output['billing_resolution']}, " f"{output['currency']} {output['amount']}" )响应流程
图生图接口是同步响应模式。客户端提交 POST /images/edits 后,应保持 HTTP 请求连接并等待服务端返回。成功时,每张图片都在同一响应的 data[].b64_json 中返回,并附有交付与计费明细。
图生图不会返回 task_id,也没有单独的状态查询接口或结果下载接口。不需要轮询任务状态,也不能通过额外的 /images/... URL 再获取结果。
超时、重试和计费
HTTP 客户端超时建议设置为数分钟。超时、断连或含义不明的 5xx 不能证明编辑是否执行或计费;结算后写回结果时连接也可能失败,因此存在重复计费风险。不要自动重复编辑 POST。先检查消费记录或请求日志,保留返回的 request ID,以及已收到时的 X-BetterToken-Image-Receipt 和 X-BetterToken-Billing-State 响应头。客户端生成的 request ID 不会让服务端具备幂等性;主动重发可能产生新请求和新费用。没有单独的状态查询接口。
先修正被拒绝的输入、认证或额度问题。只有确认 429 表示请求已被拒绝后,才可退避重试。处理前,BetterToken 可能按最高适用单价乘以 n 预授权上限。因此,余额可能足以支付实际交付的 1K 图片,却不足以通过预授权;此时请求返回 403,且 error.code=insufficient_user_quota。最终按实际交付图片结算,根级 bettertoken_image_billing 返回最终金额、币种、交付尺寸和分档。
接入检查清单
- Base URL 是
https://bettertoken.ai/v1。 - Header 包含
Authorization: Bearer sk-...。 - 请求使用
multipart/form-data。 - 根据当前请求选择可用模型:
gpt-image-2、gpt-image-2.5-flare或gpt-image-2.5-sunburst。 - 单参考图使用
image,多参考图使用重复的image[]。 - 按蒙版编辑时只提交一个
mask,并确认它与原图格式、尺寸相同且包含 Alpha 通道。 - 使用
response_format=b64_json;output_format是请求值,保存前按字节识别实际格式。 n可设为1到10;建议使用1,实际返回数可能更少。size可省略、设为auto或填写符合约束的尺寸。- 客户端能处理数分钟级别的生成耗时。
- 日志和报错截图中不会泄露 API Key 或完整 base64 图片内容。
相关文档
/v1/images/edits授权
Use your BetterToken API Key as a bearer token. Do not expose API keys in frontend browser code, screenshots, logs, tickets, or Git repositories.
请求体 multipart/form-data
GPT Image 模型 ID。一个 BetterToken API Key 可调用所有可用模型。
"gpt-image-2"单张参考图字段名。单参考图时使用 image。
多张参考图字段名。多参考图时重复提交 image[]。
可选的单个蒙版文件。当前后端只读取一个 mask。蒙版必须含 Alpha 通道,并与原图格式、尺寸一致;每个文件必须小于 50 MB。为获得最佳兼容性,建议原图和 mask 都使用带 Alpha 通道的 PNG。透明区域允许模型重绘,不透明区域用于引导保留;提交多个 image[] 时,mask 作用于第一张输入图。可用性取决于模型和尺寸组合。mask 是生成引导,不保证区域外主体或像素保持不变;请验收结果。
省略或 auto 使用自动尺寸。WxH 的宽高必须为正数且均为 16 的倍数,单边不超过 3840,长短边比不超过 3:1,总像素为 655360~8294400。示例不是封闭枚举,例如 2080x2080 也符合几何约束。实际输出以解码尺寸为准;参数合法不保证有对应可用服务。
"1024x1024"["auto","1024x1024","2048x2048","2080x2080","3840x2160"]请求的输出格式,例如 png。保存时按实际图片字节识别 PNG、JPEG 或 WebP,再选择对应扩展名;需要固定编码时按需转码。
"png"画质选项:gpt-image-2 使用 auto、low、medium、high;两款 2.5 模型还接受 xhigh、max。省略时使用模型默认值;JSON null 按未填写处理,multipart 必须填写非空字符串。参数合法不保证当前有对应模型、尺寸和编辑组合的可用服务。参数接受不保证画面质量随档位提高。
"auto"响应
200BetterToken 扩展计费信息。按实际交付图片的像素与计费档位结算;请求档位不保证等于计费档位。金额是十进制字符串,以响应为准。
OpenAI 兼容图片响应。遍历 data[],读取每张图片的 b64_json,并允许 revised_prompt 等额外字段。实际交付数量可能少于请求数量。
1710000000Show child attributes
Optional revised prompt.
BetterToken 扩展计费信息。按实际交付图片的像素与计费档位结算;请求档位不保证等于计费档位。金额是十进制字符串,以响应为准。
Show child attributes
本次结算币种,不应假定为固定币种。
本次实际结算总额,十进制字符串;计算时使用十进制类型。
实际交付图片的编号、真实尺寸、计费档位和单张金额。index 从 0 开始,对应 data[] 中的图片。
对应图片的实际结算金额,十进制字符串。
根据请求识别的尺寸档位,可能与实际计费档位不同。
{
"created": 1710000000,
"data": [
{
"b64_json": "iVBORw0KGgoAAAANSUhEUgAA...(truncated)",
"revised_prompt": "<string>"
}
],
"bettertoken_image_billing": {
"currency": "<string>",
"settled_amount": "<string>",
"outputs": [
{
"index": 0,
"width": 1,
"height": 1,
"billing_resolution": "1K",
"currency": "<string>",
"amount": "<string>"
}
],
"partial": false,
"requested_resolution": "<string>"
}
}400请求格式错误、缺少参数、JSON 或 multipart 解析失败、尺寸格式错误。
Show child attributes
"invalid request""invalid_request_error""invalid_request""insufficient quota"{
"error": {
"message": "invalid request",
"type": "invalid_request_error",
"code": "invalid_request"
},
"message": "insufficient quota"
}401API Key 缺失或无效。
Show child attributes
"invalid request""invalid_request_error""invalid_request""insufficient quota"{
"error": {
"message": "invalid request",
"type": "invalid_request_error",
"code": "invalid_request"
},
"message": "insufficient quota"
}402额度或余额不足。
Show child attributes
"invalid request""invalid_request_error""invalid_request""insufficient quota"{
"error": {
"message": "invalid request",
"type": "invalid_request_error",
"code": "invalid_request"
},
"message": "insufficient quota"
}403余额或令牌额度不足,error.code 为 insufficient_user_quota。也可能不足以预占最高档单价乘图片数量上限的额度;按实际交付结算。结合 error.code 判断,不要只按 403 认定为权限错误。
{
"error": {
"code": "insufficient_user_quota",
"message": "Insufficient balance or token quota."
}
}429触发限速、并发限制或上游繁忙。
Show child attributes
"invalid request""invalid_request_error""invalid_request""insufficient quota"{
"error": {
"message": "invalid request",
"type": "invalid_request_error",
"code": "invalid_request"
},
"message": "insufficient quota"
}500网关或上游服务异常。
Show child attributes
"invalid request""invalid_request_error""invalid_request""insufficient quota"{
"error": {
"message": "invalid request",
"type": "invalid_request_error",
"code": "invalid_request"
},
"message": "insufficient quota"
}
