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
- Une clé API. Consultez Authentification.
- Un modèle dont le
model_kindestimage_generation. Consultez Trouver un modèle d'image. - L'URL de base
https://openapi.felo.ai.
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'OpenAI | API d'images Felo | |
|---|---|---|
| Réponse de soumission | 200 avec les données de l'image | 202 avec task_id, status: "queued" |
| Quand l'image existe | Dans la réponse de soumission | Une fois que l'interrogation atteint succeeded |
| Charge utile de l'image | b64_json ou url | Une url signée à durée limitée |
| Erreurs de requête | Erreur HTTP à la soumission | Gé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}"| Statut | Signification | Que faire |
|---|---|---|
queued | Acceptée ; en attente de démarrage | Continuez d'interroger |
running | Le modèle génère l'image | Continuez d'interroger |
succeeded | Terminal ; outputs est renseigné | Téléchargez les sorties |
failed | Terminal ; lisez error.message | Corrigez la requête ; ne réessayez pas à l'identique |
interrupted | Terminal ; 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=..."
}
]
}outputsest vide jusqu'à ce que la tâche réussisse.urlest 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
expest 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
| Champ | Requis | Description |
|---|---|---|
model | Oui | ID de modèle du catalogue, par exemple gemini-3-pro-image. N'utilisez pas l'ID upstream google/... |
prompt | Oui | Instruction textuelle. Pour une modification, décrivez le changement et ce qui doit rester identique |
resolution | Non | Palier de sortie. Les modèles actuels acceptent 1K, 2K ou 4K |
aspect_ratio | Non | Ratio de sortie. Les valeurs acceptées dépendent du modèle |
n | Non | Nombre d'images. Les modèles actuels acceptent 1 |
input_references | Non | Images de référence pour la modification ou la composition |
Modèles d'image actuels :
| ID de modèle | Résolutions | Ratios d'aspect | Images par tâche | Références |
|---|---|---|---|---|
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 | Jusqu'à 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 | Jusqu'à 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.
| HTTP | error.code | Signification |
|---|---|---|
| 400 | invalid_request_error | model est manquant, ou le corps n'est pas un objet JSON |
| 400 | model_not_configured | L'ID de modèle ne figure pas dans le catalogue activé |
| 400 | chat_model_not_allowed | Un modèle de chat a été envoyé à l'endpoint d'images |
| 400 | model_kind_mismatch | Un modèle vidéo ou autre non-image a été envoyé |
| 402 | insufficient_balance | Crédits insuffisants ; aucune tâche n'a été créée |
| 404 | generation_task_not_found | L'ID de tâche est inconnu ou appartient à un autre compte |
| 429 | daily_spend_limit_exceeded | La limite de dépense quotidienne du compte a été atteinte |
| 503 | generation_unavailable | La génération est temporairement indisponible ; réessayez plus tard |
Les erreurs d'authentification sont décrites dans Erreurs, limites et nouvelles tentatives.