GPT-Image-2 代码示例
本页提供可独立运行的 Python 和 Node.js 示例,覆盖同步生成、URL 编辑、本地多图编辑、异步提交与查询,以及 Webhook 接收。所有示例只使用公开 API。
接口参数说明
完整请求字段、上传限制和错误格式请查看参数与错误。实际模型名称以模型广场和 GET /v1/models 为准。
准备环境
export SUPERTOKEN_BASE_URL="https://api.supertoken.cc"
export SUPERTOKEN_KEY="YOUR_MODEL_API_TOKEN"
export RESOURCE_API_KEY="YOUR_RESOURCE_API_KEY"
export IMAGE_MODEL="gpt-image-2"Python 示例使用 Python 3.10+:
python3 -m pip install requests flaskNode.js 示例使用 Node.js 20+,直接使用内置的 fetch、FormData 和 Blob,不需要安装依赖。请把 Node.js 示例保存为 .mjs 文件运行。
同步文生图
调用 POST /v1/images/generations,请求完成后从 data[].url 或 data[].b64_json 读取图片。
import os
import requests
base_url = os.getenv("SUPERTOKEN_BASE_URL", "https://api.supertoken.cc").rstrip("/")
api_key = os.environ["SUPERTOKEN_KEY"]
model = os.getenv("IMAGE_MODEL", "gpt-image-2")
response = requests.post(
f"{base_url}/v1/images/generations",
headers={
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
},
json={
"model": model,
"prompt": "电影感产品摄影,透明玻璃杯放在黑色石材桌面上,柔和侧光",
"n": 1,
"size": "1024x1024",
"quality": "low",
"output_format": "png",
},
timeout=300,
)
response.raise_for_status()
result = response.json()
for index, image in enumerate(result["data"], start=1):
print(f"图片 {index}:", image.get("url") or "返回了 b64_json")const baseUrl = (process.env.SUPERTOKEN_BASE_URL ?? 'https://api.supertoken.cc')
.replace(/\/$/, '');
const apiKey = process.env.SUPERTOKEN_KEY;
const model = process.env.IMAGE_MODEL ?? 'gpt-image-2';
if (!apiKey) throw new Error('SUPERTOKEN_KEY is required');
const response = await fetch(`${baseUrl}/v1/images/generations`, {
method: 'POST',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
model,
prompt: '电影感产品摄影,透明玻璃杯放在黑色石材桌面上,柔和侧光',
n: 1,
size: '1024x1024',
quality: 'low',
output_format: 'png'
})
});
const result = await response.json();
if (!response.ok) throw new Error(JSON.stringify(result));
for (const [index, image] of result.data.entries()) {
console.log(`图片 ${index + 1}:`, image.url ?? '返回了 b64_json');
}同步 URL 多图编辑
同步 URL 编辑使用顶层 image 数组,不要使用 images[].image_url。
import os
import requests
base_url = os.getenv("SUPERTOKEN_BASE_URL", "https://api.supertoken.cc").rstrip("/")
api_key = os.environ["SUPERTOKEN_KEY"]
model = os.getenv("IMAGE_MODEL", "gpt-image-2")
response = requests.post(
f"{base_url}/v1/images/edits",
headers={
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
},
json={
"model": model,
"prompt": "保留第一张图的主体,并使用第二张图的背景和光线",
"image": [
"https://img.example.com/reference-1.png",
"https://img.example.com/reference-2.png",
],
"size": "1024x1024",
"quality": "low",
"output_format": "png",
},
timeout=300,
)
response.raise_for_status()
result = response.json()
print(result["data"][0].get("url") or "返回了 b64_json")const baseUrl = (process.env.SUPERTOKEN_BASE_URL ?? 'https://api.supertoken.cc')
.replace(/\/$/, '');
const apiKey = process.env.SUPERTOKEN_KEY;
const model = process.env.IMAGE_MODEL ?? 'gpt-image-2';
if (!apiKey) throw new Error('SUPERTOKEN_KEY is required');
const response = await fetch(`${baseUrl}/v1/images/edits`, {
method: 'POST',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
model,
prompt: '保留第一张图的主体,并使用第二张图的背景和光线',
image: [
'https://img.example.com/reference-1.png',
'https://img.example.com/reference-2.png'
],
size: '1024x1024',
quality: 'low',
output_format: 'png'
})
});
const result = await response.json();
if (!response.ok) throw new Error(JSON.stringify(result));
console.log(result.data[0].url ?? '返回了 b64_json');同步本地多图编辑
本地文件使用 multipart/form-data,多张参考图重复添加 image 字段。不要手动设置 multipart 的 Content-Type,客户端会自动加入 boundary。
import os
from contextlib import ExitStack
from pathlib import Path
import requests
base_url = os.getenv("SUPERTOKEN_BASE_URL", "https://api.supertoken.cc").rstrip("/")
api_key = os.environ["SUPERTOKEN_KEY"]
model = os.getenv("IMAGE_MODEL", "gpt-image-2")
image_paths = [Path("reference-1.png"), Path("reference-2.png")]
with ExitStack() as stack:
files = [
(
"image",
(path.name, stack.enter_context(path.open("rb")), "image/png"),
)
for path in image_paths
]
response = requests.post(
f"{base_url}/v1/images/edits",
headers={"Authorization": f"Bearer {api_key}"},
data={
"model": model,
"prompt": "融合两张参考图,保持主体比例和自然光影",
"size": "1024x1024",
"quality": "low",
"output_format": "png",
},
files=files,
timeout=300,
)
response.raise_for_status()
result = response.json()
print(result["data"][0].get("url") or "返回了 b64_json")import { readFile } from 'node:fs/promises';
import { basename } from 'node:path';
const baseUrl = (process.env.SUPERTOKEN_BASE_URL ?? 'https://api.supertoken.cc')
.replace(/\/$/, '');
const apiKey = process.env.SUPERTOKEN_KEY;
const model = process.env.IMAGE_MODEL ?? 'gpt-image-2';
if (!apiKey) throw new Error('SUPERTOKEN_KEY is required');
const form = new FormData();
form.append('model', model);
form.append('prompt', '融合两张参考图,保持主体比例和自然光影');
form.append('size', '1024x1024');
form.append('quality', 'low');
form.append('output_format', 'png');
for (const path of ['reference-1.png', 'reference-2.png']) {
const bytes = await readFile(path);
form.append('image', new Blob([bytes], { type: 'image/png' }), basename(path));
}
const response = await fetch(`${baseUrl}/v1/images/edits`, {
method: 'POST',
headers: { Authorization: `Bearer ${apiKey}` },
body: form
});
const result = await response.json();
if (!response.ok) throw new Error(JSON.stringify(result));
console.log(result.data[0].url ?? '返回了 b64_json');需要 Mask 局部编辑时,Python 在 files 中再添加一个 ("mask", ...),Node.js 使用 form.append("mask", blob, "mask.png")。Mask 最多一张。
异步提交并轮询
创建任务使用普通模型 API Token,查询任务使用资源 API Key。下面示例会等待任务进入成功或失败终态。
import os
import time
import requests
base_url = os.getenv("SUPERTOKEN_BASE_URL", "https://api.supertoken.cc").rstrip("/")
api_key = os.environ["SUPERTOKEN_KEY"]
resource_key = os.environ["RESOURCE_API_KEY"]
model = os.getenv("IMAGE_MODEL", "gpt-image-2")
create_response = requests.post(
f"{base_url}/v1/image/tasks",
headers={
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
"Idempotency-Key": "demo-image-20260722-001",
},
json={
"model": model,
"operation": "generation",
"input": {"prompt": "雨夜霓虹街道的电影概念图,广角构图"},
"output": {
"count": 1,
"size": "1024x1024",
"quality": "low",
"format": "png",
},
"client_reference_id": "demo-image-001",
},
timeout=60,
)
create_response.raise_for_status()
task_id = create_response.json()["id"]
print("任务 ID:", task_id)
while True:
query_response = requests.get(
f"{base_url}/v1/image/tasks/{task_id}",
headers={"Authorization": f"Bearer {resource_key}"},
timeout=30,
)
query_response.raise_for_status()
task = query_response.json()
print("状态:", task["status"], "进度:", task["progress"])
if task["status"] == "succeeded":
for image in task["result"]["images"]:
print("图片地址:", image["url"])
break
if task["status"] == "failed":
raise RuntimeError(task["error"])
time.sleep(2)const baseUrl = (process.env.SUPERTOKEN_BASE_URL ?? 'https://api.supertoken.cc')
.replace(/\/$/, '');
const apiKey = process.env.SUPERTOKEN_KEY;
const resourceKey = process.env.RESOURCE_API_KEY;
const model = process.env.IMAGE_MODEL ?? 'gpt-image-2';
if (!apiKey) throw new Error('SUPERTOKEN_KEY is required');
if (!resourceKey) throw new Error('RESOURCE_API_KEY is required');
const createResponse = await fetch(`${baseUrl}/v1/image/tasks`, {
method: 'POST',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
'Idempotency-Key': 'demo-image-20260722-001'
},
body: JSON.stringify({
model,
operation: 'generation',
input: { prompt: '雨夜霓虹街道的电影概念图,广角构图' },
output: {
count: 1,
size: '1024x1024',
quality: 'low',
format: 'png'
},
client_reference_id: 'demo-image-001'
})
});
const createdTask = await createResponse.json();
if (!createResponse.ok) throw new Error(JSON.stringify(createdTask));
const taskId = createdTask.id;
console.log('任务 ID:', taskId);
while (true) {
const queryResponse = await fetch(`${baseUrl}/v1/image/tasks/${taskId}`, {
headers: { Authorization: `Bearer ${resourceKey}` }
});
const task = await queryResponse.json();
if (!queryResponse.ok) throw new Error(JSON.stringify(task));
console.log('状态:', task.status, '进度:', task.progress);
if (task.status === 'succeeded') {
for (const image of task.result.images) console.log('图片地址:', image.url);
break;
}
if (task.status === 'failed') throw new Error(JSON.stringify(task.error));
await new Promise((resolve) => setTimeout(resolve, 2000));
}生产环境建议为每个业务请求生成稳定且唯一的 Idempotency-Key,并遵循创建响应中的 Retry-After 建议间隔。
Webhook 接收
在控制台“资源管理中心 → Webhook”中配置接收地址并生成 wk-... Key,然后在接收服务设置:
export SUPERTOKEN_WEBHOOK_KEY="YOUR_WEBHOOK_KEY"接收端应先校验 Authorization,再按事件顶层 id 去重,并尽快返回 HTTP 2xx。
import os
from secrets import compare_digest
from flask import Flask, abort, request
app = Flask(__name__)
webhook_key = os.environ["SUPERTOKEN_WEBHOOK_KEY"]
@app.post("/webhooks/supertoken")
def receive_image_event():
authorization = request.headers.get("Authorization", "")
expected = f"Bearer {webhook_key}"
if not compare_digest(authorization, expected):
abort(401)
event = request.get_json()
event_id = event["id"]
event_type = event["type"]
task = event["data"]["object"]
# 生产环境应先在数据库中按 event_id 去重,再投递到自己的业务队列。
if event_type == "image.task.succeeded":
urls = [image["url"] for image in task["result"]["images"]]
print(event_id, task["id"], urls)
elif event_type == "image.task.failed":
print(event_id, task["id"], task["error"])
return "", 204
if __name__ == "__main__":
app.run(host="0.0.0.0", port=3000)import { timingSafeEqual } from 'node:crypto';
import { createServer } from 'node:http';
const webhookKey = process.env.SUPERTOKEN_WEBHOOK_KEY;
if (!webhookKey) throw new Error('SUPERTOKEN_WEBHOOK_KEY is required');
const expectedAuthorization = Buffer.from(`Bearer ${webhookKey}`);
function isAuthorized(value = '') {
const received = Buffer.from(value);
return received.length === expectedAuthorization.length
&& timingSafeEqual(received, expectedAuthorization);
}
createServer(async (request, response) => {
if (request.method !== 'POST' || request.url !== '/webhooks/supertoken') {
response.writeHead(404).end();
return;
}
if (!isAuthorized(request.headers.authorization)) {
response.writeHead(401).end();
return;
}
const chunks = [];
for await (const chunk of request) chunks.push(chunk);
const event = JSON.parse(Buffer.concat(chunks).toString('utf8'));
const task = event.data.object;
// 生产环境应先在数据库中按 event.id 去重,再投递到自己的业务队列。
if (event.type === 'image.task.succeeded') {
console.log(event.id, task.id, task.result.images.map((image) => image.url));
} else if (event.type === 'image.task.failed') {
console.error(event.id, task.id, task.error);
}
response.writeHead(204).end();
}).listen(3000, () => {
console.log('Webhook receiver: http://localhost:3000/webhooks/supertoken');
});Webhook 可能因为网络错误或非 2xx 响应被重复投递。完整事件 Payload 和默认重试规则见 Webhook 完成通知。