Skip to content
SuperToken 文档

GPT-Image-2 代码示例

本页提供可独立运行的 Python 和 Node.js 示例,覆盖同步生成、URL 编辑、本地多图编辑、异步提交与查询,以及 Webhook 接收。所有示例只使用公开 API。

接口参数说明

完整请求字段、上传限制和错误格式请查看参数与错误。实际模型名称以模型广场和 GET /v1/models 为准。

准备环境

bash
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+:

bash
python3 -m pip install requests flask

Node.js 示例使用 Node.js 20+,直接使用内置的 fetchFormDataBlob,不需要安装依赖。请把 Node.js 示例保存为 .mjs 文件运行。

同步文生图

调用 POST /v1/images/generations,请求完成后从 data[].urldata[].b64_json 读取图片。

python
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")
javascript
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

python
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")
javascript
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。

python
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")
javascript
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。下面示例会等待任务进入成功或失败终态。

python
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)
javascript
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,然后在接收服务设置:

bash
export SUPERTOKEN_WEBHOOK_KEY="YOUR_WEBHOOK_KEY"

接收端应先校验 Authorization,再按事件顶层 id 去重,并尽快返回 HTTP 2xx。

python
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)
javascript
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 完成通知

下一步

SuperToken - 让全球顶级 AI 模型触手可达