Langsung ke konten utama
Kumpulan praktik terbaik untuk pertanyaan umum, optimasi performa, dan penanganan error, disarankan dibaca menyeluruh sebelum integrasi.

Pengiriman tugas dan polling

Semua endpoint pengiriman bersifat asinkron: setelah dikirim akan mengembalikan task_id, lalu query status secara berkala via GET /v1/midjourney/{task_id} hingga 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} perlu memanggil /modal untuk melengkapi parameter")
        time.sleep(3)
    raise TimeoutError(task_id)
  • Ritme polling: disarankan setiap 3–5 detik, frekuensi lebih tinggi tidak berguna dan memboroskan kuota.
  • Jangan memblokir secara sinkron di dalam request web menunggu tugas selesai — setelah dikirim langsung kembalikan task_id, biarkan frontend melakukan polling secara asinkron.

Desain prompt

Prompt yang baik:
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
  • Subjek di depan: subjek dulu, lalu deskripsi scene, terakhir kata pemodifikasi.
  • Parameter terstruktur eksplisit: gunakan --ar / --v / --s (atau field body yang sesuai) lebih terkendali daripada mengandalkan nilai default.
  • Hindari kata ambigu: photorealistic lebih jelas daripada realistic.
Hindari: terlalu abstrak (“make it good”), subjek berserakan (beberapa objek sejajar tanpa prioritas), memberi tanda kutip pada kata (akan dianggap nilai literal). Niji anime: kirim niji: true + version: "7", platform menormalisasinya menjadi --niji 7, penagihan melalui midjourney@imagine-niji7.

Praktik terbaik gambar referensi

SumberPraktik yang direkomendasikanCatatan
Upload penggunaSimpan dulu ke OSS / CDN Anda sendiri, kirim URL tersebut saat submitJangan kirim base64 langsung (memboroskan bandwidth)
URL publikKirim langsungPerhatikan SSRF (harus dapat diakses publik) dan batas 12 MiB
Pihak ketiga / produk lainSimpan dulu ke OSS Anda sendiriURL pihak ketiga bisa kedaluwarsa
  • Kompres hingga < 5 MiB: batas platform 12 MiB, tetapi gambar kecil lebih cepat ditransfer / diproses.
  • Format PNG / JPG / WebP semuanya bisa, direkomendasikan JPG berkualitas tinggi.
  • Resolusi 1024–2048 px sudah cukup, lebih tinggi hanya pemborosan.
  • Bobot gambar referensi iw (0–3, default 1): >1 lebih dekat ke gambar asli, <1 lebih bebas.

Penanganan error dan strategi retry

codeArtiStrategi retry
1 / 200Sukses
4 VALIDATION_ERRORParameter salah❌ Jangan retry, perbaiki parameter
3 NOT_FOUNDTidak ada instance tersedia / task_id tidak adaJika instance tidak tersedia bisa retry nanti; jika task_id tidak ada jangan retry
9 FAILUREDitolak layanan / error internal⏳ Bisa retry, exponential backoff (1s, 4s, 16s)
21 MODALBukan status final✅ Lanjutkan memanggil /modal
24 BANNED_PROMPTKata sensitif❌ Jangan retry, ubah prompt; sudah otomatis di-refund
429Rate limit⏳ Exponential backoff + jitter
5xx / error jaringanServer / jaringan⏳ Exponential backoff, error jaringan bisa retry langsung 1 kali
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"])      # tidak bisa di-retry
            if data["code"] == 3 and "task" in data["description"]:
                raise ValueError(data["description"])      # task_id tidak ada
            # sisanya (9 / 429 / 5xx) bisa di-retry
        except httpx.RequestError:
            pass
        time.sleep((4 ** attempt) + random.uniform(0, 1))  # 1s / 4s / 16s ...
    raise RuntimeError(f"mencapai jumlah retry maksimum {max_attempts}")

Alur operasi sekunder

# 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 digabung secara lokal, 1–2s
single_image = final["image_urls"][0]
Inpaint (inpaint → modal, dua langkah):
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 menggambar mask (putih=area inpaint), upload ke OSS sendiri untuk dapat 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"])
⚠️ Setelah inpaint masuk MODAL, /modal harus dipanggil dalam 30 menit, jika tidak backend otomatis CANCEL + refund.

Kontrol penagihan video

  • Segmen tunggal: batch_size: 1 → menagih 1 × midjourney@video
  • Batch 4 segmen: batch_size: 4 → menagih 4 × midjourney@video
  • HD segmen tunggal: video_type: "vid_1.1_i2v_720" + batch_size: 1 → menagih 1 × midjourney@video-720p
Saran: jika hanya butuh 1 segmen gunakan batch_size=1, gunakan 4 hanya untuk perbandingan batch, jangan default membuka 4 (biaya berlipat N kali).

Konkurensi dan throughput

import asyncio
sem = asyncio.Semaphore(10)  # klien maksimal 10 submit konkuren

async def submit_one(prompt):
    async with sem:
        return await submit({"prompt": prompt})
  • Platform memiliki batas jumlah submit per menit, jika terlampaui mengembalikan 429, perlu backoff retry.
  • Konkurensi generasi aktual ditentukan oleh kapasitas sistem, jika terlampaui akan antre; tugas yang lama berhenti di SUBMITTED biasanya sedang antre.
  • Polling harus disertai sleep, jangan loop tanpa henti tanpa sleep.

Saran monitoring

MetrikAmbang referensiArti
Rasio SUCCESS tugas (1 jam terakhir)> 95%Terlalu rendah menandakan anomali layanan / jaringan
Rata-rata waktu penyelesaian< 90sTerlalu tinggi menandakan antrean
Jumlah tugas tertahan di MODALmendekati 0Terlalu banyak menandakan klien tidak memanggil /modal
Rasio code=24< 5%Terlalu tinggi menandakan prompt sering memicu kata sensitif

Daftar troubleshooting

GejalaArah pemeriksaan
Tugas lama SUBMITTEDSistem sedang antre, cek lagi nanti
Tugas lama NOT_STARTPlatform akan otomatis timeout + refund nanti, tidak perlu penanganan manual
Tugas MODAL lebih dari 30 menitKlien tidak memanggil /modal, sudah otomatis CANCEL + refund
Field prompt kosongHasil teks tugas describe ada di field description
image_urls kurang satu gambarSebagian gambar diblokir review konten, lihat fail_reason
Penagihan melebihi ekspektasiLihat field quota; video ingat × batch_size