Felo API PlatformFelo API Platform

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

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 OpenAIAPI de imágenes de Felo
Respuesta al envío200 con los datos de la imagen202 con task_id, status: "queued"
Cuándo existe la imagenEn la respuesta al envíoDespués de que la consulta alcance succeeded
Carga útil de la imagenb64_json o urlUna url firmada con vigencia limitada
Errores de solicitudError HTTP al enviarNormalmente 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}"
EstadoSignificadoQué hacer
queuedAceptada; en espera de inicioSigue consultando
runningEl modelo está generandoSigue consultando
succeededTerminal; outputs está pobladoDescarga las salidas
failedTerminal; lee error.messageCorrige la solicitud; no la reintentes sin cambios
interruptedTerminal; el resultado no se pudo recuperarVuelve 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=..."
    }
  ]
}
  • outputs está vacío hasta que la tarea se completa correctamente.
  • url es 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 exp es 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

CampoObligatorioDescripción
modelSíID de modelo del catálogo, por ejemplo gemini-3-pro-image. No uses el ID upstream google/...
promptSíInstrucción de texto. Para una edición, describe el cambio y lo que debe permanecer igual
resolutionNoNivel de salida. Los modelos actuales aceptan 1K, 2K o 4K
aspect_ratioNoProporción de salida. Los valores admitidos dependen del modelo
nNoNúmero de imágenes. Los modelos actuales aceptan 1
input_referencesNoImágenes de referencia para editar o componer

Modelos de imagen actuales:

ID de modeloResolucionesProporcionesImágenes por tareaReferencias
gemini-3-pro-image1K, 2K, 4K1:1, 2:3, 3:2, 3:4, 4:3, 4:5, 5:4, 9:16, 16:9, 21:91Hasta 14
gemini-nano-banana-2.11K, 2K, 4K1: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:91Hasta 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.

HTTPerror.codeSignificado
400invalid_request_errorFalta model o el cuerpo no es un objeto JSON
400model_not_configuredEl ID de modelo no está en el catálogo habilitado
400chat_model_not_allowedSe envió un modelo de chat al endpoint de imágenes
400model_kind_mismatchSe envió un modelo de video u otro modelo que no es de imagen
402insufficient_balanceNo hay suficientes créditos; no se creó ninguna tarea
404generation_task_not_foundEl ID de tarea es desconocido o pertenece a otra cuenta
429daily_spend_limit_exceededSe alcanzó el límite de gasto diario de la cuenta
503generation_unavailableLa 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.

Relacionado