__BASE_URL__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,会返回同一个任务:
- 原任务仍在排队或处理中:
202 - 原任务已经结束:
200 - key 相同但请求内容不同:
409
不传 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 | 排队耗时 |
<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 累计提交任务总数,不重置 |
- 只有提交任务消耗分钟限流、每日配额和终身配额;查询、下载、取消不计入。
- 终身配额按累计提交的任务数计算,失败或取消的任务也计入;旧任务被清理后计数不回退。
- 终身配额用完后不会彻底停用:该 token 每天最多提交 3 个任务、每小时最多 1 个(按 UTC 自然日/整点小时统计,失败或取消也计入;当天/当小时已达上限时不再放行)。超过时返回
429,detail为lifetime quota exceeded,并带Retry-After。该放行策略固定,不能按 token 配置。 - 幂等重试会消耗一次分钟请求,但不会增加每日或终身任务数。
- 每日配额按 UTC 00:00 重置;终身配额不重置。
- 并发任务上限默认 8 个;队列满时提交返回
429。 - 服务未就绪时提交返回
503,可稍后重试。
| 响应头 | 说明 |
|---|---|
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。服务不会转码上传的图片;需要固定尺寸时请在
客户端自行后处理。
- 输入图片在结果写入后删除。
- 已完成任务和结果默认保留 24 小时;过期后任务查询返回
404,结果文件提前缺失时下载返回410。 - 公开响应、图片元数据和界面不会暴露内部模型或运行时信息。
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 是两套凭证。
认证
管理员令牌由运营方在服务端配置,公开文档不包含任何真实令牌。 推荐使用 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
查询参数:
| 参数 | 说明 |
|---|---|
status | queued、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"
管理接口错误
| 状态码 | 常见原因 |
|---|---|
400 | JSON 请求体格式错误 |
401 | 管理员令牌缺失或无效 |
404 | 令牌或任务不存在 |
409 | 任务正在处理,不能取消 |
422 | 字段缺失、类型错误或超出范围 |
429 | 同一 IP 管理员认证失败过多 |
503 | 服务未配置管理员令牌 |