图片生成 API · 文档

← 返回面板

概览

异步任务式图片生成:创建任务(立即返回 202 和任务 ID)→ 后台队列调用上游生成 → 轮询任务状态 → 通过 file_url 拿结果图。所有写操作都不阻塞等待出图。

基本信息

Base URLhttp://localhost:8000(自动显示为你当前访问的域名)
鉴权X-API-Key: <key>Authorization: Bearer <key>,二者等价
请求格式application/jsonmultipart/form-data(两个创建端点都支持)
时间格式ISO 8601 UTC,如 2026-06-30T01:16:08.087Z
错误响应体{"error": {"code": 401, "message": "..."}}

端点总览

端点说明
POST /api/v1/tasks/generations创建生成任务(带参考图时自动按编辑执行)
POST /api/v1/tasks/edits创建图片编辑任务(必须至少一张参考图)
GET /api/v1/tasks列出当前 key 的任务(offset 或游标分页)
GET /api/v1/tasks/{id}查询任务详情(轮询用)
GET /api/v1/tasks/{id}/images/{index}鉴权下载结果图
GET /outputs/<name>结果图公开静态地址(不可猜文件名)
GET /healthz健康检查(无鉴权)
GET /metricsPrometheus 指标(无鉴权)
结果文件默认保留 24 小时后清理,拿到结果请及时转存,详见「获取结果图」。

快速开始

三步:创建任务 → 轮询到终态 → 下载 file_url。创建与轮询必须使用同一个 key(任务按 key 归属隔离)。

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) 轮询直到 status 变为 succeeded / failed
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:
    # 注意:失败时响应里没有 result 键,请勿直接 t["result"]
    print("failed:", t["error_status"], t["error"])

Python · 图生图(纯 JSON)

带参考图的请求发到 generations 也可以:服务端检测到图后自动按编辑(edits)执行。参考图可以直接给 URL,也可以给 base64。

r = requests.post(f"{BASE}/api/v1/tasks/generations", headers=h, json={
    "model": "gpt-image-2",
    "prompt": "给人物加一顶帽子",
    "size": "1024x1024",
    "image_urls": ["https://cdn.example.com/raw.jpg"],   # 或 data:image/...;base64,
})
print(r.json()["endpoint"])   # "edits" —— 带图请求自动按编辑执行

Python · 图生图(multipart 上传本地文件)

# 参考图放任意文件字段(惯用 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)
网络不可靠时建议带 Idempotency-Key 请求头重试创建,同一幂等键不会重复入队,见「通用约定 · 幂等创建」。

鉴权与任务归属

