Felo API PlatformFelo API Platform

Guides

Génération d'images

Générez et modifiez des images de façon asynchrone : soumettez une tâche, interrogez son achèvement et téléchargez les URL de sortie signées.

L'API d'images Felo génère et modifie des images avec des modèles du catalogue de modèles Felo. Elle est asynchrone : la requête de soumission renvoie immédiatement un ID de tâche, et vous interrogez la tâche jusqu'à ce qu'elle atteigne un état terminal.

Cette page couvre le contrat propre aux images. Pour le cycle de vie commun des tâches, consultez Tâches asynchrones.

Avant de commencer

En quoi elle diffère de l'API Images d'OpenAI

Le chemin est similaire à celui de l'API Images d'OpenAI. La réponse ne l'est pas :

API Images d'OpenAIAPI d'images Felo
Réponse de soumission200 avec les données de l'image202 avec task_id, status: "queued"
Quand l'image existeDans la réponse de soumissionUne fois que l'interrogation atteint succeeded
Charge utile de l'imageb64_json ou urlUne url signée à durée limitée
Erreurs de requêteErreur HTTP à la soumissionGénéralement status: "failed" sur la tâche

N'envoyez pas openai.images.generate() à cet endpoint. Le SDK attend un tableau data synchrone et ne peut pas interroger une tâche. Appelez l'endpoint en HTTP et interrogez la tâche, ou écrivez un petit wrapper pour les deux étapes.

Soumettre une tâche

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"
  }'

Une soumission réussie renvoie le code HTTP 202 :

{
  "task_id": "6d1f0c9a-...",
  "status": "queued",
  "kind": "image",
  "outputs": []
}

202 signifie que la plateforme a accepté la tâche. Cela ne signifie pas que le prompt, les paramètres ou les images de référence sont valides. L'appel upstream s'exécute après la réponse, donc la plupart des problèmes de requête apparaissent plus tard sous forme de tâche échouée. L'exception est le routage de modèle : un modèle de chat ou vidéo est rejeté à la soumission.

Interroger jusqu'à ce que la tâche soit terminale

curl "https://openapi.felo.ai/api/v1/images/generations/${TASK_ID}" \
  -H "Authorization: Bearer ${FELO_API_KEY}"
StatutSignificationQue faire
queuedAcceptée ; en attente de démarrageContinuez d'interroger
runningLe modèle génère l'imageContinuez d'interroger
succeededTerminal ; outputs est renseignéTéléchargez les sorties
failedTerminal ; lisez error.messageCorrigez la requête ; ne réessayez pas à l'identique
interruptedTerminal ; le résultat n'a pas pu être récupéréSoumettez à nouveau la tâche

Les échecs de tâche conservent le statut HTTP 200. Vérifiez status dans le corps, pas seulement le statut HTTP.

Commencez par un intervalle de 2 à 5 secondes et espacez les tentatives. La plupart des images se terminent en moins d'une minute ; la sortie 4K et les modifications avec de nombreuses images de référence prennent plus de temps. Fixez toujours votre propre échéance.

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")

Lire et stocker les sorties

Lorsque la tâche réussit, outputs contient une entrée par image générée :

{
  "task_id": "6d1f0c9a-...",
  "status": "succeeded",
  "kind": "image",
  "outputs": [
    {
      "media_type": "image/png",
      "url": "https://openapi.felo.ai/api/v1/media/...?exp=...&sig=..."
    }
  ]
}
  • outputs est vide jusqu'à ce que la tâche réussisse.
  • url est une URL signée. Elle se télécharge sans clé API ; traitez-la donc comme un lien porteur.
  • L'URL est à durée limitée. Le paramètre de requête exp est son heure d'expiration. Téléchargez le fichier et stockez-le dans votre propre bucket ou système de fichiers ; la tâche n'est pas un stockage permanent.
  • Interroger à nouveau après l'expiration de l'URL renvoie la même URL expirée. Cela n'en émet pas de nouvelle.
  • La réponse n'inclut pas de données d'image 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)

