GPT-Image 2.6 Flare e Sunburst chegaramA partir de $0.025 por imagem

API do Midjourney em Python: gerar e salvar imagens

Crie a tarefa, consulte até concluir e salve uma grade PNG 2x2; a chave e o corpo ficam no disco antes do POST.

Kiki, OmniaKey COO8 min de leitura
Nesta página

Este exemplo usa Python para chamar a API do Midjourney da OmniaKey, um serviço de terceiros, em três etapas: criar a tarefa, consultar o status até concluir e baixar o PNG da URL assinada do arquivo. Um teste de integração em 12 de novembro de 2026 retornou uma imagem 2048x2048 em 197,1 segundos, e essa imagem é uma grade 2x2 com quatro candidatos. O script abaixo adiciona um arquivo de estado, então um tempo esgotado ou um download com falha não cria uma segunda tarefa paga.

Resposta curta

  • Envie POST https://api.omniakey.com/v1/media/tasks com um Idempotency-Key, depois consulte GET /v1/media/tasks/{id} e baixe assets[0].url.
  • Uma tarefa retorna uma grade PNG com quatro imagens, não quatro arquivos separados.
  • O Midjourney nesta API faz só texto para imagem: sem edição, imagem de referência, blend, upscale, vary, deslocamento ou seletor de versão.
  • A chave vem da OmniaKey e é separada de uma assinatura do Midjourney. Salve o corpo e a chave de idempotência antes do POST, adicione o id da tarefa quando a API aceitar e mantenha o arquivo de estado para retomar depois. Um tempo esgotado local não cancela a tarefa no servidor.

Qual API este exemplo de Python usa?

O exemplo chama a API de terceiros da OmniaKey para o Midjourney. A OmniaKey não é o Midjourney, e uma chave OmniaKey é uma conta separada com saldo próprio. Uma assinatura no site do Midjourney é outro produto e não cria uma chave OmniaKey.

A página do modelo Midjourney mostra o modelo e a tarifa atual do lado da OmniaKey. O restante do passo a passo cobre esta integração e o ciclo de vida das tarefas.

O que você precisa

Você precisa de três coisas:

  1. Uma chave de API da OmniaKey, exportada como OMNIAKEY_API_KEY.
  2. Python 3. O script usa só a biblioteca padrão, então não há SDK para instalar.
  3. midjourney disponível para essa chave/conta. Se o modelo não aparecer na lista da chave, confira o acesso da conta antes de executar a chamada de criação.
bash
export OMNIAKEY_API_KEY="your-key"
python3 midjourney_grid.py

Mantenha a chave fora do script e dos logs. O código a lê uma vez do ambiente.

Corpo da requisição e parâmetros

O Midjourney recebe um prompt e três parâmetros. O tipo de bot é predefinido do lado da OmniaKey, então o corpo pode omitir a versão do modelo e botType.

CampoValorObservações
input.promptTextoA cena desejada
parameters.aspectRatio1:1Também 16:9, 9:16, 3:2, 2:3
parameters.quality"1"A documentação usa a string "1"
parameters.n1Precisa ser 1

Defina o formato com aspectRatio, não com um tamanho em pixels. O campo é camelCase: aspectRatio, nunca aspect_ratio. Use um dos cinco valores listados.

O corpo para o prompt de exemplo fica assim:

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

Chamar a API do Midjourney em Python

Salve como midjourney_grid.py e execute. A primeira execução grava midjourney-task.json antes do POST e adiciona o id da tarefa quando a OmniaKey aceita a requisição.

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

As formas da requisição e da resposta vêm do teste de integração de 12 de novembro; o estado de reinício é a adição aqui, e seus caminhos de ciclo de vida foram verificados offline com respostas simuladas.

O arquivo de estado guarda o que você precisa para continuar: o corpo JSON e o UUID usado como chave de idempotência. Não guarda nenhuma chave de API. É gravado antes do POST, então uma conexão interrompida deixa os dois valores no disco para recuperação.

A chamada de criação pode retornar dois códigos de sucesso. Um 202 aceita uma tarefa nova. Um 200 significa que a OmniaKey já viu a mesma chave e o mesmo corpo e está devolvendo a tarefa original; continue consultando essa tarefa. Envie o mesmo corpo e a chave de novo só quando o resultado da criação for desconhecido após uma falha de conexão. A documentação lista 409 idempotency_key_reused para um corpo alterado.

Controle o loop de consulta por status. A porcentagem é aproximada e pode ficar em 0 enquanto a tarefa roda. Pare em succeeded, failed ou canceled. A documentação do ciclo de vida de tarefas de mídia da OmniaKey (verificada em 12 de novembro de 2026) descreve esses estados.

O limite de consultas é local. Quando ele vence, a tarefa ainda está rodando ou na fila; executar o mesmo script de novo lê o id salvo e continua a consulta. Um tempo esgotado local não é cancelamento. O cancelamento usa outro endpoint.

A grade de imagens do Midjourney retornada

Uma tarefa concluída tem um único arquivo: uma grade PNG. Esta é a amostra do teste de integração de 12 de novembro de 2026:

Quatro barcos de papel vermelhos dobrados sobre água azul ao nascer do sol, dispostos em uma grade 2x2 com luz quente refletida
Amostra do teste de integração de 12 de novembro de 2026: uma tarefa midjourney, um PNG 2048x2048, quatro candidatos, 197,1 segundos da criação ao download. Os bytes baixados corresponderam ao SHA-256 da tarefa.

A resposta abaixo está resumida: a URL está oculta e expires_at é histórico. Ela adiciona os campos de arquivo necessários:

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

Leia width e height do arquivo em vez de fixar 2048. O quadrado 2048x2048 e os 197,1 segundos são uma amostra; trate-os assim. Os quatro candidatos compartilham um PNG, então divida a grade após o download se o app precisar de imagens separadas.

A url é assinada e dura pouco, cerca de uma hora. O registro da tarefa e a imagem ficam guardados por 24 horas após a criação, segundo a documentação da API de imagens da OmniaKey (verificada em 12 de novembro de 2026). Baixe antes de a URL expirar. Se ela expirou, consulte o id da tarefa de novo para obter uma URL assinada nova.

Baixe a URL do arquivo com um GET comum e sem cabeçalho Authorization. A assinatura está na URL. O script também compara os bytes baixados com o sha256 da tarefa, o que detecta um arquivo truncado antes de sobrescrever sua cópia boa.

Executar de novo após tempo esgotado ou download com falha

Execute o mesmo comando e mantenha a tarefa. O script lê midjourney-task.json e escolhe o caminho seguro mais curto:

  • Criação não confirmada: ele faz POST do mesmo corpo com a mesma chave de idempotência.
  • O id existe e a tarefa ainda roda: ele consulta o id existente.
  • A tarefa concluiu mas o PNG não está lá: ele consulta uma vez para obter uma URL assinada nova e baixa de novo.
  • O PNG já está salvo e corresponde ao SHA-256 registrado: ele sai sem outra requisição. Um arquivo corrompido é consultado e baixado de novo.

Isso funciona dentro da janela de retenção de 24 horas. A mesma chave com um prompt ou parâmetros diferentes gera 409. O script recusa um arquivo de estado cujo prompt não corresponde mais; mova o arquivo se mudar os parâmetros.

Erros e correções

As falhas abaixo vêm das regras de tarefa e idempotência da API. Corrija a causa e execute de novo com o mesmo arquivo de estado quando for o caso.

RespostaCódigoO que fazer
400missing_idempotency_keyAdicione o cabeçalho; mantenha-o com menos de 255 caracteres
409idempotency_key_reusedEnvie o corpo original ou comece uma tarefa nova com outra chave
503no_available_channelConfira o acesso desta chave/conta; fale com o suporte se persistir
failederror.code, error.messageLeia o erro da tarefa; tarefas com falha são reembolsadas
canceled—A tarefa parou; crie outra se ainda precisar

Um 503 no_available_channel não prova que uma chave não tem permissão para o modelo. Dois envios com uma chave usada neste lote retornaram 503 e não criaram tarefa; outra chave teve sucesso em um teste de integração anterior no mesmo dia. A causa é desconhecida: pode ser a disponibilidade da chave/conta ou do canal upstream. Confirme que midjourney está disponível para sua chave/conta atual. Se o 503 persistir, envie ao suporte o corpo da resposta e o id da requisição.

Um 200 na criação é a repetição da tarefa original. Só failed e canceled são falhas terminais; parar o script deixa a tarefa rodando.

Custo e quando usar outra API de imagens

A OmniaKey cobra uma vez por tarefa concluída, e tarefas com falha ou canceladas são reembolsadas automaticamente. A tarifa atual está na página do modelo Midjourney; confira antes de estimar um lote.

O Midjourney serve para escolher entre quatro variações em uma grade. Esta integração retorna essa grade de texto para imagem; para uma imagem por chamada ou uma edição que preserve uma cena de origem, veja GPT Image 2.5 API ou Nano Banana Pro API.

Se preferir digitar um prompt a escrever código, o AI Image Generator executa modelos de imagem no navegador.

Perguntas frequentes

Preciso de uma assinatura do Midjourney para usar esta API?

Não. A chave vem da OmniaKey e as chamadas vão para a API de terceiros da OmniaKey. A OmniaKey não é o Midjourney; uma assinatura no site do Midjourney é outro produto e não cria uma chave OmniaKey.

Por que uma tarefa do Midjourney retorna quatro imagens em um único arquivo?

O Midjourney retorna uma grade 2x2 com quatro candidatos como um único PNG. Os quatro compartilham esse arquivo, e upscale e vary não estão disponíveis nesta integração, então divida a grade você mesmo após o download se precisar de arquivos separados.

O que significa 409 idempotency_key_reused?

Você enviou uma chave de idempotência que a OmniaKey já tem, mas o corpo da requisição mudou. Envie o corpo original ou comece uma tarefa nova com outra chave. Reutilizar uma chave com um prompt diferente é a causa comum.

O que acontece se meu script atingir o tempo limite durante a consulta?

A tarefa continua rodando. O id da tarefa está em midjourney-task.json, então executar o mesmo script de novo vai consultar esse id ou baixar o arquivo concluído. Um tempo esgotado local mantém a tarefa viva e a cobrança em vigor.

Modelos deste artigo

API do MidjourneyPythonAPI de imagensgrade 2x2tutorial de API

Artigos relacionados

Artigos recentes

Guias populares