两条路由,都是异步的:
| 路由 | 用途 |
|---|---|
POST /v1/images/generations | 文生图 |
POST /v1/images/edits | 图生图,可带蒙版 |
两者都返回 202 Accepted 加一个 Job 信封,成品从
GET /v1/jobs/{job_id}/assets 取。
`client.images.generate()` 用不了
OpenAI SDK 的这个封装期待同步的 {"data": [{"url": …}]} 响应体,而我们返回的是 Job。这两条路由
请用普通 HTTP 调用。
Permalink to 生成生成
curl https://hypit.ai/v1/images/generations \
-H "Authorization: Bearer $HYPIT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "nano-banana-pro",
"prompt": "一只纸船顺着雨水沟漂走,浅景深",
"n": 1,
"aspect_ratio": "16:9",
"resolution": "1k"
}'{
"id": "job_5a0ce31f78b246d9c0117e42",
"status": "queued",
"kind": "image",
"model": "nano-banana-pro",
"progress": 0,
"created_at": 1787824589,
"dispatch_deadline_at": 1787825189
}Permalink to 请求体请求体
| 字段 | 类型 | 说明 |
|---|---|---|
model | string | 必填 |
prompt | string | 必填 |
n | integer | 1–10,默认 1。每张图对应一个产物序号 |
size | string | 如 "1024x1024",或 "auto" |
resolution | string | 模型公布的输出档位,如 "1k"、"2k"、"4k";与宽高比独立 |
quality | string | 如 low、medium、high、hd、standard、auto |
style | string | 各厂商自定义 |
aspect_ratio | string | 如 "16:9" |
negative_prompt | string | |
background | string | 如 transparent、opaque、auto |
output_format | string | png、jpeg、webp |
response_format | string | url 或 b64_json,其余值返回 400 |
seed | integer | |
watermark | boolean | |
reference_images | array | 见下;images 是兼容别名 |
mask | string 或 object | 见下 |
支持档位的图片模型推荐传 aspect_ratio + resolution。4k 不是固定的 3840x2160,实际像素尺寸由模型和比例决定。旧的 size 像素写法仍可用于支持它的模型;如果同时填写 aspect_ratio,两者必须一致。metadata.resolution 作为兼容别名仍然接受,与顶层 resolution 不一致时返回 400。
网关会在创建任务、冻结额度前检查可用渠道是否支持所选档位和比例;不支持的组合返回明确的参数错误,不会悄悄改用默认值。计价使用同一分辨率档位,具体价格见模型卡片的 pricing.resolution_ratio 或 pricing.media_rates。
`response_format` 不影响你怎么取结果
它会被校验并转发给上游,但 Job 的产物一律通过 assets 接口以签名链接交付,没有任何路径会在响应里 直接返回 base64。
Permalink to 厂商透传厂商透传
上表没有建模的字段会原样转发给上游。这是为了让只在某一家上游存在的功能仍然可达。两条限制:整个
额外字段块在体积和嵌套深度上有上限;任何看起来像媒体 URL 的字符串必须是真实的 http(s) 或
data: URL——从额外字段里夹带的 URL 会被 url_in_extra 拒绝,而不是被悄悄抓取。
Permalink to json-里的参考图JSON 里的参考图
reference_images(以及兼容别名 images)用于在 JSON 请求里附带素材。每一项要么是裸 URL 字符串,要么是一个对象:
{
"model": "nano-banana-pro",
"prompt": "把这只船放进暴风雪里",
"reference_images": [
{ "url": "https://example.com/boat.png" },
{ "b64": "iVBORw0KGgo…", "mime_type": "image/png", "filename": "boat.png" }
]
}mask 接受同样的对象,也接受普通 URL 或 data: URL 字符串。
你提供的每个 URL 都走受保护的抓取器:只允许 http、https 和 data:,拒绝内网与链路本地地址,
并逐跳复核重定向。校验不过就是 400(invalid_media_url、unsupported_media_url_scheme 或
invalid_input_url),不会产生任何扣费。
Permalink to 编辑编辑
POST /v1/images/edits 接受 multipart/form-data 的真实文件分片,也接受上面那种带
reference_images 的 JSON 请求体。至少要有一张源图。
curl https://hypit.ai/v1/images/edits \
-H "Authorization: Bearer $HYPIT_API_KEY" \
-F model=nano-banana-pro \
-F 'prompt=把天空改成阴天' \
-F image=@boat.png \
-F mask=@sky-mask.pngmultipart 字段名:
| 分片 | 类型 | 说明 |
|---|---|---|
image、image[] | 文件 | 一张或多张源图,按发送顺序 |
mask | 文件 | 最多一个 |
model、prompt、n、size、resolution、quality、style、response_format、background、output_format、negative_prompt、aspect_ratio、seed、watermark | 值 | 语义与 JSON 字段一致 |
限制:
- 单个上传文件 25 MiB
- 整个 multipart 请求体 100 MiB
/v1/images/generations的 JSON 请求体 10 MiB- 单个 multipart 标量字段 64 KiB
上传声明的 Content-Type 会被忽略——我们嗅探字节内容,只接受 PNG、JPEG 和 WebP,其余一律
400 unsupported_image_format。
Permalink to 完整流程完整流程
import os, time, httpx
BASE = os.environ.get("HYPIT_BASE_URL", "https://hypit.ai/v1")
AUTH = {"Authorization": f"Bearer {os.environ['HYPIT_API_KEY']}"}
job = httpx.post(
f"{BASE}/images/generations",
headers=AUTH,
json={
"model": "nano-banana-pro",
"prompt": "一只纸船顺着雨水沟漂走",
"n": 1,
"aspect_ratio": "16:9",
"resolution": "1k",
},
timeout=60,
)
job.raise_for_status()
job_id = job.json()["id"]
while True:
state = httpx.get(f"{BASE}/jobs/{job_id}", headers=AUTH, timeout=30).json()
if state["status"] in {"succeeded", "failed", "queue_expired"}:
break
time.sleep(3)
if state["status"] != "succeeded":
raise RuntimeError(f"{state['status']}: {state.get('error_code')} {state.get('error')}")
for a in httpx.get(f"{BASE}/jobs/{job_id}/assets", headers=AUTH, timeout=30).json()["items"]:
data = httpx.get(a["url"], follow_redirects=True, timeout=None).content
open(f"image-{a['ordinal']}.png", "wb").write(data)import { writeFile } from "node:fs/promises";
const BASE = process.env.HYPIT_BASE_URL ?? "https://hypit.ai/v1";
const AUTH = { Authorization: `Bearer ${process.env.HYPIT_API_KEY}` };
const TERMINAL = new Set(["succeeded", "failed", "queue_expired"]);
const created = await fetch(`${BASE}/images/generations`, {
method: "POST",
headers: { ...AUTH, "Content-Type": "application/json" },
body: JSON.stringify({
model: "nano-banana-pro",
prompt: "一只纸船顺着雨水沟漂走",
n: 1,
aspect_ratio: "16:9",
resolution: "1k",
}),
});
if (!created.ok) throw new Error(`${created.status} ${await created.text()}`);
const { id } = await created.json();
let job;
do {
await new Promise((r) => setTimeout(r, 3_000));
job = await (await fetch(`${BASE}/jobs/${id}`, { headers: AUTH })).json();
} while (!TERMINAL.has(job.status));
if (job.status !== "succeeded") throw new Error(`${job.status}: ${job.error_code} ${job.error}`);
const { items } = await (await fetch(`${BASE}/jobs/${id}/assets`, { headers: AUTH })).json();
for (const a of items) {
const bytes = Buffer.from(await (await fetch(a.url)).arrayBuffer());
await writeFile(`image-${a.ordinal}.png`, bytes);
}Permalink to 这两条路由特有的错误这两条路由特有的错误
| 状态 | code | 含义 |
|---|---|---|
400 | missing_model / missing_prompt | 两者都必填 |
400 | invalid_n | n 必须在 1–10 之间 |
400 | invalid_response_format | 不是 url 或 b64_json |
400 | missing_image | /v1/images/edits 没有提供源图 |
400 | invalid_multipart | multipart 请求体无法解析 |
400 | unexpected_file | 出现了 image、image[]、mask 之外的文件分片 |
400 | invalid_mask / empty_mask | mask 既不是 URL 也不是 {b64|url} 对象 |
400 | unsupported_image_format | 字节内容不是 PNG、JPEG 或 WebP |
400 | empty_image / unreadable_image | 文件分片里没有可用内容 |
413 | image_too_large | 单个文件超过 25 MiB |
413 | body_too_large | JSON 超过 10 MiB,或 multipart 超过 100 MiB |
413 | form_field_too_large | 单个标量字段超过 64 KiB |
404 | model_not_found | 没有启用的上游为这个模型提供图片能力 |
任务、计费和限流相关的公共错误见错误与限流。