Guías
Generación de imágenes
Genera y edita imágenes de forma asíncrona: envía una tarea, consulta su finalización y descarga las URL de salida firmadas.
La API de imágenes de Felo genera y edita imágenes con modelos del catálogo de modelos de Felo. Es asíncrona: la solicitud de envío devuelve un ID de tarea inmediatamente y se consulta la tarea hasta que alcanza un estado terminal.
Esta página trata el contrato específico de las imágenes. Para conocer el ciclo de vida compartido de las tareas, consulta Tareas asíncronas.
Antes de empezar
- Una clave de API. Consulta Autenticación.
- Un modelo cuyo
model_kindseaimage_generation. Consulta Encontrar un modelo de imagen. - La URL base
https://openapi.felo.ai.
En qué se diferencia de la API Images de OpenAI
La ruta es similar a la de la API Images de OpenAI. La respuesta no lo es:
| API Images de OpenAI | API de imágenes de Felo | |
|---|---|---|
| Respuesta al envío | 200 con los datos de la imagen | 202 con task_id, status: "queued" |
| Cuándo existe la imagen | En la respuesta al envío | Después de que la consulta alcance succeeded |
| Carga útil de la imagen | b64_json o url | Una url firmada con vigencia limitada |
| Errores de solicitud | Error HTTP al enviar | Normalmente status: "failed" en la tarea |
No envíes openai.images.generate() a este endpoint. El SDK espera un array data síncrono y no puede consultar una tarea. Llama al endpoint por HTTP y consulta la tarea, o escribe un pequeño wrapper para ambos pasos.
Enviar una tarea
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"
}'Un envío correcto devuelve HTTP 202:
{
"task_id": "6d1f0c9a-...",
"status": "queued",
"kind": "image",
"outputs": []
}202 significa que la plataforma aceptó la tarea. No significa que el prompt, los parámetros o las imágenes de referencia sean válidos. La llamada upstream se ejecuta después de la respuesta, por lo que la mayoría de los problemas de solicitud aparecen más tarde como una tarea fallida. La excepción es el enrutamiento de modelos: un modelo de chat o de video se rechaza al enviar.
Consultar hasta que la tarea sea terminal
curl "https://openapi.felo.ai/api/v1/images/generations/${TASK_ID}" \
-H "Authorization: Bearer ${FELO_API_KEY}"| Estado | Significado | Qué hacer |
|---|---|---|
queued | Aceptada; en espera de inicio | Sigue consultando |
running | El modelo está generando | Sigue consultando |
succeeded | Terminal; outputs está poblado | Descarga las salidas |
failed | Terminal; lee error.message | Corrige la solicitud; no la reintentes sin cambios |
interrupted | Terminal; el resultado no se pudo recuperar | Vuelve a enviar la tarea |
Los fallos de tarea mantienen el estado HTTP en 200. Comprueba status en el cuerpo, no solo el estado HTTP.
Empieza con un intervalo de 2–5 segundos y auméntalo progresivamente. La mayoría de las imágenes terminan en un minuto; la salida en 4K y las ediciones con muchas imágenes de referencia tardan más. Define siempre tu propio plazo límite.
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")Leer y guardar las salidas
Cuando la tarea se completa correctamente, outputs contiene una entrada por cada imagen generada:
{
"task_id": "6d1f0c9a-...",
"status": "succeeded",
"kind": "image",
"outputs": [
{
"media_type": "image/png",
"url": "https://openapi.felo.ai/api/v1/media/...?exp=...&sig=..."
}
]
}outputsestá vacío hasta que la tarea se completa correctamente.urles una URL firmada. Se descarga sin clave de API, así que trátala como un enlace de portador.- La URL tiene vigencia limitada. El parámetro de consulta
expes su hora de caducidad. Descarga el archivo y guárdalo en tu propio bucket o sistema de archivos; la tarea no es un almacenamiento permanente. - Si vuelves a consultar después de que caduque la URL, obtendrás la misma URL caducada. No se emite una nueva.
- La respuesta no incluye datos de imagen en 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)Editar una imagen
Pasa las imágenes de referencia en input_references. El modelo las usa como contexto; el prompt sigue describiendo el cambio.
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"}
}
]
}'Cada referencia es una URL HTTP(S) o una URL de datos base64. Se conserva el orden de las referencias; cuando importe, alude a las imágenes por su posición en el prompt. Los modelos actuales aceptan hasta 14 referencias.
Campos de la solicitud
| Campo | Obligatorio | Descripción |
|---|---|---|
model | Sí | ID de modelo del catálogo, por ejemplo gemini-3-pro-image. No uses el ID upstream google/... |
prompt | Sí | Instrucción de texto. Para una edición, describe el cambio y lo que debe permanecer igual |
resolution | No | Nivel de salida. Los modelos actuales aceptan 1K, 2K o 4K |
aspect_ratio | No | Proporción de salida. Los valores admitidos dependen del modelo |
n | No | Número de imágenes. Los modelos actuales aceptan 1 |
input_references | No | Imágenes de referencia para editar o componer |
Modelos de imagen actuales:
| ID de modelo | Resoluciones | Proporciones | Imágenes por tarea | Referencias |
|---|---|---|---|---|
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 | Hasta 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 | Hasta 14 |
Para fijar un proveedor, envía el encabezado X-Felo-Channel con un código de canal de la lista channels del modelo. Consulta Canales y enrutamiento de proveedores.
Encontrar un modelo de imagen
No hay un endpoint independiente para listar modelos de imagen. Consulta el catálogo general y filtra por 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} devuelve los canales del modelo y las tarifas publicadas.
supported_parameters del catálogo general es una lista de nivel de protocolo. En los modelos de imagen no enumera de forma fiable los campos de imagen anteriores, así que no construyas tu solicitud a partir de él. Usa la tabla de campos de esta guía y confirma el ID de modelo en el catálogo.
Precios
La generación de imágenes se factura según las tarifas publicadas del modelo. El catálogo expone input_image y output_image junto con las tarifas de texto prompt y completion. Las tarifas de imagen son tarifas por token: resolution cambia cuántos tokens de imagen de salida consume una imagen, por lo que una imagen en 4K cuesta más que una en 1K. Las imágenes de referencia añaden tokens de imagen de entrada.
La respuesta de la tarea no incluye un objeto usage. Consulta el cargo en Facturación de la consola.
Una tarea que termina en failed o interrupted no produce salida y no se factura. Un envío rechazado con 402 no creó ninguna tarea.
Errores
Los errores de nivel de endpoint de la ruta de imágenes usan el sobre de error de OpenAI:
{
"error": {
"message": "Model kind does not match this generation endpoint",
"type": "invalid_request_error",
"param": null,
"code": "model_kind_mismatch"
}
}El ID de solicitud está en el encabezado de respuesta X-Request-Id. Regístralo junto con el ID de tarea.
| HTTP | error.code | Significado |
|---|---|---|
| 400 | invalid_request_error | Falta model o el cuerpo no es un objeto JSON |
| 400 | model_not_configured | El ID de modelo no está en el catálogo habilitado |
| 400 | chat_model_not_allowed | Se envió un modelo de chat al endpoint de imágenes |
| 400 | model_kind_mismatch | Se envió un modelo de video u otro modelo que no es de imagen |
| 402 | insufficient_balance | No hay suficientes créditos; no se creó ninguna tarea |
| 404 | generation_task_not_found | El ID de tarea es desconocido o pertenece a otra cuenta |
| 429 | daily_spend_limit_exceeded | Se alcanzó el límite de gasto diario de la cuenta |
| 503 | generation_unavailable | La generación no está disponible temporalmente; vuelve a intentarlo más tarde |
Los errores de autenticación se describen en Errores, límites y reintentos.