Zum Hauptinhalt springen
Zusammenfassung der Best Practices für häufige Probleme, Performance-Optimierung und Fehlerbehandlung, vor der Anbindung empfohlen vollständig durchzulesen.

Aufgabenübermittlung und Polling

Die Übermittlungs-Endpunkte sind allesamt asynchrone Aufgaben: Nach dem Absenden wird eine task_id zurückgegeben; danach wird über GET /v1/midjourney/{task_id} periodisch der Status abgefragt, bis SUCCESS / FAILURE erreicht ist.
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} muss über /modal vervollständigt werden")
        time.sleep(3)
    raise TimeoutError(task_id)
  • Polling-Takt: Empfohlen alle 3–5 s; höhere Frequenz ist sinnlos und verschwendet Kontingent.
  • Nicht synchron im Web-Request blockieren, bis die Aufgabe fertig ist – nach dem Absenden sofort die task_id zurückgeben und das Frontend asynchron pollen lassen.

Prompt-Design

Guter 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
  • Motiv zuerst: erst das Motiv, dann die Szene beschreiben, zuletzt die Modifikatoren.
  • Strukturierte Parameter explizit angeben: --ar / --v / --s (oder die entsprechenden Body-Felder) zu verwenden ist kontrollierbarer als auf Standardwerte zu vertrauen.
  • Mehrdeutige Wörter vermeiden: photorealistic ist eindeutiger als realistic.
Vermeiden: zu abstrakte Angaben („make it good”), zerstreute Motive (mehrere gleichrangige Objekte ohne klare Priorität), Wörter in Anführungszeichen setzen (werden als Literalwert interpretiert). Niji-Anime: niji: true + version: "7" übergeben; die Plattform normalisiert dies zu --niji 7, die Abrechnung läuft über midjourney@imagine-niji7.

Best Practices für bildgesteuerte Generierung

QuelleEmpfohlene VorgehensweiseHinweis
Benutzer-UploadZuerst im eigenen OSS / CDN speichern, beim Absenden diese URL übergebenNicht direkt base64 übergeben (verschwendet Bandbreite)
Öffentliche URLDirekt übergebenSSRF beachten (muss öffentlich erreichbar sein) sowie das 12-MiB-Limit
Drittanbieter / andere ArtefakteZuerst ins eigene OSS umlagernDrittanbieter-URLs können ablaufen
  • Auf < 5 MiB komprimieren: Das Plattform-Limit liegt bei 12 MiB, aber kleinere Bilder werden schneller übertragen und verarbeitet.
  • Die Formate PNG / JPG / WebP sind alle möglich, empfohlen wird hochwertiges JPG.
  • Eine Auflösung von 1024–2048 px reicht aus; höher ist Verschwendung.
  • Bildgewicht iw (0–3, Standard 1): >1 näher am Original, <1 freier.

Fehlerbehandlung und Retry-Strategie

codeBedeutungRetry-Strategie
1 / 200Erfolg
4 VALIDATION_ERRORParameterfehler❌ Nicht erneut versuchen, Parameter korrigieren
3 NOT_FOUNDKeine verfügbare Instanz / task_id existiert nichtBei nicht verfügbarer Instanz später erneut versuchen; bei nicht existierender task_id nicht erneut versuchen
9 FAILUREAblehnung durch den Dienst / interner Fehler⏳ Erneut versuchbar, exponentielles Backoff (1s, 4s, 16s)
21 MODALNicht-Endzustand✅ Weiter /modal aufrufen
24 BANNED_PROMPTSensibles Wort❌ Nicht erneut versuchen, Prompt ändern; bereits automatisch erstattet
429Drosselung⏳ Exponentielles Backoff + Jitter
5xx / NetzwerkfehlerServerseitig / Netzwerk⏳ Exponentielles Backoff; bei Netzwerkfehler 1-mal sofort erneut versuchbar
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"])      # nicht erneut versuchbar
            if data["code"] == 3 and "task" in data["description"]:
                raise ValueError(data["description"])      # task_id existiert nicht
            # die übrigen (9 / 429 / 5xx) sind erneut versuchbar
        except httpx.RequestError:
            pass
        time.sleep((4 ** attempt) + random.uniform(0, 1))  # 1s / 4s / 16s ...
    raise RuntimeError(f"maximale Anzahl an Wiederholungen erreicht {max_attempts}")

