Generate Videos

April 6, 2026

Table of contents

  1. Model Compatibility Matrix
  2. Request Headers
  3. Request Body
  4. Responses
  5. Model
  6. Webhook Callback (replyUrl)
  7. Examples
  8. Try It

Generate videos using Luma AI models from text prompts with optional image input. All video generation is asynchronous — this endpoint returns immediately with a job ID. Poll GET /jobs/jobid for completion, or use replyUrl webhook for automatic callbacks.

Video generation typically completes within 30-60 seconds for draft, and ~3 minutes for 1080p on unlimited plans (relaxed mode). Generation times vary by model, resolution, and server load.

Model Compatibility Matrix

Not all parameter combinations are supported. The API validates against this matrix and returns 400 for unsupported combinations.

  ray-v3-flash (default) ray-v3 ray-v3-reasoning
Resolutions draft, 540p, 720p, 1080p draft, 540p, 720p, 1080p 720p, 1080p
Durations 5s, 10s 5s, 10s 5s, 10s
SDR all resolutions, 5s+10s all resolutions, 5s+10s all resolutions, 5s+10s
HDR 540p-1080p, 5s only 540p-1080p, 5s only 720p-1080p, 5s only
HDR+EXR 540p-720p, 5s only 540p-720p, 5s only 720p only, 5s only
imageStart/imageEnd yes yes yes
imageCharacter no yes (SDR only) no

Dynamic range output formats:

  • sdr — Standard H.264 MP4 (playable in browsers).
  • hdr — HEVC (H.265) Main 10 profile, BT.2020 color, HDR10 (PQ). Not playable in browsers — requires VLC, mpv, or a native HDR-capable player.
  • hdr_exr — Same HDR video + EXR frames download (ZIP) available via response.video_hdr_exr.

https://api.useapi.net/v1/luma/videos

Request Headers

Authorization: Bearer {API token}
Content-Type: application/json
# Alternatively you can use multipart/form-data
# Content-Type: multipart/form-data

Request Body

{
  "prompt": "A serene mountain landscape at sunset with camera slowly panning right",
  "model": "ray-v3-flash",
  "resolution": "720p",
  "aspect_ratio": "16:9",
  "duration": "5s"
}
  • prompt is required. Text description of the video to generate. Maximum 2000 characters.
  • email is optional. Specific account to use. Auto-selected (random with available capacity) if omitted. When imageStart, imageEnd, or imageCharacter is provided, the account is automatically inferred from the asset reference — email is not required. If provided, it must match the asset’s account.
  • model is optional, the AI model to use (default: ray-v3-flash). Supported values: ray-v3-flash, ray-v3, ray-v3-reasoning.
  • resolution is optional, video resolution (default: draft). Supported values: draft, 540p, 720p, 1080p. See matrix above for per-model restrictions.
  • aspect_ratio is optional, video aspect ratio (default: 16:9). Supported values: 9:16, 3:4, 1:1, 4:3, 16:9, 21:9.
  • duration is optional, video duration (default: 5s). Supported values: 5s, 10s. HDR/HDR+EXR limited to 5s.
  • dynamic_range is optional, video dynamic range (default: sdr). Supported values: sdr, hdr, hdr_exr. See matrix above for resolution restrictions and output format details.
  • imageStart is optional. assetRef from POST /assets/email — start keyframe for image-to-video.
  • imageEnd is optional. assetRef — end keyframe. Can be used alone or with imageStart.
  • imageCharacter is optional. assetRef — character reference image. Only supported with ray-v3 model and sdr dynamic range. Cannot be combined with imageStart or imageEnd.
  • replyUrl is optional, webhook URL for job status callbacks. Receives POST requests with the job record on submission and on completion/failure.
  • replyRef is optional, custom reference string passed back in webhook callbacks.

Responses

  • 200 OK

    Job created successfully. Video is generating in the background.

    {
      "jobid": "j0326140530123456789v-u12345-email:[email protected]:a1b2c3d4-e5f6-7890-abcd-ef1234567890-bot:luma",
      "type": "video",
      "status": "created",
      "created": "2026-03-26T14:05:30.123Z",
      "request": {
        "prompt": "A serene mountain landscape at sunset with camera slowly panning right",
        "model": "ray-v3-flash",
        "resolution": "720p",
        "aspect_ratio": "16:9",
        "duration": "5s",
        "dynamic_range": "sdr"
      }
    }
    

    Poll GET /jobs/jobid for completion status, or use replyUrl for webhook callbacks.

  • 400 Bad Request

    Validation error.

    {
      "error": "Parameter model (invalid-model) valid values: ray-v3-flash, ray-v3, ray-v3-reasoning"
    }
    
  • 401 Unauthorized

    Invalid API token.

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

    Subscription expired or insufficient credits.

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

    Asset not found or expired.

    {
      "error": "Asset not found"
    }
    
  • 422 Unprocessable Entity

    Content moderation rejection.

    {
      "error": "Content moderation: prompt rejected",
      "code": 422
    }
    
  • 429 Too Many Requests

    All accounts at maximum capacity. Wait for current jobs to complete or use DELETE /jobs/jobid to free slots.

    {
      "error": "All configured accounts are running at maximum capacity",
      "code": 429
    }
    
  • 596 Session Error

    Account session expired. Re-add the account using POST /accounts with correct credentials.

    {
      "error": "Luma account has an error. Please check your account configuration via GET /v1/luma/accounts or re-add the account via POST /v1/luma/accounts"
    }
    

