Image Enhancer

API 调用文档

异步图片增强接口:提交图片得到任务 ID,轮询任务状态,完成后下载结果。 所有 /v1/* 请求都需要 access token。

当前入口__BASE_URL__
机器可读契约/openapi.json
任务是异步的。提交成功后请保存 job_id,用状态接口轮询; 客户端断线不会取消任务。任务只对创建它的 token 可见。

1. 认证

使用管理员在后台创建的 access token,形如 enh_...。 token 只在创建时显示一次,吊销后立即失效。推荐使用标准 Bearer 头:

Authorization: Bearer enh_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

也兼容 X-API-Key: enh_...。缺少、无效或已吊销的 token 返回 401。

不要把 token 放进 URL、图片链接或公开的前端代码。浏览器演示页会把 token 存在本机 localStorage;生产集成建议通过自己的服务端代理调用。

2. 提交任务

POST /v1/enhance,使用 multipart/form-data。 必须且只能上传一个图片文件,字段名为 image。成功返回 202。

export BASE=__BASE_URL__
export TOKEN=enh_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

curl -sS -D - -X POST "$BASE/v1/enhance" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Idempotency-Key: my-request-001" \
  -F "image=@input.jpg" \
  -F "output_format=png" \
  -F "prompt=保持建筑结构,增强细节和材质" \
  -F "seed=42"

请求字段

字段必填说明
image是JPEG、PNG 或 WebP;默认最大 32 MiB,最长边 16384 像素,总像素不超过 1600 万
prompt否附加要求,最多 2000 字符;追加在内置基础提示词之后,建议只写需要强调的内容
output_format否png(默认)或 jpeg
seed否0 到 2^63-1 的整数,用于复现;不传时随机生成

这些字段也可以放在 query string 中,但表单字段更清晰。

成功响应

HTTP/1.1 202 Accepted
Location: /v1/jobs/0a7411f6c8be45758f84e6ac41828f25
X-RateLimit-Limit: 6
X-RateLimit-Remaining: 5
X-RateLimit-Reset: 1793147107
X-Quota-Limit: 20
X-Quota-Remaining: 19
X-Quota-Reset: 1793193600
X-Lifetime-Quota-Limit: 1000
X-Lifetime-Quota-Remaining: 999

{
  "job_id": "0a7411f6c8be45758f84e6ac41828f25",
  "status": "queued",
  "status_url": "/v1/jobs/0a7411f6c8be45758f84e6ac41828f25",
  "result_url": null,
  "created_at": "2026-10-28T00:23:56.468Z",
  "started_at": null,
  "completed_at": null,
  "attempts": 0,
  "queue_position": 1
}

queue_position 只在排队时出现,1 表示下一个被处理。

幂等提交

带 Idempotency-Key 请求头(1–128 字符)时,同一个 token 用同一个 key 重复提交完全相同的图片、提示词、格式和 seed,会返回同一个任务:

不传 seed 时,seed 会由幂等 key 推导,因此重试可以得到相同结果。

3. 查询状态

GET /v1/jobs/{job_id},需要同一个 token。其他 token 一律返回 404。

curl -sS -H "Authorization: Bearer $TOKEN" \
  "$BASE/v1/jobs/0a7411f6c8be45758f84e6ac41828f25"
{
  "job_id": "0a7411f6c8be45758f84e6ac41828f25",
  "status": "processing",
  "status_url": "/v1/jobs/0a7411f6c8be45758f84e6ac41828f25",
  "result_url": null,
  "created_at": "2026-10-28T00:23:56.468Z",
  "started_at": "2026-10-28T00:23:57.360Z",
  "completed_at": null,
  "attempts": 1
}
状态含义
queued已入队,等待处理
processing正在生成,不能取消
completed完成,可以下载结果
failed失败,error 字段给出通用原因
cancelled已取消

完成后会再多三个耗时字段:

{
  "status": "completed",
  "result_url": "/v1/jobs/0a7411f6c8be45758f84e6ac41828f25/result",
  "completed_at": "2026-10-28T00:24:14.525Z",
  "attempts": 1,
  "processing_ms": 17165.1,
  "inference_ms": 15900.2,
  "queue_ms": 891.9
}

建议轮询间隔 1–2 秒,连续失败时退避。正常任务通常在 10–30 秒内完成, 排队繁忙时会更久,请以状态接口为准。

4. 下载结果

GET /v1/jobs/{job_id}/result,需要同一个 token。只有 completed 才能下载;未完成返回 409,结果文件已过期返回 410。

curl -sS -H "Authorization: Bearer $TOKEN" \
  -o enhanced.png \
  "$BASE/v1/jobs/0a7411f6c8be45758f84e6ac41828f25/result"
响应头说明
Content-Disposition建议的下载文件名
X-Enhancer-Seed本次实际使用的 seed
X-Enhancer-Processing-Ms端到端处理耗时
X-Enhancer-Inference-Ms推理耗时
X-Enhancer-Queue-Ms排队耗时
结果需要鉴权,不能把结果 URL 直接放进 <img src> 或分享给未授权的人。 浏览器里应先 fetch 成 blob,再 URL.createObjectURL 显示或下载。

取消任务

DELETE /v1/jobs/{job_id},需要同一个 token。只有排队中的任务能取消; 正在处理返回 409,已经结束的任务原样返回当前状态。

curl -sS -X DELETE -H "Authorization: Bearer $TOKEN" \
  "$BASE/v1/jobs/0a7411f6c8be45758f84e6ac41828f25"

5. 限流与配额

每个 token 有三类限制,默认值见下表(0 表示不限)。 另有按 IP 的突发限流,默认 30 次/分钟。管理员可以在 /admin 为每个 token 单独调整,也可以修改“新令牌默认配额”;修改默认值只影响 之后新建的 token,已有 token 的配额保持不变。

限制默认值说明
每分钟请求6 次按分钟窗口重置
每日任务20 个/天按 UTC 00:00 重置(北京时间 08:00)
终身任务1000 个该 token 累计提交任务总数,不重置
响应头说明
X-RateLimit-Limit当前 token 每分钟上限
X-RateLimit-Remaining当前分钟剩余次数
X-RateLimit-Reset分钟窗口重置时间(Unix 秒)
X-Quota-Limit每日任务配额
X-Quota-Remaining今日剩余任务数
X-Quota-Reset每日配额重置时间(Unix 秒)
X-Lifetime-Quota-Limit终身任务配额;配额为 0 时不返回该头
X-Lifetime-Quota-Remaining终身剩余任务数
Retry-After被限流时建议等待的秒数

429 的 detail 可能是 rate limit exceeded、 daily quota exceeded、lifetime quota exceeded(终身配额用尽, 且用尽后的 3 个/天或 1 个/小时放行额度也已用完)、service queue is full 或 too many authentication failures。

6. 错误码

所有错误都是 JSON:{ "detail": "unsupported image" }

状态码常见原因
400请求体格式错误,或 multipart 解析失败
401缺少 token、token 无效或已吊销
404任务不存在,或不属于当前 token
409任务还在处理、结果未完成,或幂等 key 内容冲突
410结果文件已过期
413上传文件超过 32 MiB,或图片尺寸/像素超限
415不是受支持的图片格式
422缺少图片、上传多张图片,或格式/seed/提示词/幂等 key 不合法
429分钟限流、每日配额、终身配额(含用尽后的 3 个/天、1 个/小时放行额度)、队列已满或认证失败过多
500服务内部错误,可重试
503服务未就绪或容量暂时不可用

7. 输出尺寸与保留策略

输出不是原尺寸,也不是固定 4K。服务按最长边控制输出:默认 1024, 按 16 对齐,范围限制在 256–1536;例如 4:3 输入通常得到 1024x768。服务不会转码上传的图片;需要固定尺寸时请在 客户端自行后处理。

8. 完整示例

Python

import time
import requests

BASE = "__BASE_URL__"
TOKEN = "enh_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
HEADERS = {"Authorization": f"Bearer {TOKEN}"}

with open("input.jpg", "rb") as image:
    response = requests.post(
        f"{BASE}/v1/enhance",
        headers={**HEADERS, "Idempotency-Key": "demo-001"},
        files={"image": ("input.jpg", image, "image/jpeg")},
        data={"output_format": "png", "prompt": "保持建筑结构,增强细节"},
        timeout=60,
    )
response.raise_for_status()
job = response.json()
print("queued", job["job_id"], job.get("queue_position"))

while True:
    status = requests.get(f"{BASE}{job['status_url']}", headers=HEADERS, timeout=30).json()
    if status["status"] in {"completed", "failed", "cancelled"}:
        break
    time.sleep(1.5)

if status["status"] == "completed":
    result = requests.get(f"{BASE}{status['result_url']}", headers=HEADERS, timeout=120)
    result.raise_for_status()
    with open("enhanced.png", "wb") as output:
        output.write(result.content)
else:
    print("job ended:", status["status"], status.get("error"))

Node.js(18+,原生 fetch)

import { readFile, writeFile } from "node:fs/promises";

const BASE = "__BASE_URL__";
const TOKEN = process.env.ENHANCER_ACCESS_TOKEN;

const form = new FormData();
form.set("image", new Blob([await readFile("input.jpg")], { type: "image/jpeg" }), "input.jpg");
form.set("output_format", "png");

const created = await fetch(`${BASE}/v1/enhance`, {
  method: "POST",
  headers: { Authorization: `Bearer ${TOKEN}`, "Idempotency-Key": "demo-001" },
  body: form,
});
if (!created.ok) throw new Error(`${created.status} ${await created.text()}`);
const job = await created.json();

let status;
for (;;) {
  const response = await fetch(`${BASE}${job.status_url}`, {
    headers: { Authorization: `Bearer ${TOKEN}` },
  });
  status = await response.json();
  if (["completed", "failed", "cancelled"].includes(status.status)) break;
  await new Promise((resolve) => setTimeout(resolve, 1500));
}

if (status.status === "completed") {
  const result = await fetch(`${BASE}${status.result_url}`, {
    headers: { Authorization: `Bearer ${TOKEN}` },
  });
  if (!result.ok) throw new Error(`${result.status} ${await result.text()}`);
  await writeFile("enhanced.png", Buffer.from(await result.arrayBuffer()));
}

9. 管理接口(运营方)

管理后台位于 /admin,登录后每 5 秒自动刷新;页头会显示上次刷新时间, 标签页重新可见时也会立即刷新一次。管理 API 位于 /admin/api/*, 使用管理员令牌鉴权,和调用方的 enh_... access token 是两套凭证。

管理员令牌拥有创建/吊销 access token、查看任务、取消任务的全部权限。 它只能保存在服务端或运营方自己的密码管理器中,不要放进浏览器前端、 URL、截图或公开仓库。这里的公开文档只描述接口契约,不包含任何真实令牌。

认证

管理员令牌由运营方在服务端配置,公开文档不包含任何真实令牌。 推荐使用 Bearer 头:

export BASE=__BASE_URL__
export ADMIN_TOKEN=...  # 运营方提供

curl -sS -H "Authorization: Bearer $ADMIN_TOKEN" \
  "$BASE/admin/api/overview"

也接受 X-Admin-Token: ...。缺少或无效令牌返回 401;同一 IP 连续失败会先被限流并返回 429。

接口一览

方法路径作用
GET/admin/api/overview运行状态、令牌与任务概览
GET/admin/api/jobs任务列表与筛选
POST/admin/api/keys创建 access token;明文只返回一次
PATCH/admin/api/keys/{id}修改名称、分钟限流、每日或终身配额
GET/admin/api/settings读取新令牌默认配额
PATCH/admin/api/settings修改新令牌默认配额(只影响新建 token)
DELETE/admin/api/keys/{id}立即吊销 access token
DELETE/admin/api/jobs/{id}取消排队中的任务

GET /admin/api/overview

返回看板概览。status 为 ready 或 degraded;workers[].stale 表示执行端心跳超时; storage_free_bytes 在无法获取存储信息时为 null。

{
  "status": "ready",
  "queue_depth": 0,
  "counts": { "queued": 0, "processing": 0, "completed": 3, "failed": 0, "cancelled": 0 },
  "today": { "queued": 0, "processing": 0, "completed": 0, "failed": 0, "cancelled": 0 },
  "workers": [
    {
      "id": "inference-0",
      "status": "ready",
      "started_at": 1793147037.4,
      "heartbeat_at": 1793150337.3,
      "current_job_id": null,
      "stale": false
    }
  ],
  "keys": [
    {
      "id": "186a921f82c944debdad701393eba4d6",
      "name": "owner",
      "prefix": "enh_xxxxxxxx",
      "created_at": 1793147047.8,
      "last_used_at": 1793147896.3,
      "revoked_at": null,
      "rate_limit_per_minute": 6,
      "daily_quota": 20,
      "lifetime_quota": 1000,
      "requests_total": 2,
      "jobs_total": 0,
      "jobs_today": 0,
      "rate": { "used": 0, "remaining": 6, "reset_at": 1793150401.4 }
    }
  ],
  "storage_free_bytes": null,
  "generated_at": 1793150341.4
}

时间字段除特别说明外都是 Unix 秒;rate 可能额外包含 limit。

GET /admin/api/jobs

查询参数:

参数说明
statusqueued、processing、completed、failed、cancelled
key_id按创建任务的 access token ID 过滤
search按任务 ID 或提示词搜索
limit返回条数,服务端限制在 1–200;省略时使用服务端默认值
offset分页偏移,默认 0
curl -sS -H "Authorization: Bearer $ADMIN_TOKEN" \
  "$BASE/admin/api/jobs?status=failed&limit=50&offset=0"
{
  "jobs": [
    {
      "id": "0a7411f6c8be45758f84e6ac41828f25",
      "status": "completed",
      "created_at": 1793147100.0,
      "prompt": "Upscale this picture to 4K resolution...",
      "output_format": "png",
      "seed": "42",
      "media_type": "image/png",
      "processing_ms": 17165.1,
      "inference_ms": 15900.2,
      "queue_ms": 891.9,
      "attempts": 1,
      "api_key_id": "186a921f82c944debdad701393eba4d6",
      "api_key_name": "owner",
      "api_key_prefix": "enh_xxxxxxxx",
      "error": null
    }
  ],
  "total": 1,
  "limit": 50,
  "offset": 0
}

POST /admin/api/keys

创建 access token。请求字段:

字段必填说明
name是显示名称,最长 80 字符
rate_limit_per_minute否每分钟请求上限,1–600,默认 6
daily_quota否每日任务配额,0–10000;0 表示不限,默认 20
lifetime_quota否终身任务配额,0–1000000;0 表示不限,默认 1000
curl -sS -X POST -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name":"client","rate_limit_per_minute":6,"daily_quota":20,"lifetime_quota":1000}' \
  "$BASE/admin/api/keys"
{
  "id": "186a921f82c944debdad701393eba4d6",
  "name": "client",
  "prefix": "enh_xxxxxxxx",
  "created_at": 1793147047.8,
  "last_used_at": null,
  "revoked_at": null,
  "rate_limit_per_minute": 6,
  "daily_quota": 20,
  "lifetime_quota": 1000,
  "requests_total": 0,
  "jobs_total": 0,
  "token": "enh_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}
token 只在此次响应中出现一次;服务端只保存它的 SHA-256 哈希。 请立即复制保存。其他管理响应不会返回明文 token。

新令牌默认配额

GET /admin/api/settings 返回新建 token 时使用的默认限制; PATCH /admin/api/settings 接受任意组合的 rate_limit_per_minute、daily_quota、 lifetime_quota,至少提供一个,返回更新后的完整默认值。 默认值保存在 D1,只影响之后新建的 token,已有 token 的限额不会被改动。

curl -sS -X PATCH -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"daily_quota":20,"lifetime_quota":1000}' \
  "$BASE/admin/api/settings"
{
  "rate_limit_per_minute": 6,
  "daily_quota": 20,
  "lifetime_quota": 1000
}

PATCH /admin/api/keys/{id}

修改令牌。字段都是可选的,但至少提供一个:name、rate_limit_per_minute、daily_quota、lifetime_quota。返回更新后的令牌对象(不含明文 token)。

curl -sS -X PATCH -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"daily_quota":100}' \
  "$BASE/admin/api/keys/186a921f82c944debdad701393eba4d6"

DELETE /admin/api/keys/{id}

立即吊销令牌。令牌不存在返回 404;已经吊销的令牌再次删除仍返回当前对象。revoked_at 为吊销时间。

curl -sS -X DELETE -H "Authorization: Bearer $ADMIN_TOKEN" \
  "$BASE/admin/api/keys/186a921f82c944debdad701393eba4d6"

DELETE /admin/api/jobs/{id}

只有 queued 任务能取消;正在 processing 的任务返回 409,因为无法安全中断已经在执行的任务。已经结束的任务原样返回当前状态。

curl -sS -X DELETE -H "Authorization: Bearer $ADMIN_TOKEN" \
  "$BASE/admin/api/jobs/0a7411f6c8be45758f84e6ac41828f25"

管理接口错误

状态码常见原因
400JSON 请求体格式错误
401管理员令牌缺失或无效
404令牌或任务不存在
409任务正在处理,不能取消
422字段缺失、类型错误或超出范围
429同一 IP 管理员认证失败过多
503服务未配置管理员令牌