Create a music video from a song

November 10, 2026

Table of contents

  1. Modes
  2. Prices
  3. Request Headers
  4. Request Body
  5. Parameters
  6. Responses
  7. Model
  8. Examples
  9. Try It

Use a pixverse.ai account to turn a song into a finished music video, the same as PixVerse’s One-Click MV. Upload the song (10 seconds to 6 minutes), optionally add a character photo, pick a mode and a visual style, and PixVerse plans the shots, generates them and cuts them to the beat. In performance mode the character lip-syncs the vocals. With subtitles: true it also burns the lyrics in as captions.

Generation is asynchronous. The call returns a video_id right away, then poll GET music-video/video_id until status is succeeded or failed, or pass a replyUrl to get the result by callback. A 30-second song took about 5 minutes in our tests, a 2½-minute song at 1080p about 16 minutes.

Step-by-step example with a full song: How to Make an AI Music Video from a Song with the PixVerse API.

The music video is an ordinary PixVerse video as well: the same video_id works with GET videos/video_id, shows up in GET videos and is deleted with DEL videos/video_id.

Modes
Mode What you get
performance (default) the character sings the song with lip-sync, made for singer and dancer performance videos
expressive emotional, atmospheric shots of the character
storytelling a storyline that follows your prompt, with one or two characters, made by PixVerse’s partner engine
Prices

One minute of song costs (PixVerse bills every second, rounded to the nearest second):

Mode and quality Official API Premium $60/mo
15,000 cr/mo
Pro $30/mo
6,000 cr/mo
Ultra $199/mo
25,000 cr/mo
performance or expressive, 720p $4.00
15 cr/s
$2.40
10 cr/s
$3.00
10 cr/s
$4.78
10 cr/s
performance or expressive, 1080p $6.00
22.5 cr/s
$3.60
15 cr/s
$4.50
15 cr/s
$7.16
15 cr/s
storytelling, 720p not sold $4.80
20 cr/s
$6.00
20 cr/s
$9.55
20 cr/s
storytelling, 1080p not sold $9.60
40 cr/s
$12.00
40 cr/s
$19.10
40 cr/s

The first music video on every PixVerse account is free, at any length and in 1080p, even on a free Basic plan. Prices as of November 2026.

While an account’s free music video is unused, the quote shows cost_credits: 0 and free: true.

https://api.useapi.net/v2/pixverse/music-video/create