Model

{
  jobid: string                    // Unique job identifier
  type: 'video'                    // Job type
  status: 'created'                // Initial status
  created: string                  // ISO 8601 timestamp
  request: {
    prompt: string
    model: string                  // "ray-v3-flash" | "ray-v3" | "ray-v3-reasoning"
    imageStart?: string            // assetRef — start keyframe
    imageEnd?: string              // assetRef — end keyframe
    imageCharacter?: string        // assetRef — character reference (ray-v3 + sdr only)
    resolution?: string            // "draft" | "540p" | "720p" | "1080p"
    aspect_ratio?: string          // "9:16" | "3:4" | "1:1" | "4:3" | "16:9" | "21:9"
    duration?: string              // "5s" | "10s"
    dynamic_range?: string         // "sdr" | "hdr" | "hdr_exr"
  }
}

Webhook Callback (replyUrl)

When replyUrl is provided, the API sends a POST request to that URL on completion or failure. The callback payload uses the same shape as GET /jobs/jobid with an additional replyRef field. See Webhook Callbacks for full type definitions.

Examples

  • # Text-to-video
    curl -X POST \
         -H "Authorization: Bearer YOUR_API_TOKEN" \
         -H "Content-Type: application/json" \
         -d '{
           "prompt": "A serene mountain landscape at sunset",
           "model": "ray-v3-flash",
           "resolution": "720p",
           "aspect_ratio": "16:9",
           "duration": "5s"
         }' \
         "https://api.useapi.net/v1/luma/videos"
    
    # Image-to-video
    curl -X POST \
         -H "Authorization: Bearer YOUR_API_TOKEN" \
         -H "Content-Type: application/json" \
         -d '{
           "prompt": "Camera slowly pans across the scene",
           "model": "ray-v3",
           "imageStart": "u12345-email:[email protected]:a1b2c3d4-e5f6-7890-abcd-ef1234567890",
           "resolution": "1080p",
           "duration": "5s"
         }' \
         "https://api.useapi.net/v1/luma/videos"
    
  • const token = 'YOUR_API_TOKEN'
    const apiUrl = 'https://api.useapi.net/v1/luma/videos'
    
    // Text-to-video
    const response = await fetch(apiUrl, {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${token}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        prompt: 'A serene mountain landscape at sunset',
        model: 'ray-v3-flash',
        resolution: '720p',
        aspect_ratio: '16:9',
        duration: '5s'
      })
    })
    
    const result = await response.json()
    console.log('Job created:', result.jobid)
    
    // Poll for completion
    const poll = async (jobid) => {
      while (true) {
        const res = await fetch(`https://api.useapi.net/v1/luma/jobs/${jobid}`, {
          headers: { 'Authorization': `Bearer ${token}` }
        })
        const job = await res.json()
        console.log('Status:', job.status)
    
        if (job.status === 'completed') {
          console.log('Video URL:', job.response.video.url)
          return job
        }
        if (job.status === 'failed') throw new Error(job.error)
    
        await new Promise(r => setTimeout(r, 10000))
      }
    }
    
    const completed = await poll(result.jobid)
    
  • import requests
    import time
    
    token = 'YOUR_API_TOKEN'
    api_url = 'https://api.useapi.net/v1/luma/videos'
    
    headers = {
        'Authorization': f'Bearer {token}',
        'Content-Type': 'application/json'
    }
    
    # Text-to-video
    data = {
        'prompt': 'A serene mountain landscape at sunset',
        'model': 'ray-v3-flash',
        'resolution': '720p',
        'aspect_ratio': '16:9',
        'duration': '5s'
    }
    
    response = requests.post(api_url, headers=headers, json=data)
    result = response.json()
    print(f"Job created: {result['jobid']}")
    
    # Poll for completion
    jobid = result['jobid']
    while True:
        job = requests.get(
            f'https://api.useapi.net/v1/luma/jobs/{jobid}',
            headers={'Authorization': f'Bearer {token}'}
        ).json()
        print(f"Status: {job['status']}")
    
        if job['status'] == 'completed':
            print(f"Video URL: {job['response']['video']['url']}")
            break
        if job['status'] == 'failed':
            raise Exception(job.get('error'))
    
        time.sleep(10)
    

Try It