# Python ile Midjourney API: görsel üretme ve kaydetme

> Python'da Midjourney görsel görevi oluşturun, durumunu sorgulayın ve görsel ızgarasını indirin. API'nin sunduğu işlemleri entegrasyondan önce görün.

- Canonical HTML: https://omniakey.com/tr/blog/midjourney-api-python
- Markdown: https://omniakey.com/tr/blog/midjourney-api-python.md
- Published: 2026-11-12 (updated 2026-11-12)
- Author: Kiki, COO at OmniaKey
- Language: tr
- Category: API setup
- Reading time: 8 min
- All posts: https://omniakey.com/blog/llms.txt

---

Bu örnek, OmniaKey'in üçüncü taraf [Midjourney](https://omniakey.com/tr/midjourney) API'sini Python'dan üç adımda çağırır: görevi oluştur, bitene kadar durumunu sorgula ve imzalı dosya URL'sinden PNG'yi indir. 12 Kasım 2026'daki entegrasyon testi 2048x2048 bir görseli 197,1 saniyede döndürdü ve bu görsel dört adaydan oluşan 2x2 bir ızgaradır. Aşağıdaki betik bir durum dosyası ekler; böylece zaman aşımı veya başarısız indirme ikinci bir ücretli görev oluşturmaz.

## Kısa yanıt

- `POST https://api.omniakey.com/v1/media/tasks` isteğini `Idempotency-Key` ile gönderin, ardından `GET /v1/media/tasks/{id}` ile durumu sorgulayın ve `assets[0].url` adresini indirin.
- Bir görev dört görsel içeren tek bir PNG ızgarası döndürür; dört ayrı dosya değil.
- Bu API'deki Midjourney yalnızca metinden görsele çalışır: düzenleme, referans görsel, blend, upscale, vary, kaydırma ve sürüm seçimi yok.
- Anahtar OmniaKey'den gelir ve Midjourney aboneliğinden ayrıdır. POST'tan önce gövdeyi ve idempotency anahtarını kaydedin, API isteği kabul edince görev kimliğini ekleyin ve daha sonra devam etmek için durum dosyasını saklayın. Yerel zaman aşımı sunucudaki görevi iptal etmez.

## Bu Python örneği hangi API'yi kullanıyor?

Örnek, OmniaKey'in Midjourney için sunduğu üçüncü taraf API'yi çağırır. OmniaKey Midjourney değildir ve OmniaKey anahtarı kendi bakiyesi olan ayrı bir hesaptır. Midjourney web sitesindeki abonelik ayrı bir üründür ve OmniaKey anahtarı oluşturmaz.