Request Headers
Authorization: Bearer {API token}
Content-Type: application/json
# Alternatively you can use multipart/form-data
# Content-Type: multipart/form-data
Request Body
{
    "email": "Optional PixVerse API account email",
    "audio_path": "upload/8aa34c5f-7f33-4fdc-af40-7357909102cb.mp3",
    "mode": "performance",
    "style": "auto",
    "image_path": "upload/66952750-badc-473b-b2a2-721197e96e7d.jpeg",
    "music_genre": "hip_hop",
    "subtitles": true,
    "lyrics": "That bassline hits me hard right now\nWe turn it up and show them how",
    "aspect_ratio": "16:9",
    "quality": "1080p",
    "quote": false,
    "replyUrl": "Place your call back URL here",
    "replyRef": "Place your reference id here",
    "maxJobs": 3
}
Parameters
  • email is optional, if not specified API will randomly select account from available accounts.

  • audio_path is required, the song: a path returned by POST files for an MP3 upload, or a song from GET music-video/library. 10 seconds to 6 minutes. PixVerse refused an M4A file in our tests, so convert to MP3 first.

  • mode is optional, performance (default), expressive or storytelling. See Modes and Prices.

  • style is optional, the look of the video. The values depend on the mode:
    • performance and expressive: auto (default, PixVerse picks), custom (your own style image, see below), cinematic, underground, lo_fi, dreamscape, woolen_felt, candy, golden_age, voxel, retro_game, claymation, woodland_tale, impressionism, decadence, futuristic, chromatic_clash, holiday.
    • storytelling: photorealistic (default), anime, 2d_modern_animation, cyberpunk, high_energy, digital_art, pixel_art, city_pop, y2k_collage, illustration, sketch, rock_haze, bubble_gum_punk, claymation, metal_noir, ghibli, midnight_blue_soul, lego_blocks.
  • style_image_path is optional, only with style: custom. An image whose look the whole video copies, a path from POST files or a style from the library. Without it, custom uses image_path as the style. style: custom needs style_image_path or image_path.

  • image_path is optional, the character who performs: a photo uploaded via POST files or a character from the library. A clear, full-body photo with the face visible works best. Without it, PixVerse invents a performer.

  • image_path_2 is optional, a second character, storytelling only. Needs image_path.

  • music_genre is optional, helps PixVerse read the song: pop, rock, hip_hop, rnb, jazz, reggae, country, folk, electronic, classical, soul, funk, metal, ambient, others. Refused by storytelling.

  • subtitles is optional, default false. true burns lyric captions into the video, timed to the vocals. Every video also comes with a copy without captions, see GET music-video/video_id. Refused by storytelling.

  • lyrics is optional, only with subtitles: true. The song’s lyrics, one line per caption, so the captions spell the words right. Without it, PixVerse uses the lyrics it detects in the vocals. Refused by storytelling. Maximum length 5,000 characters.

  • prompt is required for storytelling and refused by the other modes, the story to tell. Maximum length 2,000 characters.

  • aspect_ratio is optional, 16:9 (default), 9:16, 1:1, 4:3 or 3:4.

  • quality is optional, 720p (default) or 1080p.

  • quote is optional, default false. true returns the price for this exact request and creates nothing. Quoting is free.

  • replyUrl is optional, place here your callback URL. This is the preferred and most optimal way to receive results quickly — the API polls every 10 seconds and will call the provided replyUrl once the music video is completed or failed. We recommend using sites like webhook.site to test callback URL functionality. Maximum length 1024 characters. Callback body has the same JSON shape as GET videos/video_id response.

  • replyRef is optional, place here your reference id which will be stored and returned along with this video response / result. Maximum length 1024 characters.

  • maxJobs is optional, if not specified value from selected accounts/email will be used. It should not exceed the number of concurrent generations supported by your account subscription plan. Valid range: 1…8

Every request gets a free quote first. If the account cannot afford the video, the API answers 412 before anything is created.

