Passer au contenu principal
Synthèse des bonnes pratiques pour les questions fréquentes, l’optimisation des performances et la gestion des erreurs. À lire entièrement avant l’intégration.

Soumission de tâche et sondage

Les endpoints de soumission sont tous des tâches asynchrones : après la soumission, task_id est renvoyé, puis vous interrogez périodiquement GET /v1/midjourney/{task_id} pour récupérer le statut, jusqu’à SUCCESS / FAILURE.
import time, httpx

def wait_task(task_id, timeout=300):
    deadline = time.time() + timeout
    while time.time() < deadline:
        resp = httpx.get(f"{HOST}/v1/midjourney/{task_id}",
                         headers={"Authorization": f"Bearer {API_KEY}"}).json()
        if resp["status"] in ("SUCCESS", "FAILURE"):
            return resp
        if resp["status"] == "MODAL":
            raise RuntimeError(f"task {task_id} nécessite un appel à /modal pour compléter les paramètres")
        time.sleep(3)
    raise TimeoutError(task_id)
  • Rythme de sondage : recommandé une fois toutes les 3 à 5 s ; une fréquence plus élevée est inutile et gaspille du quota.
  • Ne bloquez pas de manière synchrone dans une requête web en attendant l’achèvement de la tâche : renvoyez task_id immédiatement après la soumission, et laissez le frontend sonder de manière asynchrone.

Conception du prompt

Un bon prompt :
a serene mountain lake at sunrise, photorealistic, soft golden light,
mist rising from water, snow-capped peaks in distance --ar 16:9 --v 8.1 --s 100
  • Le sujet d’abord : d’abord le sujet, puis la description de la scène, enfin les modificateurs.
  • Paramètres structurés explicites : utiliser --ar / --v / --s (ou les champs body correspondants) est plus contrôlable que de s’appuyer sur les valeurs par défaut.
  • Évitez les termes ambigus : photorealistic est plus précis que realistic.
À éviter : trop abstrait (« make it good »), sujet dispersé (plusieurs objets juxtaposés sans hiérarchie), mettre des mots entre guillemets (ils seront pris comme valeur littérale). Niji anime : passez niji: true + version: "7" ; la plateforme normalise en --niji 7, et la facturation passe par midjourney@imagine-niji7.

Bonnes pratiques pour l’image de référence

SourcePratique recommandéeRemarque
Téléversement utilisateurStockez d’abord sur votre propre OSS / CDN, puis passez cette URL lors de la soumissionNe passez pas directement du base64 (gaspille de la bande passante)
URL publiquePassez directementAttention au SSRF (doit être accessible publiquement) et à la limite de 12 MiB
Tiers / autres productionsRe-stockez d’abord sur votre propre OSSLes URL tierces peuvent expirer
  • Compressez à < 5 MiB : la limite de la plateforme est 12 MiB, mais les petites images se transmettent / traitent plus vite.
  • Les formats PNG / JPG / WebP sont tous acceptés ; JPG haute qualité recommandé.
  • Une résolution de 1024 à 2048 px suffit largement ; plus haut est du gaspillage.
  • Poids de l’image de référence iw (0–3, défaut 1) : >1 colle davantage à l’original, <1 plus libre.

Gestion des erreurs et stratégie de relance

codeSignificationStratégie de relance
1 / 200Succès
4 VALIDATION_ERRORErreur de paramètre❌ Ne pas relancer, corrigez le paramètre
3 NOT_FOUNDAucune instance disponible / task_id inexistantSi l’instance est indisponible, réessayez plus tard ; si le task_id n’existe pas, ne relancez pas
9 FAILURERefus du service / erreur interne⏳ Relançable, backoff exponentiel (1s, 4s, 16s)
21 MODALÉtat non terminal✅ Continuez d’appeler /modal
24 BANNED_PROMPTMot sensible❌ Ne pas relancer, modifiez le prompt ; déjà remboursé automatiquement
429Limitation de débit⏳ Backoff exponentiel + jitter
5xx / erreur réseauCôté serveur / réseau⏳ Backoff exponentiel ; en cas d’erreur réseau, vous pouvez relancer immédiatement 1 fois
import time, random, httpx

