图片生成 API · 文档

← 返回面板

异步任务式图片生成:创建任务(请求体透传给上游)→ 后台队列生成 → 轮询任务状态 → 拿结果图。 所有写操作立即返回任务 ID(202),不阻塞等待出图。

1. 鉴权 2. 通用约定 3. 任务状态机 4. 创建文生图任务 5. 创建图片编辑任务 6. 列出任务 7. 查询任务详情 8. 下载结果图 9. 静态图 / 健康检查 10. 数据结构 11. 完整流程示例

1. 鉴权

每个 /api/v1/* 请求都要带请求头 X-API-Key,它的值就是你的上游服务商 key

这个 key 会随任务临时存库供异步 worker 调上游用,任务结束(成功/失败)后立即从库中清除,不长期保留。

2. 通用约定

Base URLhttp://localhost:8000
鉴权头X-API-Key: <你的上游key>
时间格式ISO 8601 UTC,如 2026-06-30T01:16:08.087Z
错误响应体{"error": {"code": 401, "message": "..."}}

状态码

含义
202任务已创建并入队
200查询成功
400请求体为空 / 非法(generations 非 JSON、edits 非 multipart)
401缺少 API key
404任务不存在或不属于该 key
410结果文件已被清理
413请求体超过 MAX_BODY_BYTES(默认 50MB)
422路径/查询参数校验失败(非法 UUID、index 非整数等)
503数据库不可用(仅 /healthz)

3. 任务状态机

pendingrunningsucceeded / failed

状态含义
pending已入队,等待 worker 认领
runningworker 正在调上游生成(含失败重试期间)
succeeded生成成功,result 含结果图
failed重试耗尽仍失败,error/error_status 写明原因
建议轮询间隔 1~3 秒。终态(succeeded/failed)不再变化即可停止轮询。

4. 创建文生图任务

POST /api/v1/tasks/generations

请求体是 JSON,会被原样透传给上游 /images/generations。字段以你的上游服务商文档为准,常见如下:

字段类型说明
modelstring模型名,如 gpt-image-2
promptstring提示词(必填)
sizestring1024x1024 / 3840x2160
qualitystringhigh / auto
output_formatstringpng / jpeg
streambooltrue 时让上游走 SSE 流式(服务端会据此设 Accept)
image_urlsarray图生图用:元素可为 http(s) URL(直传上游)、data:image/...;base64,裸 base64;后两者服务端落盘换成公网 URL 再发上游
n / background / …其余字段一并透传
传图字段兼容 image_urls / reference_images / images:[{"image_url":...}],元素可为 http(s) URL / data-URI / 裸 b64;multipart 上传文件同样支持。服务端会把 b64/上传图落盘到公开的 /inputs,统一改写成上游要的 images:[{"image_url":"完整公网URL"}],并转发到 /images/edits(无图则走 /images/generations)。需把 PUBLIC_INPUT_BASE_URL 配成公网域名,上游才 fetch 得到。可在任务详情「实际上传参数」里看到最终发给上游的内容。

请求示例

curl -X POST http://localhost:8000/api/v1/tasks/generations \
  -H "X-API-Key: <你的上游key>" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-image-2",
    "prompt": "a red apple on a wooden table, studio light",
    "size": "1024x1024",
    "quality": "high",
    "output_format": "png"
  }'

响应 202

{
  "id": "c69550f6-f94f-4e3d-89dc-7cbf5ca429fb",
  "status": "pending",
  "endpoint": "generations",
  "created_at": "2026-06-30T01:16:08.087Z"
}

5. 创建图片编辑任务

POST /api/v1/tasks/edits ?stream=0

请求体是 multipart/form-data整体原样透传给上游 /images/edits(服务端不解析)。参考图用字段名 image[] 上传(可多张)。

字段类型说明
image[]file参考图,可重复多张
model / prompt / size / quality / output_formatform同 generations,按上游约定
?stream=1query可选,让上游走 SSE(默认关)

请求示例

curl -X POST http://localhost:8000/api/v1/tasks/edits \
  -H "X-API-Key: <你的上游key>" \
  -F "model=gpt-image-2" \
  -F "prompt=给人物加一顶帽子" \
  -F "size=1024x1024" \
  -F "image[][email protected];type=image/jpeg"

响应 202

{ "id": "…", "status": "pending", "endpoint": "edits", "created_at": "…" }

6. 列出任务

GET /api/v1/tasks

仅返回当前 key 的任务,按创建时间倒序。

查询参数默认说明
status过滤:pending/running/succeeded/failed
limit201~100
offset0分页偏移

响应 200

{
  "items": [ { /* TaskOut,见 §10 */ } ],
  "total": 42,
  "limit": 20,
  "offset": 0
}

7. 查询任务详情

GET /api/v1/tasks/{id}

