Retrieve Job Status and Results

October 27, 2025

Table of contents

  1. Request Headers
  2. Path Parameters
  3. Responses
  4. Model
  5. Polling Example
  6. Try It

https://api.useapi.net/v3/midjourney/jobs/jobid

Request Headers
Authorization: Bearer {API token}
Path Parameters
  • jobid is required. The job ID to query.
Responses
  • 200 OK

    Job found and returned successfully.

    {
      "jobid": "j1023141520123456789i-u12345-c1234567890123456789-bot:midjourney",
      "verb": "imagine",
      "status": "completed",
      "created": "2025-10-23T14:15:20.123Z",
      "updated": "2025-10-23T14:16:45.789Z",
      "request": {
        "prompt": "a cat in a hat --ar 16:9"
      },
      "response": {
        "content": "**a cat in a hat** - <@user> (100%) (fast)",
        "progress_percent": 100,
        "attachments": [
          {
            "url": "https://cdn.discordapp.com/attachments/.../image.png",
            "proxy_url": "https://media.discordapp.net/...",
            "filename": "image.png",
            "width": 1920,
            "height": 1080
          }
        ],
        "buttons": ["U1", "U2", "U3", "U4", "V1", "V2", "V3", "V4", "🔄"],
        "imageUx": [
          {"id": 1, "url": "https://cdn.midjourney.com/.../0_0.jpeg"},
          {"id": 2, "url": "https://cdn.midjourney.com/.../0_1.jpeg"},
          {"id": 3, "url": "https://cdn.midjourney.com/.../0_2.jpeg"},
          {"id": 4, "url": "https://cdn.midjourney.com/.../0_3.jpeg"}
        ],
        "children": {},
        "executeOnce": {}
      }
    }
    
  • 401 Unauthorized

    Invalid API token.

    {
      "error": "Unauthorized"
    }
    
  • 402 Payment Required

    {
      "error": "Account has no subscription or subscription expired"
    }
    
  • 404 Not Found

    Job not found.

    {
      "error": "Job not found"
    }
    
  • 410 Gone

    Job expired (older than 62 days).

    {
      "error": "Job j1023... has expired"
    }
    
Model
{
  // Top-level metadata (always returned to customer)
  jobid: string
  verb: 'imagine' | 'button' | 'describe' | 'blend' | 'seed' | 'info' |
        'fast' | 'relax' | 'turbo' | 'variability' | 'remix'
  jobType?: 'image' | 'video'
  status: 'created' | 'started' | 'progress' | 'completed' | 'failed' | 'moderated'
  created: string  // ISO 8601 timestamp
  updated?: string  // ISO 8601 timestamp
  error?: string
  errorDetails?: string
  code?: number

  // Original request parameters (returned to customer)
  request: {
    prompt?: string
    button?: 'U1' | 'U2' | 'U3' | 'U4' | 'V1' | 'V2' |
             'V3' | 'V4' | '⬅️' | '➡️' | '⬆️' | '⬇️' |
             'Vary (Region)' | 'Vary (Strong)' | 'Vary (Subtle)' | 'Zoom Out 1.5x' | 'Zoom Out 2x' | 'Upscale (2x)' |
             'Upscale (4x)' | 'Upscale (Subtle)' | 'Upscale (Creative)' | 'Redo Upscale (2x)' | 'Redo Upscale (4x)' | 'Redo Upscale (Subtle)' |
             'Redo Upscale (Creative)' | 'Make Square' | 'Make Variations' | 'Remaster' | 'Custom Zoom' | '🔄' |
             'Animate (Low motion)' | 'Animate (High motion)' | 'Extend (Low motion)' | 'Extend (High motion)'
    mask?: string
    describeUrl?: string
    blendUrls?: string[]
    blendDimensions?: 'Portrait' | 'Square' | 'Landscape'
    jobId?: string            // Parent job ID for button/seed commands
    channel?: string
    maxJobs?: number
    replyUrl?: string
    replyRef?: string
    stream?: boolean
    debug?: boolean
  }

  // Discord response (returned to customer when available)
  // NOTE: This is essentially Discord's message format
  response?: {
    id?: string
    content?: string
    timestamp?: string
    progress_percent?: number     // Numeric progress (0-100) extracted from content
    settings?: {                  // Settings state for /remix and /variability commands
      remix?: boolean
      variability?: boolean
      relax?: boolean
      fast?: boolean
      turbo?: boolean
    }
    buttons?: Array<'U1' | 'U2' | 'U3' | 'U4' | 'V1' | 'V2' |
                    'V3' | 'V4' | '⬅️' | '➡️' | '⬆️' | '⬇️' |
                    'Vary (Region)' | 'Vary (Strong)' | 'Vary (Subtle)' | 'Zoom Out 1.5x' | 'Zoom Out 2x' | 'Upscale (2x)' |
                    'Upscale (4x)' | 'Upscale (Subtle)' | 'Upscale (Creative)' | 'Redo Upscale (2x)' | 'Redo Upscale (4x)' | 'Redo Upscale (Subtle)' |
                    'Redo Upscale (Creative)' | 'Make Square' | 'Make Variations' | 'Remaster' | 'Custom Zoom' | '🔄' |
                    'Animate (Low motion)' | 'Animate (High motion)' | 'Extend (Low motion)' | 'Extend (High motion)'>   // Available buttons (populated at job completion for efficiency)
    videoUx?: Array<{ id: number, url: string }>  // Video upscale URLs (populated at job completion)
    imageUx?: Array<{ id: number, url: string }>  // Image upscale URLs (populated at job completion)
    executeOnce?: Partial<Record<'U1' | 'U2' | 'U3' | 'U4', string>>  // ExecuteOnce button tracking
    children?: Record<string, { button: string, messageId: string }>  // Child jobs tracking
    attachments?: Array<{
      id: string
      filename: string
      url: string
      proxy_url: string
      size: number
      width?: number
      height?: number
      content_type?: string
    }>
    embeds?: Array<{
      title?: string
      description?: string
      color?: number
    }>
    components?: Array<{
      type: number
      components: Array<{
        type: number
        style: number
        label?: string
        emoji?: { name: string }
        custom_id?: string
        disabled?: boolean
        url?: string
      }>
    }>
  }
}