def submit_with_retry(payload, max_attempts=5):
    for attempt in range(max_attempts):
        try:
            r = httpx.post(f"{HOST}/v1/midjourney/generations/imagine",
                           json=payload,
                           headers={"Authorization": f"Bearer {API_KEY}"},
                           timeout=30)
            data = r.json()
            if r.status_code == 200 and data["code"] in (1, 200):
                return data
            if data["code"] in (4, 24):
                raise ValueError(data["description"])      # non relançable
            if data["code"] == 3 and "task" in data["description"]:
                raise ValueError(data["description"])      # task_id inexistant
            # les autres (9 / 429 / 5xx) sont relançables
        except httpx.RequestError:
            pass
        time.sleep((4 ** attempt) + random.uniform(0, 1))  # 1s / 4s / 16s ...
    raise RuntimeError(f"nombre maximal de relances atteint {max_attempts}")

Flux d’opérations secondaires

# imagine → sondage → upscale
imagine_id = submit({"prompt": "a cat"})["data"][0]["task_id"]
result = wait_task(imagine_id)           # grid_image_url + 4 image_urls + buttons
upscale_id = submit_to("/upscale", {"task_id": imagine_id, "index": 2})["data"][0]["task_id"]
final = wait_task(upscale_id)            # upscale composé localement, 1–2s
single_image = final["image_urls"][0]
Retouche locale (inpaint → modal en deux étapes) :
imagine_id = submit({"prompt": "a portrait"})["data"][0]["task_id"]; wait_task(imagine_id)
upscale_id = submit_to("/upscale", {"task_id": imagine_id, "index": 1})["data"][0]["task_id"]; wait_task(upscale_id)

inpaint_id = submit_to("/inpaint", {"task_id": upscale_id})["data"][0]["task_id"]  # status=modal
# Le frontend dessine le masque (blanc = zone à repeindre), le téléverse sur votre propre OSS pour obtenir mask_url
final = submit_to("/modal", {
    "task_id": inpaint_id,
    "prompt": "replace the eyes with cybernetic blue eyes",
    "mask_url": "https://your-oss.com/mask.png"
})
wait_task(final["data"][0]["task_id"])
⚠️ Une fois inpaint entré en MODAL, vous devez appeler /modal dans les 30 minutes, sinon le backend exécute automatiquement un CANCEL + remboursement.

Contrôle de la facturation vidéo

  • Segment unique : batch_size: 1 → débite 1 × midjourney@video
  • Lot de 4 segments : batch_size: 4 → débite 4 × midjourney@video
  • Segment unique HD : video_type: "vid_1.1_i2v_720" + batch_size: 1 → débite 1 × midjourney@video-720p
Recommandation : si vous n’avez besoin que d’un segment pour la production, utilisez batch_size=1 ; n’utilisez 4 que pour comparer plusieurs propositions, et n’activez pas 4 par défaut (coût multiplié par N).

Concurrence et débit

import asyncio
sem = asyncio.Semaphore(10)  # au plus 10 soumissions concurrentes côté client

async def submit_one(prompt):
    async with sem:
        return await submit({"prompt": prompt})
  • La plateforme impose une limite au nombre de soumissions par minute ; au-delà, elle renvoie 429, et il faut relancer avec backoff.
  • La concurrence réelle de génération est déterminée par la capacité du système ; au-delà, les tâches sont mises en file d’attente ; une tâche bloquée longtemps en SUBMITTED est généralement en file d’attente.
  • Le sondage doit impérativement inclure un sleep ; n’utilisez pas de boucle infinie sans sleep.

Recommandations de surveillance

MétriqueSeuil de référenceSignification
Taux de SUCCESS des tâches (dernière 1h)> 95%Un taux faible indique une anomalie du service / réseau
Durée moyenne d’achèvement< 90sUne durée élevée indique une file d’attente
Nombre de tâches bloquées en MODALproche de 0Un nombre élevé indique que le client n’appelle pas /modal
Proportion de code=24< 5%Une proportion élevée indique que le prompt déclenche fréquemment des mots sensibles

Liste de contrôle de dépannage

SymptômePiste de diagnostic
Tâche longtemps en SUBMITTEDSystème en file d’attente, consultez plus tard
Tâche longtemps en NOT_STARTLa plateforme effectuera automatiquement un timeout + remboursement plus tard, aucune action manuelle requise
Tâche MODAL au-delà de 30 minutesLe client n’a pas appelé /modal ; elle a déjà été automatiquement CANCEL + remboursée
Champ prompt videLe résultat textuel d’une tâche describe se trouve dans le champ description
Une image manque dans image_urlsLa modération de contenu a bloqué une partie des images, consultez fail_reason
Facturation supérieure aux attentesConsultez le champ quota ; pour la vidéo, n’oubliez pas × batch_size