生图脚本示例
这一篇给自己写脚本批量生图的用户。下面的请求格式、参数和结果处理,和 生图工作台 实际发给 HRouter 的完全一样:工作台能出的图,照着写的脚本也能出。各分组能用哪些模型,先看 生图与生视频。
示例都从环境变量读取 Key:
export HROUTER_API_KEY="sk-xxxxx"
工作台的调用流程
每生成一张图,工作台按这个顺序做:
- 查模型:
GET /v1/models,从返回里挑出生图模型(gpt-image-*或名字里带image的gemini-*)。 - 查参数能力(GPT 模型):
GET /v1/images/capabilities。返回"native_params": true表示这个 Key 的分组全部走 OpenAI 官方通道,可以使用尺寸、质量、背景等官方参数;返回false时只用比例写法。 - 发生图请求:没有参考图用
/v1/images/generations(JSON),有参考图用/v1/images/edits(multipart 表单);Gemini 用generateContent。每次n为 1。 - 取图片:GPT 读
data[0].b64_json,Gemini 读candidates[].content.parts[].inlineData.data,Base64 解码后按真实格式保存。 - 失败不自动重试:生图按次计费,接口不会按
Idempotency-Key去重,重试可能重复扣费。确认没出图再手动重发。
curl https://hrouter.net/v1/images/capabilities -H "Authorization: Bearer $HROUTER_API_KEY"
# {"object":"images.capabilities","native_params":true}
GPT:官方参数写法(native_params 为 true)
和 OpenAI 官方控制台一样直接给参数,提示词原样发送:
| 参数 | 可选值 | 说明 |
|---|---|---|
size |
auto、1024x1024、1536x1024、1024x1536;gpt-image-2 系列另有 2048x2048、2048x1152、1152x2048、3840x2160、2160x3840 或自定义 |
自定义宽高须是 16 的倍数,最长边不超过 3840,长宽比不超过 3:1,总像素在 655,360 到 8,294,400 之间 |
quality |
auto、low、medium、high |
质量越高越慢 |
background |
auto、opaque、transparent |
透明背景不能配 jpeg |
output_format |
png、jpeg、webp |
决定保存的文件格式 |
output_compression |
0–100 | 只对 jpeg、webp 生效 |
moderation |
auto、low |
内容审核强度 |
尺寸越大单价越高,档位和价格见 模型广场。4K PNG 解码后可以超过 30 MB,脚本的超时至少留 4 分钟。
GPT:比例写法(native_params 为 false)
非官方通道的分组不保证认这些参数,工作台的做法是:size 固定填 auto,在提示词最前面加一段比例要求,参数只保留 model、prompt、n、size、response_format。比例要求的原文:
【画面比例要求】请生成 16:9 横幅构图,画面宽度大于高度,不要竖幅构图。请优先遵循此比例要求,包括使用参考图或重绘时。
<你的提示词>
正方形写「正方形构图,画面宽度等于高度,不要横幅或竖幅构图」,竖图写「竖幅构图,画面高度大于宽度,不要横幅构图」。这是给模型的要求,实际尺寸以返回的图片为准。
完整 Python 脚本
需要 pip install requests。脚本自动判断官方参数能力,支持参考图,结果按真实格式保存:
import base64, os, sys, requests
BASE = "https://hrouter.net"
KEY = os.environ["HROUTER_API_KEY"]
AUTH = {"Authorization": f"Bearer {KEY}"}
def ratio_prompt(ratio: str, prompt: str) -> str:
w, h = map(int, ratio.split(":"))
if w == h:
shape = "正方形构图,画面宽度等于高度,不要横幅或竖幅构图"
elif w > h:
shape = "横幅构图,画面宽度大于高度,不要竖幅构图"
else:
shape = "竖幅构图,画面高度大于宽度,不要横幅构图"
return f"【画面比例要求】请生成 {ratio} {shape}。请优先遵循此比例要求,包括使用参考图或重绘时。\n\n{prompt}"
def native_params() -> bool:
r = requests.get(f"{BASE}/v1/images/capabilities", headers=AUTH, timeout=10)
return r.ok and r.json().get("native_params") is True
def gpt_image(prompt, model="gpt-image-2", ratio="1:1", references=(), **options):
"""options 仅在官方通道生效:size、quality、background、output_format、output_compression、moderation"""
native = native_params()
fields = {"model": model, "n": 1, "response_format": "b64_json"}
if native:
fields.update({"prompt": prompt, "size": options.pop("size", "auto"), **options})
else:
fields.update({"prompt": ratio_prompt(ratio, prompt), "size": "auto"})
if references:
files = [("image[]", (os.path.basename(p), open(p, "rb"))) for p in references]
data = {k: str(v) for k, v in fields.items()}
r = requests.post(f"{BASE}/v1/images/edits", headers=AUTH, data=data, files=files, timeout=300)
else:
r = requests.post(f"{BASE}/v1/images/generations", headers=AUTH, json=fields, timeout=300)
if not r.ok:
sys.exit(f"生成失败 {r.status_code}: {r.text[:500]}(未自动重试)")
return base64.b64decode(r.json()["data"][0]["b64_json"])
def save(image: bytes, name: str) -> str:
ext = "png" if image[:4] == b"\x89PNG" else "jpg" if image[:2] == b"\xff\xd8" else "webp"
path = f"{name}.{ext}"
with open(path, "wb") as f:
f.write(image)
return path
if __name__ == "__main__":
# 官方通道:直接给 4K 尺寸和高质量;非官方通道会自动退回比例写法
img = gpt_image("雪山日出,电影感光影", ratio="16:9",
size="3840x2160", quality="high", output_format="png")
print("已保存", save(img, "snow-mountain"))
# 参考图改图(最多 4 张,单张 ≤ 8 MB,合计 ≤ 16 MB)
# img = gpt_image("把背景换成海边", references=["input.png"])
Gemini 生图
Gemini 分组的高清模型(gemini-3.1-flash-image、gemini-3-pro-image*)支持 1K、2K、4K,比例可选 1:1、3:2、2:3、16:9、9:16;其他 Gemini 生图模型只支持默认分辨率和 1:1、3:2、2:3。参考图直接放进 parts:
import base64, os, requests
KEY = os.environ["HROUTER_API_KEY"]
def gemini_image(prompt, model="gemini-3.1-flash-image", ratio="16:9", size="4K", references=()):
parts = [{"text": prompt}]
for path in references:
mime = "image/png" if path.endswith(".png") else "image/jpeg"
with open(path, "rb") as f:
parts.append({"inlineData": {"mimeType": mime, "data": base64.b64encode(f.read()).decode()}})
body = {
"contents": [{"role": "user", "parts": parts}],
"generationConfig": {
"responseModalities": ["TEXT", "IMAGE"],
"imageConfig": {"aspectRatio": ratio, "imageSize": size},
},
}
r = requests.post(f"https://hrouter.net/v1beta/models/{model}:generateContent",
headers={"x-goog-api-key": KEY}, json=body, timeout=300)
r.raise_for_status()
for candidate in r.json().get("candidates", []):
for part in candidate.get("content", {}).get("parts", []):
image = part.get("inlineData") or part.get("inline_data")
if image:
return base64.b64decode(image["data"])
raise SystemExit("模型没有返回图片,可能被安全策略拒绝,请调整提示词")
with open("mountain.png", "wb") as f:
f.write(gemini_image("雪山日出,电影感光影"))
Node.js(GPT)
Node 18 以上自带 fetch,不需要装依赖:
import { writeFile } from 'node:fs/promises'
const BASE = 'https://hrouter.net'
const auth = { Authorization: `Bearer ${process.env.HROUTER_API_KEY}` }
const caps = await fetch(`${BASE}/v1/images/capabilities`, { headers: auth }).then((r) => (r.ok ? r.json() : {}))
const body = caps.native_params
? { model: 'gpt-image-2', prompt: '雪山日出,电影感光影', n: 1, size: '3840x2160', quality: 'high', output_format: 'png' }
: { model: 'gpt-image-2', prompt: '【画面比例要求】请生成 16:9 横幅构图,画面宽度大于高度,不要竖幅构图。请优先遵循此比例要求,包括使用参考图或重绘时。\n\n雪山日出,电影感光影', n: 1, size: 'auto', response_format: 'b64_json' }
const res = await fetch(`${BASE}/v1/images/generations`, {
method: 'POST',
headers: { ...auth, 'Content-Type': 'application/json' },
body: JSON.stringify(body),
signal: AbortSignal.timeout(300_000),
})
if (!res.ok) throw new Error(`生成失败 ${res.status}: ${(await res.text()).slice(0, 500)}(未自动重试)`)
const { data } = await res.json()
await writeFile('snow-mountain.png', Buffer.from(data[0].b64_json, 'base64'))
常见问题
| 现象 | 原因 | 处理 |
|---|---|---|
400 提示参数不支持(如 Unknown parameter) |
分组不是官方通道,却传了官方参数 | 先调 /v1/images/capabilities,为 false 时改用比例写法 |
400 尺寸被拒 |
自定义尺寸不符合上面的规则 | 宽高取 16 的倍数,最长边 ≤ 3840,长宽比 ≤ 3:1 |
| 请求超时但扣了费 | 4K 出图慢,客户端先断开了 | 超时设到 4–5 分钟;先到 使用记录 确认,再决定是否重发 |
403 / 404 |
Key 所在分组不支持生图或该模型 | 见 生图与生视频 |
429 / 余额不足 |
并发或额度不够 | 见 429 限流与额度、余额不足 |