GPT-Image 2.6 Flare dan Sunburst telah hadirMulai $0.025 per gambar

Panduan Midjourney API Python: buat dan simpan gambar

Buat tugas, periksa sampai selesai, lalu simpan satu grid PNG 2x2; kunci dan body tersimpan di disk sebelum POST.

Kiki, OmniaKey COO8 menit baca
Di halaman ini

Contoh ini memanggil API Midjourney pihak ketiga dari OmniaKey melalui Python dalam tiga langkah: buat tugas, periksa statusnya sampai selesai, lalu unduh PNG dari URL aset bertanda tangan. Uji integrasi 12 November 2026 mengembalikan satu gambar 2048x2048 dalam 197,1 detik, dan gambar itu adalah grid 2x2 berisi empat kandidat. Skrip di bawah menambahkan file status, sehingga timeout atau unduhan gagal tidak membuat tugas berbayar kedua.

Jawaban singkat

  • Kirim POST https://api.omniakey.com/v1/media/tasks dengan Idempotency-Key, lalu periksa GET /v1/media/tasks/{id} dan unduh assets[0].url.
  • Satu tugas mengembalikan satu grid PNG berisi empat gambar, bukan empat file terpisah.
  • Midjourney di API ini hanya teks ke gambar: tanpa edit, gambar referensi, blend, upscale, vary, geser, atau pemilih versi.
  • Kunci berasal dari OmniaKey dan terpisah dari langganan Midjourney. Simpan body dan kunci idempotensi sebelum POST, tambahkan id tugas setelah API menerima, dan pertahankan file status untuk melanjutkan nanti. Timeout lokal tidak membatalkan tugas di server.

API mana yang dipakai contoh Python ini?

Contoh memanggil API pihak ketiga OmniaKey untuk Midjourney. OmniaKey bukan Midjourney, dan kunci OmniaKey adalah akun terpisah dengan saldo sendiri. Langganan di situs Midjourney adalah produk lain dan tidak membuat kunci OmniaKey.

Halaman model Midjourney menampilkan model dan tarif saat ini di sisi OmniaKey. Bagian berikutnya membahas integrasi ini dan siklus hidup tugasnya.

Yang Anda butuhkan

Anda membutuhkan tiga hal:

  1. Kunci API OmniaKey, diekspor sebagai OMNIAKEY_API_KEY.
  2. Python 3. Skrip hanya memakai pustaka standar, jadi tidak ada SDK yang perlu dipasang.
  3. midjourney tersedia untuk kunci/akun itu. Jika model tidak ada di daftar kunci, periksa akses akun sebelum menjalankan panggilan pembuatan.
bash
export OMNIAKEY_API_KEY="your-key"
python3 midjourney_grid.py

Jauhkan kunci dari skrip dan log. Kode membacanya sekali dari environment.

Body permintaan dan parameter

Midjourney menerima prompt dan tiga parameter. Tipe bot sudah diatur di sisi OmniaKey, jadi body bisa melewati versi model dan botType.

KolomNilaiCatatan
input.promptTeksAdegan yang diinginkan
parameters.aspectRatio1:1Juga 16:9, 9:16, 3:2, 2:3
parameters.quality"1"Dokumentasi memakai string "1"
parameters.n1Harus 1

Tentukan bentuk dengan aspectRatio, bukan ukuran piksel. Kolomnya camelCase: aspectRatio, bukan aspect_ratio. Pakai salah satu dari lima nilai yang terdaftar.

Body untuk prompt contoh terlihat seperti ini:

json
{
  "model": "midjourney",
  "type": "image",
  "operation": "generate",
  "input": {
    "prompt": "A small red paper boat floating on a quiet blue lake at sunrise, cinematic lighting, elegant composition, no text"
  },
  "parameters": {"aspectRatio": "1:1", "quality": "1", "n": 1}
}

Memanggil Midjourney API dari Python

Simpan sebagai midjourney_grid.py dan jalankan. Eksekusi pertama menulis midjourney-task.json sebelum POST, lalu menambahkan id tugas setelah OmniaKey menerima permintaan.

python
import hashlib
import json
import os
import sys
import time
import urllib.error
import urllib.request
import uuid
from pathlib import Path