Responses
  • 200 OK

    Use the returned video_id with GET music-video/video_id until status is succeeded or failed. cost_credits is what this video costs, list_cost_credits the price without the free music video.

    {
        "video_id": "user:<userid>-pixverse:<email>-video:642767619916505",
        "project_id": 192080023076656,
        "mode": "performance",
        "quality": "1080p",
        "aspect_ratio": "16:9",
        "bill_seconds": 152,
        "credits_per_second": 15,
        "list_cost_credits": 2280,
        "cost_credits": 0,
        "free": true
    }
    

    With quote: true nothing is created:

    {
        "quote": true,
        "mode": "performance",
        "quality": "720p",
        "bill_seconds": 30,
        "credits_per_second": 10,
        "list_cost_credits": 300,
        "cost_credits": 300,
        "free": false,
        "has_enough_credits": true
    }
    
  • 400 Bad Request

    A parameter is invalid, for example a style that the mode doesn’t have, prompt outside storytelling, lyrics without subtitles, or an audio format PixVerse refuses.

    {
      "error": "Parameter style (anime) is not available for mode performance. Valid values: auto,custom,cinematic,…",
      "code": 400
    }
    
  • 401 Unauthorized

    {
      "error": "Unauthorized"
    }
    
  • 412 Insufficient credits

    The account does not have enough credits for this video. Nothing was created.

    {
      "error": "Not enough credits: this music video costs 1800 credits",
      "code": 412
    }
    
  • 429 Too Many Requests

    Wait in a loop for at least 10..30 seconds and retry again.

    The API query is full and can not accept new requests. Size of the query is defined by the maxJobs optional parameter.

    {
        "error":
          "Account <email> is busy executing <maxJobs> tasks."
          "All configured accounts are running at maximum capacity."
    }
    
  • 596 Pending mod message

    The account has a pending error, and the API does not use it until you add it again with POST accounts/email. You receive an email when this happens. There are three causes.

    PixVerse could not sign the account in, most often because its password was changed. Add the account again with the current password.

    {
      "error": "Your PixVerse account has pending error. Please address this issue at https://useapi.net/docs/api-pixverse-v2/post-pixverse-accounts-email before making any new API calls.",
      "code": 596
    }
    

    Or PixVerse suspended the account. PixVerse suspends accounts that break its content rules and does not say which generation caused it. The first response carries PixVerse’s own details in account_moderation, and later calls return the same error. Add the account again after reinstatement_at. To ask PixVerse to lift the suspension earlier, write to the support_email it gives.

    {
      "error": "PixVerse suspended account [email protected] for 30 days, from 2026-10-16 09:26 UTC until 2026-11-15 09:26 UTC. PixVerse suspends accounts that break its content rules and does not say which generation caused it. Add the account again at https://useapi.net/docs/api-pixverse-v2/post-pixverse-accounts-email after 2026-11-15 09:26 UTC. To ask PixVerse to lift it earlier, write to [email protected].",
      "code": 596,
      "account_moderation": {
        "ban_level": "suspend_30d",
        "action": "temporary_suspend",
        "duration_days": 30,
        "suspended_at": "2026-10-16 09:26:25 (UTC)",
        "reinstatement_at": "2026-11-15 09:26:25 (UTC)",
        "support_email": "[email protected]"
      }
    }
    

    Or PixVerse banned the account for violating its community guidelines. A ban is permanent unless PixVerse accepts an appeal, and the error quotes PixVerse’s own message, which says how to appeal. Remove the account with DELETE accounts/email, or add it again only if PixVerse lifts the ban.

    {
      "error": "PixVerse banned account [email protected] for violating its community guidelines (400024). A ban is permanent unless PixVerse accepts an appeal. PixVerse says: \"Your account has been banned for violating community guidelines. If you wish to appeal, please contact us via ...\" Remove it with DELETE /v2/pixverse/accounts/[email protected], or add it again (https://useapi.net/docs/api-pixverse-v2/post-pixverse-accounts-email) only if PixVerse lifts the ban.",
      "code": 596
    }
    

    If every account you have is in this state, a request without email returns 596 as well.

Model
{ // TypeScript, all fields are optional
    video_id: string
    project_id: number
    mode: 'performance' | 'expressive' | 'storytelling'
    quality: '720p' | '1080p'
    aspect_ratio: string
    bill_seconds: number
    credits_per_second: number
    list_cost_credits: number
    cost_credits: number
    free: boolean
    error: string
    code: number
    // quote: true only
    quote: boolean
    has_enough_credits: boolean
}
Examples
  • curl -H "Accept: application/json" \
         -H "Content-Type: application/json" \
         -H "Authorization: Bearer …" \
         -X POST "https://api.useapi.net/v2/pixverse/music-video/create" \
         -d '{"audio_path": "upload/<song>.mp3", "image_path": "upload/<singer>.jpeg", "aspect_ratio": "9:16"}'
    
  • const apiUrl = `https://api.useapi.net/v2/pixverse/music-video/create`;
    const token = "API token";
    const data = {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${token}`,
        'Content-Type': 'application/json' }
    };
    data.body = JSON.stringify({
      audio_path: "upload/<song>.mp3",
      image_path: "upload/<singer>.jpeg",
      aspect_ratio: "9:16"
    });
    const response = await fetch(apiUrl, data);
    const result = await response.json();
    console.log("response", {response, result});
    
  • import requests
    apiUrl = f"https://api.useapi.net/v2/pixverse/music-video/create"
    token = "API token"
    headers = {
        "Content-Type": "application/json",
        "Authorization" : f"Bearer {token}"
    }
    body = {
        "audio_path": "upload/<song>.mp3",
        "image_path": "upload/<singer>.jpeg",
        "aspect_ratio": "9:16"
    }
    response = requests.post(apiUrl, headers=headers, json=body)
    print(response, response.json())
    
Try It