每个 /api/v1/* 请求都要带 key:请求头 X-API-Key: <key>,或等价的 Authorization: Bearer <key>

key 如何被接受

按服务端部署配置,以下任一条件满足即可通过鉴权:

方式行为
服务 keykey 在服务端白名单(SERVICE_API_KEYS)内。此时 key 只用于鉴权和任务归属,不会转发给上游;任务用服务端配置的默认上游执行。
网关用户 keykey 不在白名单,但服务端配置了网关(SUB2API_BASE_URL)。仅对 /api/v1/tasks* 生效:任务经网关执行,你的 key 作为上游 key 计费。
自带上游请求同时带 X-Upstream-Base-URLX-Upstream-API-Key(或对应 JSON 字段):任务用你自己的上游地址和 key 执行。两者都以请求头提供时可以不带 X-API-Key(任务归属按上游 key 计算);只用 JSON 字段的话仍需带 X-API-Key。详见「创建生成任务 · 自带上游」。

都不满足时返回 401(缺 key 或 invalid API key)。开发模式(服务端未配置白名单)下任何非空 key 都被接受;若服务端没有配置自己的上游 key,你的 key 会被转发给上游做鉴权。

任务归属

  • 任务按 sha256(key) 分组:同一个 key 在任何设备/工具上都能看到同一份任务列表,换 key 查询别人的任务返回 404
  • 明文 key 不会出现在任何响应里。

key 的存储安全

需要转发给上游的 key 会加密后随任务临时入库(供异步 worker 调上游),任务进入终态(succeeded / failed)后立即从库中清除;重试等待期间会保留到终态为止。任务成功后,请求体缓存和落盘的参考图也会一并删除。

失败时的错误信息

上游返回错误时,任务 error 字段写入的是脱敏后的概括语(如 upstream authentication failed),不是上游原始响应;上游 HTTP 状态码写进 error_status。上游返回 401/403 时不重试、直接判失败。完整对照表见「数据结构」。

管理端点

/api/v1/upstreams*(上游 provider 管理)在服务端配置了管理 key(ADMIN_API_KEY)后仅接受该 key,普通业务 key 访问返回 401。普通 API 使用者无需关心。

通用约定

接口(/api/v1/*)的错误响应统一为 {"error": {"code": <数字或稳定错误标识>, "message": "..."}};分辨率校验会返回 resolution_size_mismatch / unsupported_model_resolution,其余常规错误通常使用 HTTP 状态码;静态文件路径(/outputs / /inputs)的 404 为纯文本。

状态码

含义
202任务已创建并入队(幂等命中时返回既有任务,同样 202)
200查询成功
400请求非法:body 为空 / JSON 或 multipart 解析失败 / body 超过大小上限(默认 50MB)/ edits 不带参考图 / 参考图超过 16 张 / statuscursor 查询参数非法 / 自带上游参数不配对
401缺少 key,或 key 未通过校验(含管理端点缺管理 key)
404任务不存在、不属于当前 key、任务 ID 非法(三种情况响应完全一致,不泄露存在性);图片 index 越界同样返回 404(文案不同)
405HTTP 方法不对(如对创建端点发 GET)
410结果文件已被清理(或该结果从未缓存到本地)
422显式分辨率档位低于目标尺寸要求,或没有匹配当前接口、模型和档位的上游路由
429创建任务触发限流,带响应头 Retry-After: 60
500服务端内部错误(统一 internal server error,不泄露细节)
503数据库不可用(/healthz),或管理端点未配置 provider 仓库

限流

创建任务(及同步 /v1/images/* 端点)按调用方 key 限流,默认每 key 每分钟 10 次(滑动窗口,部署可调)。超限返回 429 + Retry-After: 60。查询类接口(列表/详情/下载)不限流。

幂等创建

创建任务时带请求头 Idempotency-Key(或 JSON 字段 idempotency_key,头优先,超过 200 字节截断):同一调用方 key 下重复提交相同幂等键时直接返回既有任务的 {id, status, endpoint, created_at}(仍是 202,status 可能已是 running/succeeded/failed),不会重复入队。该字段不会转发给上游。

任务生命周期

pendingrunningsucceeded / failed

状态说明

状态含义
pending已入队,等待 worker 认领;重试退避等待期间也会回到 pending
running某个 worker 正持有任务并调用上游
succeeded生成成功,result 含结果图
failed重试耗尽或遇到不可重试错误,error / error_status 写明原因
状态可能在 pending ↔ running 之间往返(每次重试一轮);running 超过 30 分钟未完成(如进程崩溃)会被自动回收为 pending。终态(succeeded / failed)之后不再变化,轮询到终态即可停止。建议轮询间隔 1~3 秒。

重试策略

默认行为
次数最多重试 3 次(共 4 次尝试,部署可调)
退避指数退避:2s 起每次翻倍,上限 30s
不重试上游 401 / 403(鉴权失败)、请求超时(默认 300s)→ 直接 failed
总是重试上游 429、5xx、网络错误
其余 4xx由部署配置决定(默认重试)

创建生成任务

POST /api/v1/tasks/generations
POST /api/v1/tasks/edits ?stream=1 可选

两个端点都同时接受 application/jsonmultipart/form-data(按 Content-Type 自动识别)。带参考图的请求一律按编辑执行、发往上游 /images/edits——发给 generations 也会自动转;反之 edits 不带图直接 400。所以通常只需记住:有图 = 编辑,无图 = 文生图

请求体不是原样透传:服务端会归一化后以 JSON 转发上游——统一传图字段、换算 size、在 prompt 末尾追加尺寸标注(见下)。最终发给上游的内容可在任务详情的 forwarded_request(面板里的「实际上传参数」)中查看。

请求字段(JSON)

字段类型说明
modelstring模型名,如 gpt-image-2
promptstring提示词(必填)
sizestring宽x高(如 1024x1024),或比例写法(如 16:9,配合 resolution 换算)
resolutionstring可选档位 1k/2k/4k:size 为比例时按档位换算成具体宽高(16 像素对齐,基数 1024/2048/2880)后删除,不发给上游
quality / output_format / n / background …原样透传,按上游文档为准
streambooltrue 时 worker 以 Accept: text/event-stream 请求上游(对调用方仍是异步轮询);edits 也可用 ?stream=1
image_urls / reference_images / imagesarray参考图,三种字段名等价,见下节
idempotency_keystring幂等键(也可用请求头),不转发上游
upstream_base_url / upstream_api_keystring自带上游,见下节,不转发上游
分辨率分流:精确尺寸按最长边归档,最长边不超过 1536 为 1k,1537~2560 为 2k,2561~6144 为 4k,超过 6144 直接拒绝。方向不影响档位,所以 1024x15361536x1024 都是 1k,2048x1024 是 2k,2880x2880 是 4k。显式 resolution 可以高于尺寸的最低档位,但不能低于它。
部署方一旦创建任意上游路由,托管上游将启用严格匹配:仅在 endpoint + model + resolution_tier 完全匹配时转发,并按该上游路由改写 model。没有匹配项返回 422 unsupported_model_resolution,不会自动降档。
主备顺序:匹配路由按 route.priority 从高到低执行;相同时才比较上游的默认状态和上游优先级。建议 cjy 的 1k/2k 路由设为 100、goeasy 设为 50;goeasy 的 4k 路由设为 100、cjy 设为 50。两个上游在同一档位都映射到对应的 gpt-image-2-1k / -2k / -4k。主路由出现可重试错误时会切到同档位备用路由,不会降档。
size 为 宽x高 时,服务端会在 prompt 末尾自动追加一行「【以下是这张图的参数,宽x高,横图/竖图/方图】」(已有该标记则不重复),帮助上游按目标比例出图。

参考图规则

  • JSON:image_urls / reference_images / images:[{"image_url":...}] 三种写法等价;元素可为 http(s) URL(直接透传上游)、data:image/...;base64,裸 base64
  • 裸 base64 有门槛:长度 ≥ 256 字符且解码后 ≥ 64 字节,否则该元素被静默忽略
  • multipart:所有文件字段都会被当作参考图收集(字段名不限,惯用 image[]);按文件内容嗅探格式,仅接受 png / jpeg / webp / gif。
  • 单任务上限 16 张,超出返回 400
  • base64 / 上传文件会落盘为 /inputs 公开 URL 再发上游,最终统一改写为 images:[{"image_url":"完整URL"}]。服务本身需以公网地址可达(或部署方配置 PUBLIC_INPUT_BASE_URL),上游才拉得到图。

自带上游

可为单个任务指定自己的上游(不走服务端默认上游/网关):

  • 请求头 X-Upstream-Base-URL 或 JSON 字段 upstream_base_url:上游地址。会被规范化——去掉 query/fragment 和结尾的 /images/generations|edits,路径为空时自动补 /v1
  • 请求头 X-Upstream-API-Key 或 JSON 字段 upstream_api_key:上游 key。地址与 key 需配对提供:只给 key 不给地址 → 400;只给地址不给 key,在配置了服务白名单的部署下也 → 400(开发模式下会回落用调用方 key)。
  • 生产环境拒绝解析到内网/回环地址的上游。

请求示例

文生图(JSON)

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"
  }'

图生图(JSON,自动转 edits)

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": "给人物加一顶帽子",
    "size": "16:9",
    "resolution": "2k",
    "image_urls": ["https://cdn.example.com/raw.jpg"]
  }'

图片编辑(multipart 上传本地文件)

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": "c69550f6-f94f-4e3d-89dc-7cbf5ca429fb",
  "status": "pending",
  "endpoint": "generations",
  "created_at": "2026-06-30T01:16:08.087Z"
}

只有这四个字段(精简摘要,完整字段需再查详情)。endpoint 反映任务实际执行方式:带参考图时为 "edits",即便你调的是 generations 端点。

查询任务

列出任务

GET /api/v1/tasks

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

查询参数默认说明
status过滤:pending/running/succeeded/failed,其他值 400
limit201~100;数字越界时回落为 20 但响应回显原始值;非数字直接按 20 处理并回显 20
offset0分页偏移
cursor出现该参数即切换为游标分页,见下

offset 模式响应

{
  "items": [ { /* TaskOut,见「数据结构」 */ } ],
  "total": 42,
  "limit": 20,
  "offset": 0
}