返回单个任务(TaskOut)。不属于当前 key 或不存在 → 404。轮询这个端点直到 status 为终态。

响应 200(succeeded 示例)

{
  "id": "c69550f6-f94f-4e3d-89dc-7cbf5ca429fb",
  "endpoint": "generations",
  "status": "succeeded",
  "retry_count": 0,
  "request_summary": { "model": "gpt-image-2", "size": "1024x1024", "prompt_chars": 42 },
  "result": [ { "file_url": "https://api.你的域名.com/outputs/generated_c69550f6_1.png" } ],
  "error": null,
  "error_status": null,
  "created_at": "2026-06-30T01:16:08.087Z",
  "started_at": "2026-06-30T01:16:08.096Z",
  "finished_at": "2026-06-30T01:16:08.112Z",
  "updated_at": "2026-06-30T01:16:08.112Z"
}

failed 示例

{
  "id": "…", "status": "failed", "retry_count": 3,
  "result": null,
  "error": "upstream 401: invalid api key",
  "error_status": 401,
  ...
}

8. 下载结果图(鉴权)

GET /api/v1/tasks/{id}/images/{index} ?download=0

校验任务归属后直出图片字节。indexresult 数组下标(从 0 起)。?download=1 触发浏览器下载(attachment)。文件已被清理 → 410

9. 静态图 / 健康检查

GET /outputs/<filename>

结果图的公开静态地址(即 result[].file_url)。文件名含不可猜的 UUID,作为能力令牌;无需鉴权,可直接 <img src> 加载。

GET /healthz

无需鉴权。{"status":"ok","db":"ok","queue_depth":0,"workers":3};DB 不通返回 503

10. 数据结构

TaskOut

字段类型说明
iduuid任务 ID
endpointstringgenerations / edits
statusstringpending / running / succeeded / failed
retry_countint已重试次数
request_summaryobject|null脱敏摘要(model/size/quality/prompt_chars 等)
resultarray|null结果项数组,见下;失败为 null
errorstring|null失败时的上游错误文本(已截断)
error_statusint|null失败时的上游 HTTP 状态码(网络错误为 null)
created_at / started_at / finished_at / updated_atstring|nullISO8601 时间戳

result 项

字段类型说明
file_urlstring结果图的完整 URL,如 https://api.域名/outputs/generated_<uuid>_1.png(按你访问 API 的域名自动生成)
remote_urlstring?上游若直接回图床 URL 时附带(可选)
出于安全,result 不含服务端绝对路径,任务响应里也不会出现 key / owner 等敏感字段。

11. 完整流程示例

curl

# 1) 创建任务,拿到 id
ID=$(curl -s -X POST http://localhost:8000/api/v1/tasks/generations \
  -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
  -d '{"model":"gpt-image-2","prompt":"a red apple","size":"1024x1024"}' \
  | python -c "import sys,json;print(json.load(sys.stdin)['id'])")

# 2) 轮询直到终态
curl -s -H "X-API-Key: $KEY" http://localhost:8000/api/v1/tasks/$ID

Python

import time, requests
BASE, KEY = "http://localhost:8000", "<你的上游key>"
h = {"X-API-Key": KEY}

r = requests.post(f"{BASE}/api/v1/tasks/generations", headers=h,
                  json={"model": "gpt-image-2", "prompt": "a red apple", "size": "1024x1024"})
tid = r.json()["id"]

while True:
    t = requests.get(f"{BASE}/api/v1/tasks/{tid}", headers=h).json()
    if t["status"] in ("succeeded", "failed"):
        break
    time.sleep(2)

if t["status"] == "succeeded":
    url = t["result"][0]["file_url"]          # 已是完整 URL
    open("out.png", "wb").write(requests.get(url).content)
else:
    print("failed:", t["error_status"], t["error"])

Python(图生图 / 编辑:multipart 上传参考图)

import time, requests
BASE, KEY = "http://localhost:8000", "<你的上游key>"
h = {"X-API-Key": KEY}

# 参考图用字段名 image[] 上传(可多张);其余字段放 data
files = [("image[]", ("raw.jpg", open("raw.jpg", "rb"), "image/jpeg"))]
data = {"model": "gpt-image-2", "prompt": "给人物加一顶帽子", "size": "1024x1024"}
r = requests.post(f"{BASE}/api/v1/tasks/edits", headers=h, files=files, data=data)
tid = r.json()["id"]

while True:
    t = requests.get(f"{BASE}/api/v1/tasks/{tid}", headers=h).json()
    if t["status"] in ("succeeded", "failed"):
        break
    time.sleep(2)

if t["status"] == "succeeded":
    url = t["result"][0]["file_url"]          # 已是完整 URL
    open("edited.png", "wb").write(requests.get(url).content)
else:
    print("failed:", t["error_status"], t["error"])