Tutoriel
Comment utiliser flux-2-klein-9b/image-to-image via l'API hiapi : curl, Python et requêtes exécutables
Ce tutoriel montre comment appeler le mode image-to-image de flux-2-klein-9b via l'API de tâches unifiée de hiapi : la structure exacte de la requête, une commande curl exécutable, un script Python directement intégrable dans un backend, ainsi qu'une particularité de schéma que vous rencontrerez si vous migrez depuis d'autres modèles de la plateforme.
À la fin de votre lecture, vous disposerez d'un script qui reçoit une URL d'image de référence et un prompt d'édition, et renvoie une URL d'image générée téléchargeable.
Ce dont vous avez besoin
- Un compte hiapi et une clé API. Disponibles depuis la console hiapi.
- Exactement une image de référence hébergée sur une URL HTTPS publique. Contrairement à certains modèles image-to-image de la plateforme qui acceptent plusieurs images de référence, le tableau
image_urlsde flux-2-klein-9b est limité à un seul élément — voir les détails ci-dessous. - Un prompt texte décrivant l'édition ou la transformation.
Toutes les requêtes sont envoyées au point de terminaison de tâches unifié, la clé étant placée dans l'en-tête Authorization :
POST https://api.hiapi.ai/v1/tasks
Authorization: Bearer sk-<your-key>
Content-Type: application/json
Structure de la requête (et un champ qui piège facilement)
L'identifiant du modèle est flux-2-klein-9b/image-to-image — transmettez-le tel quel, exactement comme listé dans /v1/models. Son schéma input est strict :
| Champ | Type | Obligatoire | Description |
|---|---|---|---|
prompt |
string | Oui | Instruction d'édition. |
image_urls |
string[] | Oui | Exactement une URL d'image de référence — minItems et maxItems valent tous deux 1. |
aspect_ratio |
string | Non | L'une des valeurs suivantes : 1:1, 4:3, 3:4, 16:9, 9:16. |
seed |
integer | Non | Figez-le pour reproduire les résultats. |
output_format |
string | Non | L'une des valeurs suivantes : jpeg, png, webp. |
Deux points méritent d'être connus avant d'écrire du code :
image_urlsaccepte une seule URL, pas une plage.Si vous avez construit du code de soumission partagé autour d'un modèle acceptant 1 à 14 images de référence, envoyer deux URL ici sera rejeté —maxItems: got 2, want 1. Ce modèle effectue l'édition d'une seule image, et non la fusion de plusieurs références.- Les champs supplémentaires sont rejetés, et non ignorés.
resolution,size,n,strength,image_sizeetnum_images— courants dans d'autres modèles d'image de la plateforme — renvoient tous ici400 INVALID_REQUEST: additional properties '<field>' not allowed. Ne réutilisez pas le payload d'un autre modèle sans vérifier le schéma de celui-ci.
Exemple minimal exécutable : curl
Créer une tâche :
curl -X POST https://api.hiapi.ai/v1/tasks \
-H "Authorization: Bearer sk-<your-key>" \
-H "Content-Type: application/json" \
-d '{
"model": "flux-2-klein-9b/image-to-image",
"input": {
"prompt": "Change the jacket to charcoal grey leather, keep the pose and background unchanged",
"image_urls": ["https://your-cdn.example.com/subject.png"],
"aspect_ratio": "3:4",
"output_format": "png"
}
}'
Une soumission réussie renvoie un identifiant de tâche dans data :
{"code": 200, "data": {"taskId": "tk-hiapi-..."}}
Interroger le résultat :
curl https://api.hiapi.ai/v1/tasks/tk-hiapi-... \
-H "Authorization: Bearer sk-<your-key>"
Pendant l'exécution, data.status passe successivement par queued → handling → archiving. Une fois terminé, il passe à "success" et l'image apparaît dans data.output :
{
"code": 200,
"data": {
"status": "success",
"output": [{"url": "https://.../result.png", "expireAt": "..."}]
}
}
Les URL de sortie ont une durée de vie courte (notez expireAt). Téléchargez immédiatement les octets et stockez-les vous-même — ne faites jamais de hotlink et ne persistez pas l'URL brute.
Python de niveau production : soumettre, interroger, télécharger
import time
import requests
API_BASE = "https://api.hiapi.ai/v1/tasks"
API_KEY = "sk-<your-key>"
HEADERS = {"Authorization": f"Bearer {API_KEY}"}
def edit_image(prompt: str, image_url: str,
aspect_ratio: str = "1:1", output_format: str = "png") -> bytes:
# 1. Créer la tâche — image_urls de ce modèle accepte exactement une URL
resp = requests.post(API_BASE, headers=HEADERS, json={
"model": "flux-2-klein-9b/image-to-image",
"input": {
"prompt": prompt,
"image_urls": [image_url],
"aspect_ratio": aspect_ratio,
"output_format": output_format,
},
}, timeout=60)
body = resp.json()
if resp.status_code != 200 or not (body.get("data") or {}).get("taskId"):
raise RuntimeError(f"create failed [{resp.status_code}]: {body}")
task_id = body["data"]["taskId"]
# 2. Interroger jusqu'à l'état terminal
deadline = time.time() + 600
while time.time() < deadline:
task = requests.get(f"{API_BASE}/{task_id}", headers=HEADERS,
timeout=30).json().get("data") or {}
if task.get("status") == "success":
outputs = task.get("output") or []
if not outputs or not outputs[0].get("url"):
raise RuntimeError(f"task {task_id} succeeded but returned no output URL")
# 3. Télécharger immédiatement — l'URL expire
img = requests.get(outputs[0]["url"], timeout=120)
img.raise_for_status()
return img.content
if task.get("status") == "fail":
err = task.get("error") or {}
raise RuntimeError(f"task failed: {err.get('code')} {err.get('message')}")
time.sleep(5)
raise TimeoutError(f"task {task_id} still running after 600s")
if __name__ == "__main__":
png = edit_image(
"Change the jacket to charcoal grey leather, keep the pose and background unchanged",
"https://your-cdn.example.com/subject.png",
aspect_ratio="3:4",
)
with open("edited.png", "wb") as f:
f.write(png)
print(f"saved edited.png ({len(png)} bytes)")
Éditions reproductibles avec seed
Transmettez un entier seed et le modèle le réutilisera pour cette génération, ce qui est utile lorsque vous souhaitez faire des tests A/B sur la formulation du prompt en gardant tout le reste inchangé :
{
"model": "flux-2-klein-9b/image-to-image",
"input": {
"prompt": "Change the jacket to charcoal grey leather, keep the pose and background unchanged",
"image_urls": ["https://your-cdn.example.com/subject.png"],
"seed": 42
}
}
Si vous l'omettez, le modèle choisit un seed aléatoire à chaque appel, ce qui est précisément ce que veut le trafic de production normal — lorsque vous avez besoin de sorties d'apparence différente, modifiez le prompt, pas le seed.
Utiliser un callback au lieu de l'interrogation
Pour les scripts et les files de workers, l'interrogation suffit. Pour un backend web, enregistrez un callback à la création de la tâche afin que hiapi vous pousse l'état terminal — l'objet callback se trouve au niveau supérieur de la requête, au même niveau que model :
{
"model": "flux-2-klein-9b/image-to-image",
"input": { "...": "..." },
"callback": {
"url": "https://your-app.example.com/hooks/hiapi",
"when": "final"
}
}
Avec "when": "final", vous recevez exactement un POST lorsque la tâche atteint l'état succès ou échec. Deux règles pour le gestionnaire :
- Rendez-le idempotent.Faites en sorte que votre logique de traitement soit indexée par identifiant de tâche, afin qu'un webhook redélivré ne soit pas traité deux fois. Stockez l'identifiant de tâche lors de la soumission et considérez le callback comme « aller chercher et finaliser », plutôt que comme source unique de vérité.
- Conservez un chemin de réconciliation.Si votre point de terminaison est indisponible au moment du déclenchement du callback, scannez périodiquement les identifiants non résolus et faites un GET
/v1/tasks/<id>pour rattraper tout ce qui a été manqué.
Si votre callback semble ne jamais arriver, examinez une par une les causes dans pourquoi le callback de votre tâche hiapi ne se déclenche pas — l'article couvre les modes de défaillance courants par ordre de probabilité.
Gestion des erreurs : trois scénarios d'échec clés
401 permission_denied à la création — votre clé ne peut pas utiliser ce modèle. Le corps de la réponse est le suivant :
{"error": {"code": "permission_denied", "message": "...", "request_id": "...", "type": "hiapi_error"}}
Si vous rencontrez ce problème, consultez clé API invalide, ou vérifiez les permissions de modèle de la clé dans la console.
400 INVALID_REQUEST à la création — votre input ne correspond pas au schéma de ce modèle. Le message indique directement le champ en infraction (par exemple <root>: additional properties 'resolution' not allowed, ou image_urls: maxItems: got 2, want 1). Ce sont des erreurs permanentes — réessayer le même payload échouera toujours ; corrigez le champ puis renvoyez.
La tâche renvoie status: "fail" — la création a réussi mais la génération a échoué (URL de référence non récupérable par le backend, politique de contenu, capacité amont). Les champs error.code et error.message de la tâche indiquent de laquelle il s'agit. Contrairement aux erreurs 400, certaines sont temporaires et méritent une nouvelle tentative avec backoff.
Autre point à connaître : un GET sur un identifiant de tâche inexistant ou expiré renvoie 404 task not found — traitez-le comme terminal dans vos scans de réconciliation, et non comme « toujours en attente ».
Où aller ensuite
- Page du modèle flux-2-klein-9b/image-to-image — aire de jeu et tarification par image.
- Tarification hiapi — tarifs actuels de tous les modèles d'image.
- Tutoriel seedream-5.0-lite image-to-image — même API de tâches, schéma acceptant jusqu'à 14 images de référence au lieu d'une ; une bonne illustration de pourquoi les payloads ne sont pas transférables entre modèles.
- Documentation de l'API de tâches asynchrones hiapi — référence complète création/interrogation/callback.
- Documentation d'authentification hiapi — configuration de la clé API et format des en-têtes.
Questions fréquentes
Puis-je transmettre plusieurs images de référence à flux-2-klein-9b image-to-image ?
Non. minItems et maxItems de image_urls valent tous deux 1 — exactement une URL, pas plus, pas moins. Si vous avez besoin de fusionner plusieurs références, tournez-vous vers un modèle dont le schéma documente une plage plus large, comme seedream-5.0-lite.
Pourquoi est-ce que je reçois « additional properties 'resolution' not allowed » ?
Le schéma input de l'API de tâches est propre à chaque modèle. flux-2-klein-9b/image-to-image n'accepte pas du tout les champs resolution ou size — les dimensions de sortie suivent l'image d'entrée et aspect_ratio. Supprimez tout champ de dimension hérité du payload d'un autre modèle.
Puis-je envoyer l'image de référence en base64 plutôt qu'en URL ?
Le schéma de ce modèle accepte image_urls — un tableau d'URL. Hébergez l'image sur une URL HTTPS accessible (n'importe quel CDN, bucket de stockage objet ou URL pré-signée convient) et transmettez cette URL.
Que contrôle réellement seed ?
Avec le même seed, le même prompt et la même image de référence, la génération obtenue est identique, ce qui est utile pour des tests A/B contrôlés de prompts. Dans le trafic normal, omettez-le pour que chaque appel obtienne son propre seed aléatoire.
Le même code s'applique-t-il à flux-2-klein-9b text-to-image ?
Le point de terminaison et le flux sont identiques, mais text-to-image a son propre schéma input (pas de image_urls). Remplacez l'identifiant de modèle par flux-2-klein-9b/text-to-image et retirez l'image de référence — puis validez par un appel de test, car les champs obligatoires varient selon le mode.
Combien coûte chaque image ? La tarification est facturée à l'usage par génération. Consultez les tarifs en vigueur sur la page de tarification.
Articles similaires
Voir tous les articles →-
Tutoriel
Comment appeler hailuo-2.3-fast/image-to-video via l'API HiAPI : curl et Python en pratique
La première requête exploitable du palier image-vers-vidéo rapide et économique — de l'ID du modèle au téléchargement du MP4
-
Tutoriel
Comment appeler hailuo-2.3/text-to-video via l'API HiAPI : curl, Python et exemples de requêtes exécutables
-
Tutoriel
Utiliser Kandinsky 6.0 Pro texte-vers-vidéo sur HiAPI : tutoriel curl et Python
Démonstration pratique axée sur la requête : construire l'appel du prompt au clip vidéo, soumettre la requête, interroger l'état d'achèvement et télécharger le résultat