指南
图片生成
异步提交图片生成与编辑任务,轮询状态并下载签名图片链接。
Felo 图片 API 使用 Felo 模型目录中的模型生成和编辑图片。它是异步的:提交请求会立即返回任务 ID,然后轮询任务直到进入终态。
本页说明图片接口的专属约定。通用任务生命周期见异步任务。
开始之前
- API key。见认证。
model_kind为image_generation的模型。见下文“查找图片模型”。- Base URL:
https://openapi.felo.ai。
与 OpenAI Images API 的区别
路径与 OpenAI 的 Images API 相似,但响应不是:
| OpenAI Images API | Felo 图片 API | |
|---|---|---|
| 提交响应 | 200,直接返回图片数据 | 202,返回 task_id、status: "queued" |
| 图片何时可用 | 提交响应里 | 轮询到 succeeded 后 |
| 图片载荷 | b64_json 或 url | 有时效的签名 url |
| 请求错误 | 提交时返回 HTTP 错误 | 通常体现在任务的 status: "failed" |
不要把 openai.images.generate() 指向这个 endpoint。SDK 期待同步的 data 数组,无法轮询任务。请直接通过 HTTP 调用并轮询,或自己封装这两步。
提交任务
curl -X POST "https://openapi.felo.ai/api/v1/images/generations" \
-H "Authorization: Bearer ${FELO_API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"model": "gemini-3-pro-image",
"prompt": "A studio product photo of a ceramic coffee cup on warm linen, soft morning light",
"resolution": "2K",
"aspect_ratio": "4:3"
}'成功提交返回 HTTP 202:
{
"task_id": "6d1f0c9a-...",
"status": "queued",
"kind": "image",
"outputs": []
}202 只表示平台已接收任务,不表示 prompt、参数或参考图有效。上游调用在响应之后才执行,因此大多数请求问题会稍后表现为失败任务。例外是模型路由:chat 或 video 模型会在提交时被拒绝。
轮询到终态
curl "https://openapi.felo.ai/api/v1/images/generations/${TASK_ID}" \
-H "Authorization: Bearer ${FELO_API_KEY}"| 状态 | 含义 | 处理 |
|---|---|---|
queued | 已接收,等待开始 | 继续轮询 |
running | 模型正在生成 | 继续轮询 |
succeeded | 终态;outputs 已填充 | 下载输出 |
failed | 终态;读取 error.message | 修正请求,不要原样重试 |
interrupted | 终态;结果无法恢复 | 重新提交任务 |
任务失败时 HTTP 状态仍是 200。要检查响应体里的 status,不能只看 HTTP 状态。
从 2–5 秒开始,逐步退避。大多数图片一分钟内完成;4K 输出和带大量参考图的编辑会更久。始终在代码里设置自己的截止时间。
import os
import time
import requests
BASE = "https://openapi.felo.ai"
HEADERS = {"Authorization": f"Bearer {os.environ['FELO_API_KEY']}"}
def generate_image(prompt: str, **params) -> list[dict]:
response = requests.post(
f"{BASE}/api/v1/images/generations",
headers=HEADERS,
json={"model": "gemini-3-pro-image", "prompt": prompt, **params},
timeout=30,
)
response.raise_for_status()
task = response.json()
deadline = time.monotonic() + 300
delay = 2.0
while time.monotonic() < deadline:
time.sleep(delay)
delay = min(delay * 1.5, 10.0)
poll = requests.get(
f"{BASE}/api/v1/images/generations/{task['task_id']}",
headers=HEADERS,
timeout=30,
)
poll.raise_for_status()
task = poll.json()
if task["status"] == "succeeded":
return task["outputs"]
if task["status"] in {"failed", "interrupted"}:
message = task.get("error", {}).get("message", task["status"])
raise RuntimeError(message)
raise TimeoutError(f"image task {task['task_id']} did not finish")读取并保存输出
任务成功后,outputs 每张图片一个条目:
{
"task_id": "6d1f0c9a-...",
"status": "succeeded",
"kind": "image",
"outputs": [
{
"media_type": "image/png",
"url": "https://openapi.felo.ai/api/v1/media/...?exp=...&sig=..."
}
]
}- 任务成功前
outputs为空。 url是签名链接,无需 API key 即可下载,请按 bearer 链接对待。- URL 有时效。
exp查询参数就是过期时间。请下载文件并存到你自己的桶或文件系统;任务不是永久存储。 - URL 过期后再次轮询返回的仍是同一个过期 URL,不会刷新。
- 响应不包含 base64 图片数据。
for index, output in enumerate(outputs):
image = requests.get(output["url"], timeout=60)
image.raise_for_status()
with open(f"image-{index}.png", "wb") as file:
file.write(image.content)编辑图片
通过 input_references 传入参考图。模型会把它作为上下文;prompt 仍然要描述修改内容。
curl -X POST "https://openapi.felo.ai/api/v1/images/generations" \
-H "Authorization: Bearer ${FELO_API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"model": "gemini-3-pro-image",
"prompt": "Keep the product and framing. Replace the background with a sunlit marble countertop.",
"resolution": "2K",
"aspect_ratio": "4:3",
"input_references": [
{
"type": "image_url",
"image_url": {"url": "https://example.com/product.png"}
}
]
}'每个参考图是 HTTP(S) URL 或 base64 data URL。参考图顺序会保留;需要时在 prompt 里按位置引用。当前模型最多接受 14 张参考图。
请求字段
| 字段 | 必填 | 说明 |
|---|---|---|
model | 是 | 目录中的模型 ID,例如 gemini-3-pro-image。不要使用上游的 google/... ID |
prompt | 是 | 文本指令。编辑图片时,描述要改什么、什么必须保持不变 |
resolution | 否 | 输出档位。当前模型接受 1K、2K、4K |
aspect_ratio | 否 | 输出比例。可选值随模型而定 |
n | 否 | 图片数量。当前模型接受 1 |
input_references | 否 | 用于编辑或构图的参考图 |
当前图片模型:
| 模型 ID | 分辨率 | 宽高比 | 单任务图片数 | 参考图 |
|---|---|---|---|---|
gemini-3-pro-image | 1K、2K、4K | 1:1、2:3、3:2、3:4、4:3、4:5、5:4、9:16、16:9、21:9 | 1 | 最多 14 张 |
gemini-nano-banana-2.1 | 1K、2K、4K | 1:1、1:4、1:8、2:3、3:2、3:4、4:1、4:3、4:5、5:4、8:1、9:16、16:9、21:9 | 1 | 最多 14 张 |
要固定供应商,发送 X-Felo-Channel 请求头,值取自模型 channels 列表中的 channel code。见渠道与供应商路由。
查找图片模型
没有独立的图片模型列表 endpoint。查询通用目录并按 model_kind 过滤:
curl "https://openapi.felo.ai/api/v1/models" \
-H "Authorization: Bearer ${FELO_API_KEY}" \
| jq '.data[] | select(.model_kind == "image_generation") | {id, name}'GET /api/v1/models/{model_id} 返回模型的 channels 和公布费率。
通用目录里的 supported_parameters 是协议层参数列表。对图片模型来说,它并不能可靠列出上面的图片字段,不要据此拼请求。请使用本页的字段表,并通过目录确认模型 ID。
计费
图片生成按模型公布的费率计费。目录里除了文本的 prompt 和 completion,还提供 input_image 和 output_image。图片费率是 token 费率:resolution 会改变一张图消耗的输出图片 token 数,所以 4K 比 1K 贵。参考图会增加输入图片 token。
任务响应不包含 usage 对象。实际扣费以 Console Billing 的用量记录为准。
以 failed 或 interrupted 结束的任务没有输出,也不计费。提交时返回 402 表示任务没有创建。
错误
图片路由的 endpoint 级错误使用 OpenAI 错误信封:
{
"error": {
"message": "Model kind does not match this generation endpoint",
"type": "invalid_request_error",
"param": null,
"code": "model_kind_mismatch"
}
}request ID 在 X-Request-Id 响应头里。请和 task ID 一起记录。
| HTTP | error.code | 含义 |
|---|---|---|
| 400 | invalid_request_error | 缺少 model,或请求体不是 JSON 对象 |
| 400 | model_not_configured | 模型 ID 不在已启用的目录中 |
| 400 | chat_model_not_allowed | 把 chat 模型发到了图片 endpoint |
| 400 | model_kind_mismatch | 把 video 或其他非图片模型发到了图片 endpoint |
| 402 | insufficient_balance | 额度不足;任务没有创建 |
| 404 | generation_task_not_found | 任务 ID 不存在,或属于其他账号 |
| 429 | daily_spend_limit_exceeded | 达到账号的每日消费上限 |
| 503 | generation_unavailable | 生成服务暂时不可用;稍后重试 |
认证错误见错误、限流与重试。