[Midjourney model sayfası](https://omniakey.com/tr/midjourney), OmniaKey tarafındaki modeli ve güncel ücreti gösterir. Geri kalan bölümler bu entegrasyonu ve görevlerin yaşam döngüsünü anlatır.

## Neye ihtiyacınız var?

Üç şey gerekir:

1. `OMNIAKEY_API_KEY` olarak dışa aktarılmış bir OmniaKey API anahtarı.
2. Python 3. Betik yalnızca standart kitaplığı kullanır, SDK kurmanız gerekmez.
3. Bu anahtar/hesap için `midjourney` erişimi. Model anahtarın listesinde yoksa oluşturma çağrısını çalıştırmadan önce hesap erişimini kontrol edin.

```bash
export OMNIAKEY_API_KEY="your-key"
python3 midjourney_grid.py
```

Anahtarı betiğin ve günlüklerin dışında tutun. Kod anahtarı ortamdan bir kez okur.

## İstek gövdesi ve parametreler

Midjourney bir prompt ve üç parametre alır. Bot türü OmniaKey tarafında önceden ayarlıdır, bu yüzden gövde model sürümünü ve `botType` alanını atlayabilir.

|Alan|Değer|Not|
|-|-|-|
|`input.prompt`|Metin|İstediğiniz sahne|
|`parameters.aspectRatio`|`1:1`|Ayrıca `16:9`, `9:16`, `3:2`, `2:3`|
|`parameters.quality`|`"1"`|Belgeler `"1"` dizesini kullanır|
|`parameters.n`|`1`|1 olmalı|

Biçimi piksel boyutuyla değil `aspectRatio` ile belirleyin. Alan camelCase'dir: `aspectRatio`, asla `aspect_ratio`. Listelenen beş değerden birini kullanın.

Örnek prompt için gövde şöyle görünür:

```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}
}
```

## Midjourney API için Python kodu

Bunu `midjourney_grid.py` olarak kaydedip çalıştırın. İlk çalıştırma POST'tan önce `midjourney-task.json` dosyasını yazar, OmniaKey isteği kabul edince görev kimliğini ekler.

```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()
```

İstek ve yanıt biçimleri 12 Kasım'daki entegrasyon testinden gelir; yeniden başlatma durumu burada eklenen kısımdır ve yaşam döngüsü dalları sahte yanıtlarla çevrimdışı kontrol edildi.

Durum dosyası devam etmek için gerekeni saklar: JSON gövdesini ve idempotency anahtarı olarak kullanılan UUID'yi. API anahtarı tutmaz. POST'tan önce yazıldığı için kopan bir bağlantı, kurtarma için iki değeri de diskte bırakır.

Oluşturma çağrısı iki başarı kodu döndürebilir. `202` yeni bir görevi kabul eder. `200`, OmniaKey'in aynı anahtarı ve aynı gövdeyi daha önce gördüğünü ve özgün görevi döndürdüğünü gösterir; o görevi sorgulamaya devam edin. Aynı gövdeyi ve anahtarı yalnızca bağlantı hatasından sonra oluşturma sonucu bilinmiyorsa yeniden gönderin. Belgeler değişen gövde için `409 idempotency_key_reused` listeler.

Sorgu döngüsünü `status` ile yönetin. Yüzde yaklaşık bir değerdir ve görev çalışırken 0'da kalabilir. `succeeded`, `failed` veya `canceled` durumunda durun. OmniaKey'in [medya görevleri yaşam döngüsü belgeleri](https://docs.omniakey.com/tr/media/overview) (12 Kasım 2026'da kontrol edildi) bu durumları açıklar.

Sorgu sınırı yereldir. Süre dolduğunda görev hâlâ çalışıyor veya kuyruktadır; aynı betiği yeniden çalıştırmak kayıtlı görev kimliğini okur ve sorgulamaya devam eder. Yerel zaman aşımı iptal değildir. İptal için ayrı bir endpoint vardır.

## Dönen Midjourney görsel ızgarası

Başarılı bir görevin tek dosyası vardır: bir PNG ızgarası. Aşağıdaki, 12 Kasım 2026 entegrasyon testinden alınan örnektir:

![Gün doğumunda mavi su üzerinde dört kırmızı katlanmış kâğıt kayık, sıcak yansıyan ışıkla 2x2 ızgara halinde](https://omniakey.com/_img/blog/midjourney-api-python/paper-boat-grid.03735622.1600.webp)

_12 Kasım 2026 entegrasyon testi örneği: bir midjourney görevi, bir 2048x2048 PNG, dört aday, oluşturmadan indirmeye 197,1 saniye. İndirilen baytlar görevin SHA-256 değeriyle eşleşti._

Aşağıdaki yanıt kısaltılmıştır: URL gizlenmiştir ve `expires_at` geçmiş bir değerdir. İhtiyacınız olan dosya alanlarını ekler:

```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…"
    }
  ]
}
```

2048 değerini sabitlemek yerine `width` ve `height` alanlarını dosyadan okuyun. 2048x2048 kare ve 197,1 saniye tek bir örnektir; onları böyle ele alın. Dört aday tek bir PNG'yi paylaşır, uygulamanız ayrı görseller istiyorsa ızgarayı indirdikten sonra bölün.

`url` imzalıdır ve kısa ömürlüdür, yaklaşık bir saat. Görev kaydı ve görsel, OmniaKey'in [görsel API belgelerine](https://docs.omniakey.com/tr/media/image) göre (12 Kasım 2026'da kontrol edildi) oluşturmadan sonra 24 saat saklanır. URL'nin süresi dolmadan indirin. Süresi dolduysa yeni imzalı URL için görev kimliğini tekrar sorgulayın.

Dosya URL'sini normal bir GET ile ve `Authorization` başlığı olmadan indirin. İmza URL'nin içindedir. Betik ayrıca indirilen baytları görevin `sha256` değeriyle karşılaştırır; böylece kesik bir dosya, iyi kopyanızın üzerine yazılmadan önce yakalanır.

## Zaman aşımı veya başarısız indirmeden sonra yeniden çalıştırma

Aynı komutu yeniden çalıştırın ve görevi koruyun. Betik `midjourney-task.json` dosyasını okur ve en kısa güvenli yolu seçer:

- Oluşturma doğrulanmadı: aynı gövdeyi aynı idempotency anahtarıyla yeniden POST eder.
- Görev kimliği var ve görev hâlâ çalışıyor: mevcut kimliği sorgular.
- Görev başarılı ama PNG yok: yeni imzalı URL için bir kez sorgular ve yeniden indirir.
- PNG zaten kayıtlı ve kaydedilen SHA-256 ile eşleşiyor: başka istek olmadan çıkar. Bozuk dosya yeniden sorgulanır ve indirilir.

Bu, 24 saatlik saklama penceresi içinde çalışır. Aynı anahtar farklı bir prompt veya parametreyle `409` verir. Betik, promptu artık eşleşmeyen durum dosyasını reddeder; parametreleri değiştirirseniz dosyayı başka bir yere taşıyın.

## Hatalar ve çözümler

Aşağıdaki hatalar API'nin görev ve idempotency kurallarından gelir. Nedeni düzeltin, uygunsa aynı durum dosyasıyla yeniden çalıştırın.

|Yanıt|Kod|Ne yapmalı|
|-|-|-|
|400|`missing_idempotency_key`|Başlığı ekleyin; 255 karakterin altında tutun|
|409|`idempotency_key_reused`|Özgün gövdeyi gönderin veya yeni anahtarla yeni bir görev başlatın|
|503|`no_available_channel`|Bu anahtar/hesap için erişimi kontrol edin; sürerse destekle iletişime geçin|
|`failed`|`error.code`, `error.message`|Görev hatasını okuyun; başarısız görevler iade edilir|
|`canceled`|—|Görev durdu; hâlâ gerekiyorsa yeni bir tane oluşturun|

`503 no_available_channel`, bir anahtarın model izni olmadığı anlamına gelmez. Bu grupta kullanılan bir anahtarla iki gönderim `503` döndürdü ve hiç görev oluşturmadı; ayrı bir anahtar aynı gün daha erken bir entegrasyon testinde başarılı oldu. Neden bilinmiyor: anahtar/hesap kullanılabilirliği veya üst akış kanalı kullanılabilirliği olabilir. `midjourney` modelinin mevcut anahtarınız/hesabınız için kullanılabilir olduğunu doğrulayın. `503` sürerse yanıt gövdesini ve istek kimliğini desteğe gönderin.

Oluşturmada `200`, özgün görevin aynen döndürülmesidir. Yalnızca `failed` ve `canceled` terminal hatalardır; betiği durdurmak görevi çalışır bırakır.

## Maliyet ve ne zaman başka bir görsel API'si kullanmalı

OmniaKey başarılı görev başına bir kez ücret alır; başarısız veya iptal edilen görevler otomatik iade edilir. Güncel ücret [Midjourney model sayfasında](https://omniakey.com/tr/midjourney); toplu bir işi planlamadan önce kontrol edin.

Midjourney, tek ızgarada dört varyasyon arasında seçim yapmak için uygundur. Bu entegrasyon şu anda metinden görsele ızgarayı döndürür; çağrı başına tek görsel veya kaynak sahneyi koruyan bir düzenleme için [GPT Image 2.5 API](https://omniakey.com/tr/blog/gpt-image-2-5-api) ya da [Nano Banana Pro API](https://omniakey.com/tr/blog/nano-banana-pro-api) sayfasına bakın.

Kod yazmak yerine prompt girmeyi tercih ederseniz [AI Image Generator](https://omniakey.com/tr/ai-image-generator) görsel modellerini tarayıcıda çalıştırır.

## Sık sorulan sorular

### Bu API'yi kullanmak için Midjourney aboneliği gerekli mi?

Hayır. Anahtar OmniaKey'den gelir ve çağrılar OmniaKey'in üçüncü taraf API'sine gider. OmniaKey Midjourney değildir; Midjourney web sitesindeki abonelik ayrı bir üründür ve OmniaKey anahtarı oluşturmaz.

### Bir Midjourney görevi neden dört görseli tek dosyada döndürür?

Midjourney dört adayı tek bir PNG olarak 2x2 ızgarada döndürür. Dördü bu dosyayı paylaşır ve bu entegrasyonda upscale ile vary yoktur; ayrı dosyalar gerekiyorsa indirdikten sonra ızgarayı kendiniz bölün.

### 409 idempotency\_key\_reused ne anlama gelir?

OmniaKey'in zaten bildiği bir idempotency anahtarı gönderdiniz ama istek gövdesi değişti. Özgün gövdeyi gönderin veya yeni anahtarla yeni bir görev başlatın. Aynı anahtarı farklı bir promptla kullanmak yaygın nedendir.

### Sorgulama sırasında betiğim zaman aşımına uğrarsa ne olur?

Görev çalışmaya devam eder. Görev kimliği `midjourney-task.json` içindedir; aynı betiği yeniden çalıştırmak o kimliği sorgular veya tamamlanan dosyayı indirir. Yerel zaman aşımı görevi ve ücreti olduğu gibi bırakır.