Ablauf von Folgeoperationen

# imagine → Polling → 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 lokal zusammengesetzt, 1–2s
single_image = final["image_urls"][0]
Lokales Neuzeichnen (inpaint → modal, zwei Schritte):
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
# Frontend zeichnet die Maske (weiß = Neuzeichnungsbereich), lädt sie ins eigene OSS hoch und erhält die 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"])
⚠️ Nachdem inpaint in MODAL übergegangen ist, muss /modal innerhalb von 30 Minuten aufgerufen werden, sonst wird im Hintergrund automatisch CANCEL + Erstattung ausgelöst.

Abrechnungssteuerung bei video

  • Einzelclip: batch_size: 1 → Abzug 1 × midjourney@video
  • Stapel von 4 Clips: batch_size: 4 → Abzug 4 × midjourney@video
  • HD-Einzelclip: video_type: "vid_1.1_i2v_720" + batch_size: 1 → Abzug 1 × midjourney@video-720p
Empfehlung: Wenn nur 1 Clip benötigt wird, batch_size=1 verwenden; 4 nur für Stapel-Entwürfe nutzen und nicht standardmäßig auf 4 stellen (vervielfacht die Kosten um das N-fache).

Nebenläufigkeit und Durchsatz

import asyncio
sem = asyncio.Semaphore(10)  # clientseitig maximal 10 gleichzeitige Übermittlungen

async def submit_one(prompt):
    async with sem:
        return await submit({"prompt": prompt})
  • Die Plattform hat eine Obergrenze für die Anzahl der Übermittlungen pro Minute; bei Überschreitung wird 429 zurückgegeben und ein Retry mit Backoff ist nötig.
  • Die tatsächliche Generierungs-Nebenläufigkeit wird durch die Systemkapazität bestimmt; bei Überschreitung wird die Aufgabe in eine Warteschlange gestellt; bleibt eine Aufgabe lange bei SUBMITTED, liegt das meist an der Warteschlange.
  • Beim Polling unbedingt sleep einbauen; keine Endlosschleife ohne sleep.

Monitoring-Empfehlungen

KennzahlReferenzschwelleBedeutung
SUCCESS-Rate der Aufgaben (letzte 1 h)> 95%Niedrig deutet auf Dienst-/Netzwerkanomalien hin
Durchschnittliche Fertigstellungsdauer< 90sHoch deutet auf eine Warteschlange hin
Anzahl in MODAL verweilender Aufgabennahe 0Viele deuten darauf hin, dass der Client /modal nicht aufgerufen hat
Anteil code=24< 5%Hoch deutet darauf hin, dass der Prompt häufig sensible Wörter auslöst

Checkliste zur Fehlerbehebung

SymptomUntersuchungsrichtung
Aufgabe lange bei SUBMITTEDSystem in der Warteschlange, später erneut abfragen
Aufgabe lange bei NOT_STARTDie Plattform löst später automatisch einen Timeout mit Erstattung aus, kein manuelles Eingreifen nötig
Aufgabe MODAL über 30 MinutenClient hat /modal nicht aufgerufen, wurde bereits automatisch CANCEL + Erstattung
Feld prompt leerDas Textergebnis einer describe-Aufgabe steht im Feld description
Ein Bild fehlt in image_urlsInhaltsprüfung hat einen Teil der Bilder blockiert, siehe fail_reason
Abrechnung höher als erwartetFeld quota prüfen; bei video an × batch_size denken