Midjourney doesn't give you a clean synchronous "give me an image" call. Image generation is slow, so the API is built around an async job model: you submit a job, poll for its status, then fetch the result when it's done. Get that loop right and the rest of the midjourney api python integration is straightforward.
If you've worked with the Midjourney REST API before, this is the same shape in Python. If you haven't, I'll show you the full pattern below.
The midjourney api python pattern: submit, poll, fetch
Every Midjourney API integration reduces to three steps. Here's the minimal version with the standard requests library:
import requests
import time
BASE = "https://api.example.com/v1"
headers = {"Authorization": "Bearer YOUR_API_KEY"}
def submit_generation(prompt: str) -> str:
resp = requests.post(f"{BASE}/jobs", json={"prompt": prompt}, headers=headers)
resp.raise_for_status()
return resp.json()["job_id"]
def wait_for_result(job_id: str, timeout: int = 180) -> str:
deadline = time.time() + timeout
while time.time() < deadline:
resp = requests.get(f"{BASE}/jobs/{job_id}", headers=headers)
data = resp.json()
if data["status"] == "succeeded":
return data["result_url"]
if data["status"] == "failed":
raise RuntimeError(data.get("error", "generation failed"))
time.sleep(3)
raise TimeoutError(f"job {job_id} did not finish in time")
job_id = submit_generation("a minimalist logo, flat design")
url = wait_for_result(job_id)
print(url)
That's the whole shape. Submit returns a job_id, polling reads status until it's succeeded, and you get a result_url back.
Async with httpx: don't block your app
The synchronous loop above works fine for scripts and CLI tools. If you're embedding generation into a FastAPI service or a background worker, use httpx async so you're not tying up threads while waiting on image jobs that can take 30-60 seconds.
import asyncio
import httpx
ASYNC_BASE = "https://api.example.com/v1"
async def generate(prompt: str, client: httpx.AsyncClient) -> str:
r = await client.post(f"{ASYNC_BASE}/jobs", json={"prompt": prompt})
r.raise_for_status()
job_id = r.json()["job_id"]
for _ in range(60): # ~3 min cap
r = await client.get(f"{ASYNC_BASE}/jobs/{job_id}")
data = r.json()
if data["status"] == "succeeded":
return data["result_url"]
if data["status"] == "failed":
raise RuntimeError(data.get("error", "generation failed"))
await asyncio.sleep(3)
raise TimeoutError(f"job {job_id} timed out")
async def main():
async with httpx.AsyncClient() as client:
url = await generate("a hero image for a SaaS landing page", client)
print(url)
asyncio.run(main())
The async version is the one that scales. If you're planning to process hundreds of prompts, you can batch them with asyncio.gather against a single client connection pool.
Handle errors properly
Two failures dominate real integrations: credentials and rate limits.
- 401 Unauthorized — your key is wrong or expired. Log it loudly; don't retry.
- 429 Too Many Requests — you hit the rate limit. Respect
Retry-Afteror back off exponentially instead of hammering.
def submit_with_retry(prompt: str, max_retries: int = 4) -> str:
for attempt in range(max_retries):
r = requests.post(f"{BASE}/jobs", json={"prompt": prompt}, headers=headers)
if r.status_code == 429:
wait = min(2 ** attempt, 30)
print(f"rate limited, backing off {wait}s")
time.sleep(wait)
continue
r.raise_for_status()
return r.json()["job_id"]
raise RuntimeError("still rate limited after retries")
There's more detail on this in the Midjourney API error handling guide, but the rule of thumb is: retry on 429 with backoff, don't retry on 4xx auth errors.
Pull the result down and save it
Once you have the result_url, download the image and store it. Handle retries here too — CDN links can be temporarily flaky:
def download(url: str, path: str) -> None:
for _ in range(3):
r = requests.get(url)
if r.status_code == 200:
with open(path, "wb") as f:
f.write(r.content)
return
time.sleep(2)
raise RuntimeError(f"failed to download {url}")
What about the heavier setups
If you're building something more serious than a single script — a multi-user platform, an admin dashboard, job queues — you don't want to maintain this plumbing by hand. That's the case for buying the source code instead: a production-tested Midjourney API integration with the queue, multi-account handling, and dashboard already built. Writing the happy path in Python takes an afternoon; making it survive real traffic takes months.
There's no official Midjourney API, and third-party access carries ToS risk with Midjourney's terms. Nextjourney is an independent platform and is not affiliated with Midjourney or Discord.
Ready to self-host?
Full source code — backend, frontend, REST API, admin dashboard. One-time license from $399.
See pricing