Retrieve Job Status and Results
October 27, 2025
Table of contents
https://api.useapi.net/v3/midjourney/jobs/
jobid
Request Headers
Authorization: Bearer {API token}
API tokenis required, see Setup useapi.net for details.
Path Parameters
jobidis required. The job ID to query.
Responses
-
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": {} } } -
Invalid API token.
{ "error": "Unauthorized" } -
{ "error": "Account has no subscription or subscription expired" } -
Job not found.
{ "error": "Job not found" } -
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 contentimageUx/videoUx- Array of individual media assets from the grid. Each containsid(1-4) andurl(direct CDN link fromhttps://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 commandsexecuteOnce- 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"