Note: The response object is an enhanced version of a Discord message. All standard Discord message fields are included by default. See Discord Message Object for reference.

Response Fields:

  • content - Midjourney message text. Contains useful information for settings jobs (current settings display) and moderation events (ban/CAPTCHA warnings).
  • progress_percent - Numeric progress (0-100) extracted from content
  • imageUx / videoUx - Array of individual media assets from the grid. Each contains id (1-4) and url (direct CDN link from https://cdn.midjourney.com). Use GET /proxy/cdn-midjourney to retrieve these assets via useapi.net proxy.
  • attachments - Discord attachments array with full metadata (url, proxy_url, width, height, filename)
  • settings - Settings state for /remix and /variability commands
  • executeOnce - ExecuteOnce button tracking (U1-U4 buttons can only be executed once)
  • children - Child jobs tracking

For image/video jobs, use imageUx/videoUx to access individual grid cells, or attachments[0] for the complete grid image. The proxy_url in attachments may be preferred as it provides Discord’s CDN caching.

Job Statuses:

Status Description
created Job created, not yet started
started Job started processing
progress Job in progress (check response.progress_percent)
completed Job finished successfully
failed Job failed (see error, errorDetails, and code fields for error code and details)
moderated Content moderation (see error, errorDetails, code fields, and check response.embeds for moderation details)
Polling Example
  • // ⚠️ Classic polling approach (use SSE instead when possible)
    async function pollJobStatus(jobId) {
      const maxAttempts = 120; // 10 minutes max
      const pollInterval = 5000; // 5 seconds
    
      for (let i = 0; i < maxAttempts; i++) {
        const response = await fetch(
          `https://api.useapi.net/v3/midjourney/jobs/${jobId}`,
          {
            headers: {'Authorization': 'Bearer YOUR_API_TOKEN'}
          }
        );
    
        const job = await response.json();
        console.log(`${job.status}: ${job.response?.progress_percent || 0}%`);
    
        if (job.status === 'completed') {
          console.log('Job completed!', job.response);
          return job;
        }
    
        if (job.status === 'failed' || job.status === 'moderated') {
          console.error('Job failed:', job.error);
          return job;
        }
    
        await new Promise(resolve => setTimeout(resolve, pollInterval));
      }
    
      throw new Error('Polling timeout');
    }
    
  • import requests
    import time
    
    # ⚠️ Classic polling approach (use SSE instead when possible)
    def poll_job_status(job_id):
        max_attempts = 120
        poll_interval = 5  # seconds
    
        for i in range(max_attempts):
            response = requests.get(
                f'https://api.useapi.net/v3/midjourney/jobs/{job_id}',
                headers={'Authorization': 'Bearer YOUR_API_TOKEN'}
            )
    
            job = response.json()
            progress = job.get('response', {}).get('progress_percent', 0)
            print(f"{job['status']}: {progress}%")
    
            if job['status'] == 'completed':
                print('Job completed!', job['response'])
                return job
    
            if job['status'] in ['failed', 'moderated']:
                print(f"Job failed: {job.get('error')}")
                return job
    
            time.sleep(poll_interval)
    
        raise Exception('Polling timeout')
    
  • # Poll job status
    curl -H "Authorization: Bearer YOUR_API_TOKEN" \
         "https://api.useapi.net/v3/midjourney/jobs/j1023141520123456789i-u12345-c1234567890123456789-bot:midjourney"
    
Try It