游标模式(推荐深分页)

首页传空值 ?cursor=,后续页传上一次响应的 next_cursor(不透明字符串)。响应不含 total/offsetnext_cursor 为空字符串表示没有更多数据。cursor 非法返回 400

{
  "items": [ ... ],
  "limit": 20,
  "next_cursor": "eyJjIjoi…"
}

任务详情

GET /api/v1/tasks/{id}

轮询此端点直到 status 为终态。任务不存在、不属于当前 key、或 ID 非法 → 统一 404

响应 200(succeeded 示例)

{
  "id": "c69550f6-f94f-4e3d-89dc-7cbf5ca429fb",
  "endpoint": "edits",
  "status": "succeeded",
  "retry_count": 0,
  "request_summary": { "endpoint": "edits", "model": "gpt-image-2", "size": "1024x1024",
                       "prompt_chars": 42, "input_images": 1, "stream": false },
  "forwarded_request": { /* 实际发给上游的 JSON(含完整 prompt 与托管图 URL) */ },
  "result": [ { "file_url": "https://api.你的域名.com/outputs/3f9a…e7b2.png", "source": "b64_json" } ],
  "upstream_response": { /* 脱敏后的上游响应快照(base64 替换为占位符) */ },
  "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:12.112Z",
  "updated_at": "2026-06-30T01:16:12.112Z"
}

