图片合成 API
把 1~3 张参考图与一段 prompt 组合,让模型综合生成一张新图。常见用法:把人物 A 放进场景 B、把多个产品组合在同一画面、风格迁移合成等。
想做单张图编辑? 用 图片编辑 API — 单图换装、场景替换、变体等。
纯文生图? 用 图片生成 API。
适用场景对比
| 场景 | 推荐端点 | 协议 |
|---|---|---|
| 纯文生图 | /v1/images/generations | JSON |
| 单图编辑 | /v1/images/edits | JSON + image_key 或 generation_id |
| 多图合成 | /v1/images/compose | JSON + image_keys[] / generation_ids[] / image_urls[](1~3 张) |
请求
POST /v1/images/compose
请求头
Authorization: Bearer pk_live_xxxxxxxxxxxxxxxx
Content-Type: application/json
也支持 x-api-key 头(Anthropic SDK 风格)。
请求体
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
model | string | ✅ | 模型名称,如 gpt-image-2 |
prompt | string | ✅ | 文本指令,最长 32000 字符 |
image_keys | string[] | ⚠️ | 通过 文件上传 API 得到的 R2 key 列表 — 与 generation_ids / image_urls 至少三选一 |
generation_ids | string[] | ⚠️ | 之前通过 /v1/images/{generations,edits,compose} 生成的图片 UUID 列表 — 与 image_keys / image_urls 至少三选一 |
image_urls | string[] | ⚠️ | 内联图片列表 — 每项是 https://... URL 或 data:image/(jpeg|png|gif|webp);base64,<数据> 形式的 data URI;无需先调用文件上传。与 image_keys / generation_ids 至少三选一 |
n | integer | ❌ | 生成数量,默认 1,上限由模型配置决定 |
size | string | ❌ | 图片尺寸,与 图片生成 API 一致 |
参考图总数:image_keys.length + generation_ids.length + image_urls.length 必须在 1~3 之间。三个数组可以混用。同一张图(包括跨数组)重复引用会被拒绝。
image_urls 安全限制:
https://URL 必须使用 HTTPS,Picklyone 会拒绝私网 / loopback / link-local / CGN 等内部地址,拒绝跳转、拒绝携带凭据(user:pass@host形式),响应 Content-Type 必须是image/(jpeg\|png\|gif\|webp),30 秒超时,单图最大 25MB。data:URI 必须是data:image/(jpeg\|png\|gif\|webp);base64,...的合法 base64,字符串长度上限 35MB,解码后字节上限 25MB,Picklyone 会校验文件头魔数是否与声明的 MIME 一致。- 内联图片直接走 Workers 内存,不会写入 R2、不会消耗你的 200MB 文件配额、也不会出现在
/api/images/history。
完整示例
引用之前生成的两张图做合成:
{
"model": "gpt-image-2",
"prompt": "把图1的人物放进图2的场景里,保留服装,光照统一",
"generation_ids": [
"00000000-0000-0000-0000-000000000001",
"00000000-0000-0000-0000-000000000002"
],
"n": 1
}
混用上传图 + 生成图:
{
"model": "gpt-image-2",
"prompt": "把这个 logo 印在产品上,在白色木桌背景前展示",
"image_keys": ["files/<userId>/<key>.png"],
"generation_ids": ["<uuid>"],
"n": 1
}
直接传 base64 内联图片(不需要预先上传):
{
"model": "gpt-image-2",
"prompt": "把图1的人物放进图2的场景里,保留服装,光照统一",
"image_urls": [
"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQ...",
"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQ..."
],
"n": 1
}
或者传公网 HTTPS URL:
{
"model": "gpt-image-2",
"prompt": "把人物放进这个场景",
"image_urls": [
"https://example.com/portrait.jpg",
"https://example.com/scene.jpg"
],
"n": 1
}
响应
{
"created": 1776864985,
"data": [
{
"b64_json": "iVBORw0KGgo...",
"revised_prompt": "..."
}
],
"picklyone_generation_ids": ["00000000-0000-0000-0000-000000000099"]
}
| 字段 | 类型 | 说明 |
|---|---|---|
data[i].b64_json | string | base64 编码的 PNG(无 data: 前缀,可直接 base64 解码) |
data[i].revised_prompt | string | null | 模型重写后的 prompt(仅供调试,可能为 null) |
picklyone_generation_ids[i] | string | 本次生成图的 UUID,可作为下一次 /v1/images/{compose,edits} 调用的 generation_id 引用做链式合成 |
计费
按次计费,与 图片生成 API 一致:actualCount × price_per_image。预扣按请求的 n 冻结,结算按实际返回数量收取,差额自动退回。
Python 示例
import requests, base64
API_KEY = "pk_live_xxxxxxxxxxxxxxxx"
BASE = "https://api.picklyone.com"
resp = requests.post(
f"{BASE}/v1/images/compose",
headers={
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
},
json={
"model": "gpt-image-2",
"prompt": "把图1的人物放进图2的场景里",
"generation_ids": [
"00000000-0000-0000-0000-000000000001",
"00000000-0000-0000-0000-000000000002",
],
"n": 1,
},
timeout=300,
).json()
for i, item in enumerate(resp.get("data", [])):
if item.get("b64_json"):
with open(f"out_{i}.png", "wb") as f:
f.write(base64.b64decode(item["b64_json"]))
print(f"saved out_{i}.png — generation_id: {resp['picklyone_generation_ids'][i]}")
cURL 示例
curl https://api.picklyone.com/v1/images/compose \
-H "Authorization: Bearer pk_live_xxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-image-2",
"prompt": "把图1的人物放进图2的场景里",
"generation_ids": [
"00000000-0000-0000-0000-000000000001",
"00000000-0000-0000-0000-000000000002"
]
}'
Node.js 示例
import fs from "fs";
const API_KEY = "pk_live_xxxxxxxxxxxxxxxx";
const BASE = "https://api.picklyone.com";
const resp = await fetch(`${BASE}/v1/images/compose`, {
method: "POST",
headers: {
Authorization: `Bearer ${API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "gpt-image-2",
prompt: "...",
image_keys: ["files/<userId>/<key>.png"],
generation_ids: ["<uuid>"],
n: 1,
}),
}).then(r => r.json());
for (const [i, item] of resp.data.entries()) {
if (item.b64_json) {
fs.writeFileSync(`out_${i}.png`, Buffer.from(item.b64_json, "base64"));
}
}
限制
| 项 | 限制 |
|---|---|
| 请求 body 最大 | 100 MB(为内联 image_urls 留空间;若只用 image_keys/generation_ids 实际不会用到) |
| 单张参考图最大 | 25 MB(R2 来源 + 内联同口径) |
单条 image_urls 数据 URI 字符串 | 35 MB(对应解码后约 25 MB) |
单条 image_urls HTTPS URL | 2048 字符 |
| 参考图合计最大 | 30 MB(R2 + 内联累计) |
| 参考图总数 | 1~3(image_keys + generation_ids + image_urls 合计) |
| 一次请求图片数 | 由模型 max_n_per_request 决定(n 超过会 400 invalid_request) |
| 响应超时 | 通常 30-90 秒 |
| 速率限制 | 30 请求/分钟/用户(与其他图片端点共享) |
错误码
| HTTP | code | 说明 |
|---|---|---|
| 400 | invalid_request | 字段格式错 / n 超出 / size 格式错 |
| 400 | invalid_source | 参考图 0 张或超过 3 张,或者三个数组都没传 |
| 400 | invalid_image_key | image_key 格式不对或不属于调用方 |
| 400 | invalid_generation_id | generation_id 不是合法 UUID |
| 400 | invalid_url | image_urls 条目不是 https:// 或 data:image/...,或 URL 长度超过 2048 字符 |
| 400 | invalid_data_uri | image_urls 中的 data URI 格式不合法(MIME 不在白名单内,或 base64 主体缺失) |
| 400 | image_too_large | image_urls 数据 URI 字符串超过 35MB,或解码后超过 25MB |
| 400 | MAGIC_MISMATCH | image_urls 的实际文件头与声明的 MIME 不一致(防止 MIME 伪造) |
| 400 | IMAGE_TYPE_UNSUPPORTED | image_urls 远端响应的 Content-Type 不在 image/(jpeg|png|gif|webp) 白名单中 |
| 400 | NON_HTTPS / BLOCKED_HOST / PRIVATE_IP / REDIRECT_BLOCKED / NOT_IMAGE / TOO_LARGE / UPSTREAM_ERROR | image_urls 远端 URL SSRF/拉取检查失败(详见上方安全限制) |
| 400 | IMAGES_TOO_LARGE_TOTAL | 三种来源累加超过 30MB |
| 400 | duplicate_reference | 同一张图被引用了多次(image_keys 内、generation_ids 内、image_urls 内,或跨 image_keys 与 generation_ids) |
| 400 | content_policy_violation | prompt 触发内容审查(已退预扣) |
| 400 | no_image_generated | 模型对模糊 prompt 无图返回(已退预扣) |
| 401 | invalid_api_key | API Key 无效 |
| 402 | insufficient_balance | 余额不足 |
| 404 | model_not_found | 模型不存在或未激活 |
| 404 | generation_not_found | generation_ids 中某个 UUID 不存在或不属于调用方 |
| 408 | upstream_timeout | 请求超时(已退预扣) |
| 409 | generation_not_ready | 引用的 generation_id 还在生成中 — 稍后重试 |
| 413 | image_too_large | 单条 data URI 字符串超过 35MB(请求被早拒,未计费) |
| 422 | generation_not_ready | 引用的 generation_id 是失败的 generation,无法用作参考 |
| 429 | rate_limit_exceeded | 速率限制 |
| 502 | upstream_error | 服务异常(已退预扣) |
注意事项
- 响应中的
b64_json是裸 base64(不带data:image/png;base64,前缀),可以直接Buffer.from(b64, "base64")或 Pythonbase64.b64decode(b64)解码 - 图片自动入库 — 此端点会把生成的图片自动持久化到 Picklyone 历史记录,可在
/api/images/history查询。响应里的picklyone_generation_ids是入库的 UUID - 链式合成 — 把上一次返回的
picklyone_generation_ids[i]作为下一次调用的generation_id,即可在同一组上下文里逐步迭代