Modifier une image

Passez les images de référence dans input_references. Le modèle les utilise comme contexte ; le prompt décrit toujours la modification.

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"}
      }
    ]
  }'

Chaque référence est une URL HTTP(S) ou une URL de données base64. L'ordre des références est conservé ; référez-vous aux images par leur position dans le prompt lorsque c'est important. Les modèles actuels acceptent jusqu'à 14 références.

Champs de requête

ChampRequisDescription
modelOuiID de modèle du catalogue, par exemple gemini-3-pro-image. N'utilisez pas l'ID upstream google/...
promptOuiInstruction textuelle. Pour une modification, décrivez le changement et ce qui doit rester identique
resolutionNonPalier de sortie. Les modèles actuels acceptent 1K, 2K ou 4K
aspect_ratioNonRatio de sortie. Les valeurs acceptées dépendent du modèle
nNonNombre d'images. Les modèles actuels acceptent 1
input_referencesNonImages de référence pour la modification ou la composition

Modèles d'image actuels :

ID de modèleRésolutionsRatios d'aspectImages par tâcheRéférences
gemini-3-pro-image1K, 2K, 4K1:1, 2:3, 3:2, 3:4, 4:3, 4:5, 5:4, 9:16, 16:9, 21:91Jusqu'à 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:91Jusqu'à 14

Pour épingler un fournisseur, envoyez l'en-tête X-Felo-Channel avec un code de canal issu de la liste channels du modèle. Consultez Canaux et routage fournisseur.

Trouver un modèle d'image

Il n'existe pas d'endpoint de liste de modèles d'image séparé. Interrogez le catalogue général et filtrez sur 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} renvoie les canaux du modèle et les tarifs publiés.

supported_parameters dans le catalogue général est une liste au niveau du protocole. Pour les modèles d'image, elle n'énumère pas de manière fiable les champs d'image ci-dessus : ne construisez donc pas votre requête à partir d'elle. Utilisez le tableau des champs de ce guide et confirmez l'ID de modèle dans le catalogue.

Tarification

La génération d'images est facturée selon les tarifs publiés du modèle. Le catalogue expose input_image et output_image aux côtés des tarifs texte prompt et completion. Les tarifs d'image sont des tarifs par jeton : resolution change le nombre de jetons d'image de sortie qu'une image consomme, donc une image 4K coûte plus cher qu'une image 1K. Les images de référence ajoutent des jetons d'image d'entrée.

La réponse de tâche n'inclut pas d'objet usage. Vérifiez les frais dans Console Billing.

Une tâche qui se termine en failed ou interrupted ne produit aucune sortie et n'est pas facturée. Une soumission rejetée avec 402 n'a pas créé de tâche.

Erreurs

Les erreurs au niveau de l'endpoint de la route images utilisent l'enveloppe d'erreur OpenAI :

{
  "error": {
    "message": "Model kind does not match this generation endpoint",
    "type": "invalid_request_error",
    "param": null,
    "code": "model_kind_mismatch"
  }
}

L'ID de requête se trouve dans l'en-tête de réponse X-Request-Id. Journalisez-le avec l'ID de tâche.

HTTPerror.codeSignification
400invalid_request_errormodel est manquant, ou le corps n'est pas un objet JSON
400model_not_configuredL'ID de modèle ne figure pas dans le catalogue activé
400chat_model_not_allowedUn modèle de chat a été envoyé à l'endpoint d'images
400model_kind_mismatchUn modèle vidéo ou autre non-image a été envoyé
402insufficient_balanceCrédits insuffisants ; aucune tâche n'a été créée
404generation_task_not_foundL'ID de tâche est inconnu ou appartient à un autre compte
429daily_spend_limit_exceededLa limite de dépense quotidienne du compte a été atteinte
503generation_unavailableLa génération est temporairement indisponible ; réessayez plus tard

Les erreurs d'authentification sont décrites dans Erreurs, limites et nouvelles tentatives.

Voir aussi