failed 示例

{
  "id": "…", "status": "failed", "retry_count": 3,
  "error": "upstream authentication failed",
  "error_status": 401,
  ...
}
任务未成功时响应里没有 result(整体省略,不是 null),upstream_response 同理;request_summaryforwarded_request 创建时即写入,各状态下都会返回。取值请用 t.get("result") 之类的安全写法。error / error_status 两个键则始终存在。

获取结果图

鉴权下载

GET /api/v1/tasks/{id}/images/{index} ?download=1 可选

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

情形返回
index 非数字 / 为负 / 越界,或任务无结果404
文件已被 TTL 清理,或该结果从未缓存到本地410

公开静态地址

GET /outputs/<filename>

result[].file_url 指向的地址,无需鉴权,可直接 <img src> 加载。文件名是加密随机的 32 位十六进制串(128 位熵),作为不可猜测的能力令牌。

  • 只能精准 URL 访问:请求目录、以 / 结尾的路径或不存在的文件一律 404,没有目录列举。
  • /inputs/<name>(参考图托管地址)规则相同。

文件生命周期

结果文件默认保留 24 小时(部署可调),后台每 15 分钟清理过期文件:过期后 /outputs URL 返回 404,鉴权下载端点返回 410请及时转存需要长期保留的图。另外任务成功后,上传的参考图(/inputs 文件)会被立即删除。

特殊情况:若上游直接返回图床 URL 且服务端未回源缓存,file_url 会是上游图床的外部地址(不指向本服务),其有效期由上游决定。

健康检查与监控

健康检查

GET /healthz

无需鉴权。数据库不可用时返回 503statusdegradeddbdown)。

{ "status": "ok", "db": "ok", "queue_pending": 0, "version": "go-rewrite" }

Prometheus 指标

GET /metrics

无需鉴权,text/plain Prometheus 文本格式。

指标说明
imagegen_queue_pending待处理任务数
imagegen_workers_configured配置的 worker 并发数
imagegen_tasks_total{status="…"}各状态任务总数(pending/running/succeeded/failed)

数据结构

TaskOut

字段类型说明
iduuid任务 ID
endpointstringgenerations / edits(带图请求自动为 edits)
statusstringpending / running / succeeded / failed
retry_countint已重试次数
request_summaryobject请求摘要,见下;空时整个键省略
forwarded_requestobject归一化后实际发给上游的请求体(含完整 prompt、托管图 URL;已剔除 upstream_base_url / upstream_api_key / idempotency_key);空时省略
resultarray结果项数组,见下;未成功时整个键省略(不是 null)
upstream_responseobject脱敏后的上游响应快照:base64 数据替换为 <base64 N chars> 占位符;SSE 流式时为 {"stream":true,"images":N};未成功时省略
errorstring|null失败原因(服务端归一化文案,见下表);键始终存在
error_statusint|null失败时的上游 HTTP 状态码(网络错误/超时为 null);键始终存在
created_at / started_at / finished_at / updated_atstring|nullISO8601 时间戳
响应中不会出现 key、owner、服务端本地路径等敏感字段。注意 forwarded_request 含你的完整提示词(以及服务端自动追加的尺寸标注行)。

result 项

字段类型说明
file_urlstring结果图完整 URL。通常指向本服务 /outputs/<32位hex>.png(按访问域名自动拼全);上游直接回图床 URL 且未回源缓存时为上游外部地址
revised_promptstring?上游改写/扩写后的提示词(若上游返回)
sourcestring?结果来源形态:url / b64_json / data_uri

request_summary

字段说明
endpoint实际执行端点(generations / edits)
model / size / resolution_tier / n / response_format按请求出现;size 为换算后的最终值,resolution_tier 为后端解析出的路由档位(resolution 不会转发上游)
prompt_chars提示词字符数(Unicode 计数),不含明文 prompt
input_images参考图数量(恒出现)
stream是否要求上游走 SSE(恒出现)

error 文案对照

上游情形error 文案
401 / 403upstream authentication failed
404upstream endpoint not found; check upstream Base URL…
429upstream rate limit exceeded
超时upstream request timed out; increase UPSTREAM_TIMEOUT…
200 但无图upstream returned no images
无匹配路由no enabled upstream route supports endpoint ..., model ..., resolution ...,任务的 error_status 为 422
其他upstream request failed