API = "https://api.omniakey.com/v1"
KEY = os.environ["OMNIAKEY_API_KEY"]
STATE = Path("midjourney-task.json")
OUT = Path("midjourney-grid.png")
PROMPT = ("A small red paper boat floating on a quiet blue lake at sunrise, "
          "cinematic lighting, elegant composition, no text")


def save(state):
    tmp = STATE.with_suffix(".tmp")
    tmp.write_text(json.dumps(state, indent=2))
    tmp.replace(STATE)


def read_json(response):
    raw = response.read()
    try:
        return json.loads(raw)
    except ValueError:
        return {"raw": raw[:300].decode("utf-8", "replace")}


def call(method, path, body=None, headers=None):
    data = json.dumps(body).encode() if body is not None else None
    req = urllib.request.Request(
        API + path, data=data, method=method,
        headers={"Authorization": f"Bearer {KEY}", **(headers or {})})
    if body is not None:
        req.add_header("Content-Type", "application/json")
    try:
        with urllib.request.urlopen(req, timeout=60) as response:
            return response.status, read_json(response)
    except urllib.error.HTTPError as error:
        return error.code, read_json(error)
    except (urllib.error.URLError, TimeoutError) as error:
        return None, {"error": str(error)}


def main():
    state = json.loads(STATE.read_text()) if STATE.exists() else None
    if state is None:
        body = {
            "model": "midjourney", "type": "image", "operation": "generate",
            "input": {"prompt": PROMPT},
            "parameters": {"aspectRatio": "1:1", "quality": "1", "n": 1},
        }
        state = {"idempotency_key": str(uuid.uuid4()), "body": body,
                 "task_id": None, "status": "creating"}
        save(state)  # no API key here; write before POST
    elif state["body"]["input"]["prompt"] != PROMPT:
        sys.exit("The state file holds a different prompt. Move it aside to start a new job.")

    if OUT.exists() and state.get("status") == "downloaded":
        saved_sha = state.get("download_sha")
        if not saved_sha or hashlib.sha256(OUT.read_bytes()).hexdigest() == saved_sha:
            print("already saved", OUT)
            return

    if not state["task_id"]:
        code, task = call("POST", "/media/tasks", state["body"],
                          {"Idempotency-Key": state["idempotency_key"]})
        if code is None:
            sys.exit(f"Create not confirmed: {task['error']}. Rerun to retry the same key and body.")
        if code == 409:
            sys.exit("409 idempotency_key_reused: same key, different body. Restore the saved body.")
        if code == 503:
            sys.exit("503 no_available_channel: check model access for this key/account; "
                     "contact support with the request id if it persists.")
        if code not in (200, 202) or "id" not in task:
            sys.exit(f"Create failed: HTTP {code} {task}")
        state.update(task_id=task["id"], status=task["status"])
        save(state)

    deadline = time.monotonic() + 900
    while True:
        code, task = call("GET", f"/media/tasks/{state['task_id']}")
        if code is None:
            sys.exit(f"Polling stopped: {task['error']}. The job is still saved; rerun this script.")
        if code != 200 or "status" not in task:
            sys.exit(f"Poll failed: HTTP {code} {task}")
        state["status"] = task["status"]
        save(state)
        if task["status"] in ("failed", "canceled"):
            sys.exit(f"Job {task['status']}: {task.get('error')}")
        if task["status"] == "succeeded":
            break
        if time.monotonic() > deadline:
            sys.exit(f"Still {task['status']} after 900 s. Rerun with the same state file.")
        time.sleep(min(8, 2 + task.get("progress", {}).get("percent", 0) // 20))

    asset = task["assets"][0]
    if asset.get("mime_type") != "image/png":
        sys.exit(f"Expected a PNG grid, got {asset.get('mime_type')}")
    try:
        with urllib.request.urlopen(asset["url"], timeout=120) as response:
            data = response.read()  # signed URL: no Authorization header
    except (urllib.error.HTTPError, urllib.error.URLError, TimeoutError) as error:
        sys.exit(f"Download failed: {error}. Rerun to get a fresh signed URL and retry.")
    digest = hashlib.sha256(data).hexdigest()
    if asset.get("sha256") and digest != asset["sha256"]:
        sys.exit("SHA-256 mismatch. Rerun to download the asset again.")
    part = OUT.with_suffix(".part")
    part.write_bytes(data)
    part.replace(OUT)
    state.update(status="downloaded", download_sha=digest)
    save(state)
    print("saved", OUT, len(data), "bytes")


if __name__ == "__main__":
    main()

Bentuk permintaan dan respons berasal dari uji integrasi 12 November; status mulai ulang adalah tambahan di sini, dan cabang siklus hidupnya diperiksa offline dengan respons tiruan.

File status menyimpan yang Anda butuhkan untuk melanjutkan: body JSON dan UUID yang dipakai sebagai kunci idempotensi. File ini tidak menyimpan kunci API. File ditulis sebelum POST, jadi koneksi yang terputus meninggalkan kedua nilai di disk untuk pemulihan.

Panggilan pembuatan bisa mengembalikan dua kode sukses. 202 menerima tugas baru. 200 berarti OmniaKey sudah melihat kunci dan body yang sama dan mengembalikan tugas asli; lanjutkan memeriksa tugas itu. Kirim lagi body dan kunci yang sama hanya jika hasil pembuatan tidak diketahui setelah koneksi gagal. Dokumentasi mencantumkan 409 idempotency_key_reused untuk body yang berubah.

Kendalikan loop pemeriksaan dengan status. Persentase bersifat perkiraan dan bisa tetap 0 selama tugas berjalan. Berhenti pada succeeded, failed, atau canceled. Dokumentasi siklus hidup tugas media OmniaKey (diperiksa 12 November 2026) menjelaskan status tersebut.

Batas pemeriksaan bersifat lokal. Saat batas habis, tugas masih berjalan atau antre; menjalankan skrip yang sama lagi akan membaca id tersimpan dan melanjutkan pemeriksaan. Timeout lokal bukan pembatalan. Pembatalan memakai endpoint terpisah.

Grid gambar Midjourney yang dikembalikan

Tugas yang berhasil punya satu aset: grid PNG. Ini sampel dari uji integrasi 12 November 2026:

Empat perahu kertas merah terlipat di atas air biru saat matahari terbit, tersusun dalam grid 2x2 dengan pantulan cahaya hangat
Sampel uji integrasi 12 November 2026: satu tugas midjourney, satu PNG 2048x2048, empat kandidat, 197,1 detik dari pembuatan sampai unduhan. Byte yang diunduh cocok dengan SHA-256 tugas.

Respons di bawah dipersingkat: URL disamarkan dan expires_at adalah nilai historis. Respons ini menambahkan kolom aset yang Anda butuhkan:

json
{
  "id": "task_DbCWJPfPL1jyhSD38IihNHiiFQao8hjV",
  "status": "succeeded",
  "progress": {"percent": 100, "stage": "completed"},
  "assets": [
    {
      "kind": "image",
      "mime_type": "image/png",
      "width": 2048,
      "height": 2048,
      "byte_size": 5834301,
      "sha256": "f503cef038d15fa150b36b5fe7c41874febad3aa1167698d790781e47a4a10b9",
      "expires_at": "2026-11-12T04:16:41Z",
      "url": "https://…redacted-signed-url…"
    }
  ]
}

Baca width dan height dari aset, jangan menetapkan 2048 secara hard-code. Persegi 2048x2048 dan 197,1 detik adalah satu sampel; perlakukan seperti itu. Keempat kandidat berbagi satu PNG, jadi potong grid setelah diunduh jika aplikasi Anda butuh gambar terpisah.

url bertanda tangan dan berumur pendek, sekitar satu jam. Catatan tugas dan gambar disimpan 24 jam setelah pembuatan, menurut dokumentasi API gambar OmniaKey (diperiksa 12 November 2026). Unduh sebelum URL kedaluwarsa. Jika sudah kedaluwarsa, periksa id tugas lagi untuk mendapatkan URL bertanda tangan baru.

Unduh URL aset dengan GET biasa dan tanpa header Authorization. Tanda tangannya ada di URL. Skrip juga membandingkan byte yang diunduh dengan sha256 tugas, sehingga file terpotong terdeteksi sebelum menimpa salinan bagus Anda.

Menjalankan ulang setelah timeout atau unduhan gagal

Jalankan perintah yang sama dan pertahankan tugas. Skrip membaca midjourney-task.json dan memilih jalur aman terpendek:

  • Pembuatan belum terkonfirmasi: skrip mengirim POST body yang sama dengan kunci idempotensi yang sama.
  • Id tugas ada dan tugas masih berjalan: skrip memeriksa id yang ada.
  • Tugas berhasil tetapi PNG hilang: skrip memeriksa sekali untuk URL bertanda tangan baru dan mengunduh lagi.
  • PNG sudah tersimpan dan cocok dengan SHA-256 yang dicatat: skrip keluar tanpa permintaan lain. File rusak diperiksa dan diunduh lagi.

Ini berfungsi dalam jendela penyimpanan 24 jam. Kunci yang sama dengan prompt atau parameter berbeda menghasilkan 409. Skrip menolak file status yang prompt-nya tidak lagi cocok; pindahkan file jika Anda mengubah parameter.

Error dan perbaikan

Kegagalan berikut berasal dari aturan tugas dan idempotensi API. Perbaiki penyebabnya, lalu jalankan ulang dengan file status yang sama bila berlaku.

ResponsKodeYang harus dilakukan
400missing_idempotency_keyTambahkan header; jaga di bawah 255 karakter
409idempotency_key_reusedKirim body asli atau mulai tugas baru dengan kunci baru
503no_available_channelPeriksa akses untuk kunci/akun ini; hubungi dukungan jika berlanjut
failederror.code, error.messageBaca error tugas; tugas gagal dikembalikan dananya
canceled—Tugas berhenti; buat yang baru jika masih perlu

503 no_available_channel tidak berarti kunci tidak punya izin model. Dua pengiriman dengan satu kunci yang dipakai di kelompok ini mengembalikan 503 dan tidak membuat tugas; kunci lain berhasil dalam uji integrasi lebih awal pada hari yang sama. Penyebabnya tidak diketahui: bisa jadi ketersediaan kunci/akun atau ketersediaan kanal upstream. Pastikan midjourney tersedia untuk kunci/akun Anda saat ini. Jika 503 berlanjut, kirim body respons dan id permintaan ke dukungan.

200 saat pembuatan adalah tugas asli yang dikembalikan lagi. Hanya failed dan canceled yang merupakan kegagalan terminal; menghentikan skrip membiarkan tugas tetap berjalan.

Biaya dan kapan memakai API gambar lain

OmniaKey menagih sekali per tugas yang berhasil, dan tugas gagal atau dibatalkan dikembalikan dananya secara otomatis. Tarif saat ini ada di halaman model Midjourney; periksa sebelum memperkirakan satu kelompok.

Midjourney cocok untuk memilih di antara empat variasi dalam satu grid. Integrasi ini saat ini mengembalikan grid teks ke gambar tersebut; untuk satu gambar per panggilan atau edit yang mempertahankan adegan sumber, lihat GPT Image 2.5 API atau Nano Banana Pro API.

Jika Anda lebih suka mengetik prompt daripada menulis kode, AI Image Generator menjalankan model gambar di browser.

Pertanyaan umum

Apakah saya perlu langganan Midjourney untuk memakai API ini?

Tidak. Kunci berasal dari OmniaKey dan panggilan ditujukan ke API pihak ketiga OmniaKey. OmniaKey bukan Midjourney; langganan di situs Midjourney adalah produk lain dan tidak membuat kunci OmniaKey.

Mengapa satu tugas Midjourney mengembalikan empat gambar dalam satu file?

Midjourney mengembalikan grid 2x2 berisi empat kandidat sebagai satu PNG. Keempatnya berbagi file itu, dan upscale serta vary tidak tersedia di integrasi ini, jadi potong grid sendiri setelah mengunduh jika butuh file terpisah.

Apa arti 409 idempotency_key_reused?

Anda mengirim kunci idempotensi yang sudah dimiliki OmniaKey, tetapi body permintaan berubah. Kirim body asli atau mulai tugas baru dengan kunci baru. Memakai ulang kunci dengan prompt berbeda adalah penyebab umum.

Apa yang terjadi jika skrip saya timeout saat memeriksa status?

Tugas tetap berjalan. Id tugas ada di midjourney-task.json, jadi menjalankan skrip yang sama lagi akan memeriksa id itu atau mengunduh aset yang sudah selesai. Timeout lokal membiarkan tugas hidup dan biaya tetap berlaku.

Model di artikel ini

Midjourney APIPythonAPI gambargrid 2x2tutorial API

Artikel terkait

Artikel terbaru

Panduan populer