Midjourney for Developers — API Access, Prompt Patterns, and Workflow Integration

Sanjeev SharmaSanjeev Sharma
6 min read

Advertisement

Introduction

Why This Matters

Midjourney consistently produces the most visually sophisticated AI images available, particularly for artistic, illustrative, and conceptual work. For developers building products where image quality directly affects user perception — creative tools, marketing platforms, design assistants, and content generators — Midjourney's output quality justifies its higher cost. Understanding how to drive it programmatically and how to write prompts that produce consistent, on-brand results is the difference between prototype and production.

API Access in 2026

Midjourney launched its official API in late 2024. Access requires an active Midjourney subscription (Pro or Mega plan recommended for API usage) plus API key generation from the dashboard at midjourney.com/account.

The API uses an asynchronous job model: you submit a generation request, get a job ID, and poll for completion.

import httpx
import time
 
MJ_API_KEY = "mj-..."
BASE_URL = "https://api.midjourney.com/v1"
 
def generate_image(prompt: str, params: dict = None) -> str:
    """Submit a generation job and return the image URL when complete."""
    headers = {"Authorization": f"Bearer {MJ_API_KEY}"}
    
    # Build the full prompt with parameters
    param_str = " ".join(f"--{k} {v}" for k, v in (params or {}).items())
    full_prompt = f"{prompt} {param_str}".strip()
    
    # Submit job
    response = httpx.post(
        f"{BASE_URL}/imagine",
        json={"prompt": full_prompt},
        headers=headers
    )
    response.raise_for_status()
    job_id = response.json()["job_id"]
    
    # Poll for completion
    for _ in range(60):  # Max 5 minutes
        status_resp = httpx.get(
            f"{BASE_URL}/jobs/{job_id}",
            headers=headers
        )
        job = status_resp.json()
        
        if job["status"] == "completed":
            return job["image_urls"][0]  # First of four generated images
        elif job["status"] == "failed":
            raise RuntimeError(f"Generation failed: {job.get('error')}")
        
        time.sleep(5)
    
    raise TimeoutError("Generation timed out after 5 minutes")

Midjourney Prompt Parameters

Midjourney parameters are appended to the prompt as --parameter value:

ParameterValuesEffect
--ar16:9, 4:3, 1:1, 9:16Aspect ratio
--styleraw, cute, expressive, scenicStyle preset
--stylize0-1000How strongly Midjourney applies its aesthetic (100 default)
--chaos0-100Variation between results (0 = consistent)
--quality0.25, 0.5, 1, 2Generation quality and time
--notext, watermarkElements to exclude

Example with parameters:

url = generate_image(
    "A minimalist technical illustration showing microservices architecture",
    params={
        "ar": "16:9",
        "style": "raw",       # Less Midjourney aesthetic
        "stylize": "50",      # Closer to the literal prompt
        "no": "text, labels", # No text overlaid
        "quality": "1"
    }
)

Prompt Engineering for Consistent Style

For brand-consistent output, develop a base style string and prepend it to every prompt:

BRAND_STYLE = (
    "flat vector illustration, "
    "minimal geometric shapes, "
    "blue and white color palette, "
    "professional technical aesthetic, "
    "no gradients, no shadows, "
    "clean white background"
)
 
def brand_image(subject: str) -> str:
    return generate_image(
        f"{subject}, {BRAND_STYLE}",
        params={"stylize": "50", "chaos": "0"}
    )
 
# Consistent output across all generated images
hero_img = brand_image("cloud infrastructure with servers and databases")
icon_img = brand_image("API connection between two services")

Using --chaos 0 reduces variation between runs, making output more predictable for systematic generation.

Selecting From Four Generated Images

Midjourney generates four image variations per request. The API returns URLs for all four:

def generate_and_select_best(prompt: str, selector_fn=None) -> str:
    """Generate four images and select the best one."""
    headers = {"Authorization": f"Bearer {MJ_API_KEY}"}
    
    response = httpx.post(
        f"{BASE_URL}/imagine",
        json={"prompt": prompt},
        headers=headers
    )
    job_id = response.json()["job_id"]
    
    # Wait for completion
    job = poll_until_complete(job_id)
    urls = job["image_urls"]  # Four URLs
    
    if selector_fn:
        return selector_fn(urls)  # Custom selection logic
    return urls[0]  # Default: first image

For human-in-the-loop workflows, present all four to the user and let them choose before storing.

Midjourney vs DALL-E 3 vs Stable Diffusion

CriterionMidjourneyDALL-E 3Stable Diffusion
Image qualityHighestVery highVariable
API maturityModerateMatureMature (various)
Cost per image$0.10-0.30$0.04-0.12Free (self-hosted)
Prompt adherenceModerateHighHigh (with guidance)
Style controlExcellentGoodExcellent (fine-tuned)
Best forArt, illustration, conceptProduct, contentHigh-volume, custom

Common Mistakes

  • Not handling the async model: Midjourney generation takes 30-120 seconds. Never make a synchronous API call from a user-facing request — use a job queue.
  • Ignoring --chaos for batch generation: Default chaos produces different-looking results each time. Set --chaos 0 for systematic, consistent generation.
  • Too many negative prompts: The --no parameter works for 1-3 exclusions. Long exclusion lists reduce overall image quality.
  • Not saving successful prompts: Keep a library of prompts that produced good results — iteration is faster from a good starting point.

Best Practices

  • Use a job queue (Celery, BullMQ) for image generation — never block a request thread on a 30-120 second operation
  • Develop and version your base style string separately from subject descriptions — this is your brand's visual identity
  • Store all four generated image URLs, not just the first — users or downstream processes may prefer a different variant
  • Implement cost alerts in your billing dashboard; API generation costs compound quickly at scale
  • Cache generated images by prompt hash to avoid regenerating identical requests

Key Takeaways

  • Midjourney uses an async job model — submit a request, get a job ID, poll for completion over 30-120 seconds
  • Every generation produces four image variants — store all four and select the best rather than discarding alternatives
  • The --chaos 0 parameter is essential for consistent, predictable batch generation in production workflows
  • A shared base style string prepended to every prompt is the most effective way to maintain visual brand consistency
  • Midjourney is best for artistic and illustrative content; DALL-E 3 is better for literal prompt adherence and product images
  • Never call the Midjourney API synchronously from a user-facing request — use a background job queue
  • API costs are $0.10-0.30 per image — implement prompt caching to avoid regenerating identical requests
  • Successful prompt templates should be versioned and stored as they represent significant prompt engineering investment

Advertisement

Sanjeev Sharma

Written by

Sanjeev Sharma

Full Stack Engineer · E-mopro