How to Generate AI Video with PixVerse V6 via the PixVerse API

6 min read • June 8, 2024 (June 15, 2026)

Table of contents

  1. Introduction
  2. Generate a video in two API calls
  3. Supported models
  4. Audio, multi-shot & effects
  5. Pricing
  6. Batch-generate with a script
  7. Examples
  8. Frequently asked questions
  9. Conclusion

Introduction

Yes — you can generate PixVerse V6 video through a REST API. useapi.net provides a PixVerse API for PixVerse.ai that drives your own PixVerse account: you send a text or image prompt to one POST endpoint, poll a GET endpoint for the result, and get back an MP4.

The default model is the native PixVerse V6 — up to 15-second 1080p clips with integrated audio and multi-shot storytelling — and the same videos/create endpoint also exposes the rest of the native family (v5.6, v5.5, v5, v5-fast, pixverse-c1) plus third-party models such as Seedance 2.0, Kling O3/V3, Veo 3.1, Sora 2, Grok Imagine, and HappyHorse — all selectable with a single model field. This guide shows the two-call video workflow with copy-paste curl, then a runnable Node.js script that batch-generates from a list of prompts.

Generate a video in two API calls

You need a useapi.net API token and a connected PixVerse account. Generation is asynchronous: the create call returns a video_id immediately, then you poll until the video is ready.

1. Submit the jobPOST https://api.useapi.net/v2/pixverse/videos/create:

curl -X POST "https://api.useapi.net/v2/pixverse/videos/create" \
  -H "Authorization: Bearer $USEAPI_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "v6",
    "prompt": "A red panda eating bamboo in a misty forest, camera slowly pushing in",
    "quality": "720p",
    "duration": 5,
    "aspect_ratio": "16:9"
  }'

The response returns immediately with a video_id:

{
  "video_id": "user:1234-pixverse:jo***@gmail.com-video:112233445566"
}

2. Poll for the resultGET https://api.useapi.net/v2/pixverse/videos/{video_id}:

curl "https://api.useapi.net/v2/pixverse/videos/VIDEO_ID" \
  -H "Authorization: Bearer $USEAPI_TOKEN"

While the video is still processing this endpoint returns 404. Once it is done it returns 200 with video_status_name: "COMPLETED" and video_status_final: true. The finished MP4 is in the url field:

{
  "video_id": "user:1234-pixverse:jo***@gmail.com-video:112233445566",
  "model": "v6",
  "prompt": "A red panda eating bamboo in a misty forest, camera slowly pushing in",
  "quality": "720p",
  "duration": 5,
  "url": "https://media.pixverse.ai/....mp4",
  "video_status_name": "COMPLETED",
  "video_status_final": true
}

Prefer not to poll? Pass a replyUrl in the create body to receive a webhook callback (same JSON shape) when the video completes or fails.

Supported models

Pick a model per request with the model field (default v6). Native PixVerse models support integrated audio, the off-peak discount, preview mode, and seed; see Model Capabilities for the full per-model quality / duration / aspect-ratio matrix.

Model id Highlights Qualities Durations
v6 (default) Multi-shot, integrated audio, longest clips 360p, 540p, 720p, 1080p 1–15s
v5.6 Integrated audio 360p, 540p, 720p, 1080p 1–10s (1080p ≤ 8s)
v5.5 Integrated audio 360p, 540p, 720p, 1080p 1–10s (1080p ≤ 8s)
v5 Lip-sync TTS + sound-effect prompts 360p, 540p, 720p, 1080p 1–10s (1080p ≤ 8s)
v5-fast Fastest native model 360p, 540p, 720p, 1080p 1–10s (1080p ≤ 8s)
pixverse-c1 Wide aspect-ratio support, 15s 360p, 540p, 720p, 1080p 1–15s

Beyond the native family, the same videos/create endpoint accepts third-party models via the model field: seedance-2.0 / seedance-2.0-fast (ByteDance), kling-o3 / kling-v3 (Kuaishou), veo-3.1-lite / veo-3.1-standard / veo-3.1-fast (Google), sora-2 / sora-2-pro (OpenAI), grok-imagine (xAI), and happyhorse-1.0 (Alibaba). Each has its own quality, duration, and feature constraints — see Model Capabilities.

Audio, multi-shot & effects

Integrated audio. Set audio: true to generate voice, dialogue, and background sound in a single step. It is a toggle on v6, v5.6, v5.5, pixverse-c1, the Seedance models, and the Kling models; on veo-3.1-standard, veo-3.1-fast, and happyhorse-1.0 audio is always on. (Legacy v5 instead uses lip_sync_tts_prompt and sound_effect_prompt.)

Multi-shot storytelling. On v6 only, set multi_shot: true to have the model generate multiple camera angles and scene cuts within one clip instead of a single continuous shot.

900+ effect templates. List the catalog with GET videos/effects, then pass the chosen template_id to videos/create along with the required input image(s). When template_id is set, the template controls model, duration, and quality — you only supply the frame path(s).

For more workflows on native PixVerse models, the API also offers image-to-video (upload a frame via POST files, pass it as first_frame_path), extend, upscale, lip-sync, modify, and motion-control.

Pricing

You keep your own PixVerse subscription (Pro, Premium, or Ultra) and add a single flat $15/month to useapi.net that covers API access to every supported service — there are no per-call API surcharges.

Each generation spends credits from your PixVerse plan. For native PixVerse models you can cut that cost with off_peak_mode: true (results usually delivered within 24 hours): Pro 30% off, Premium 50% off, Ultra unlimited free. preview_mode: true is another native-only option — a fast, lower-quality preview at 20% off that you can later upscale. Third-party models don’t support off-peak or preview mode; Ultra subscribers get a 40% discount on them instead. Pro+ plans additionally include unlimited image generation in Relax Mode for select models (Ultra covers all of them).

For an interactive per-model, per-plan cost calculator, see the PixVerse API overview.

Batch-generate with a script

Finding the right shot takes many attempts, and running them by hand is tedious. The Node.js script below reads a list of prompts from prompts.json, uploads any input frame images, submits each videos/create job, then polls and downloads every finished MP4 — so you can queue a batch and come back to the winners.

You need Node.js v21 or newer. Put prompts.json and pixverse.mjs in the same folder and run node ./pixverse.mjs API_TOKEN EMAIL, where API_TOKEN is your useapi.net API token and EMAIL is your connected PixVerse account email. The script looks the account up by email automatically.

Expand prompts.json
[
    {
        "prompt": "By default the v6 model with a 5-second 720p 16:9 clip will be used."
    },
    {
        "model": "v6",
        "duration": 15,
        "quality": "1080p",
        "aspect_ratio": "16:9",
        "audio": true,
        "multi_shot": true,
        "prompt": "PixVerse V6 with integrated audio and multi-shot storytelling. A dramatic courtroom scene with multiple camera angles. For all parameters see https://useapi.net/docs/api-pixverse-v2/post-pixverse-videos-create-v4"
    },
    {
        "model": "v5.6",
        "duration": 8,
        "quality": "1080p",
        "first_frame_path": "./first_frame.webp",
        "audio": true,
        "prompt": "Image-to-video: the local image is uploaded via POST files and passed as first_frame_path. Note: aspect_ratio is derived from the image and must NOT be set for i2v."
    },
    {
        "model": "seedance-2.0",
        "duration": 5,
        "quality": "720p",
        "aspect_ratio": "16:9",
        "prompt": "Third-party models (seedance-2.0, kling-o3, veo-3.1-fast, sora-2, grok-imagine, happyhorse-1.0, …) are selectable via the model field. They reject native-only flags such as off_peak_mode, preview_mode, multi_shot and seed."
    }
]
Expand pixverse.mjs script
/*

Script version 1.0, June 15, 2026

Script to batch-generate videos using prompts with the PixVerse API v2 by useapi.net 🚀
Uses the POST /videos/create endpoint (default model: v6) and polls GET /videos/{video_id}.
For more details visit https://useapi.net/docs/api-pixverse-v2/post-pixverse-videos-create-v4

Installation Instructions:
==========================

You need Node.js v21 or newer installed to run this script. Download and install Node.js from:

- Windows, macOS, Linux: https://nodejs.org/

After installation, verify by running the following command in a terminal:

   node -v

Running the Script:
===================

Usage: node pixverse.mjs <API_TOKEN> <EMAIL> [PROMPTS_FILE]

Replace API_TOKEN with your actual useapi.net API token, see https://useapi.net/docs/start-here/setup-useapi
Replace EMAIL with configured PixVerse email account, see https://useapi.net/docs/start-here/setup-pixverse
If optional PROMPTS_FILE not provided prompts.json will be used.

Example:
--------

node pixverse.mjs user:1234-abcdefhijklmnopqrstuv [email protected]

This command executes the script using API token user:1234-abcdefhijklmnopqrstuv with [email protected] PixVerse account email.

Changelog:
==========

- June 15, 2026: Initial release.

*/

import readline from 'node:readline';
import fs from 'fs/promises';
import { writeFile } from 'node:fs/promises';
import { Readable } from 'node:stream';


// Constants
const RESULTS_FILE = 'pixverse_results.txt';
const ERRORS_FILE = 'pixverse_errors.txt';
const DEFAULT_PROMPTS_FILE = 'prompts.json';
const DEFAULT_MODEL = 'v6';
const SLEEP_429 = 30 * 1000; // in milliseconds
const SLEEP_POLL = 15 * 1000; // in milliseconds

const urlAccounts = 'https://api.useapi.net/v2/pixverse/accounts';
const urlCreate = 'https://api.useapi.net/v2/pixverse/videos/create';
const urlVideo = 'https://api.useapi.net/v2/pixverse/videos/'; // + video_id (poll)
const urlFiles = 'https://api.useapi.net/v2/pixverse/files/'; // + ?email= (upload)

// PixVerse accepts these image extensions for uploaded frames.
const supportedFileExtensions = ['png', 'jpeg', 'jpg', 'gif', 'webp'];

// { filename: path }
const uploadedFiles = {};

// Utility to sleep for given milliseconds
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

// Function to fetch configured PixVerse API accounts
async function fetchAccounts(apiToken) {
    const response = await fetch(urlAccounts, {
        headers: {
            'Accept': 'application/json',
            'Authorization': `Bearer ${apiToken}`
        }
    });

    if (!response.ok) {
        console.error(`⛔ Error fetching accounts (HTTP ${response.status}): ${response.statusText}`);
        process.exit(1);
    }

    return response.json();
}

const elapsedTimeSec = (start) => (Date.now() - start) / 1000;

// Map a file extension to the Content-Type required by POST /files
const contentTypeForExt = (ext) =>
    ext === 'png' ? 'image/png' :
    ext === 'gif' ? 'image/gif' :
    ext === 'webp' ? 'image/webp' : 'image/jpeg';

// Upload a local image and return its PixVerse `path` (used as first_frame_path etc.)
async function uploadFile(apiToken, email, filename) {

    // Check if already uploaded
    if (uploadedFiles.hasOwnProperty(filename))
        return uploadedFiles[filename];

    const startTime = Date.now();

    console.log(`⬆️  Account ${email} uploading file…`, filename);

    const body = new Blob([await fs.readFile(filename)]);

    const fileExt = filename.split('.').pop().toLowerCase();

    const response = await fetch(`${urlFiles}?email=${encodeURIComponent(email)}`, {
        method: 'POST',
        headers: {
            'Accept': 'application/json',
            'Authorization': `Bearer ${apiToken}`,
            'Content-Type': contentTypeForExt(fileExt)
        },
        body
    });

    if (response.ok) {
        const json = await response.json();
        // Image uploads return result[0].path; use that as the frame path.
        const path = json?.result?.[0]?.path ?? json?.path;
        console.log(`🆗 path (${elapsedTimeSec(startTime)} sec)`, path);
        uploadedFiles[filename] = path;
    }
    else {
        console.error(`❗ Unable to upload file HTTP ${response.status} (${elapsedTimeSec(startTime)} sec)`, await response.text());
        // Do not attempt to upload failed file again
        uploadedFiles[filename] = undefined;
    }

    return uploadedFiles[filename];
}

async function submit(apiToken, url, body, index, prompt) {
    const createResponse = await fetch(url, {
        method: 'POST',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
            'Authorization': `Bearer ${apiToken}`
        },
        body
    });

    const createBody = await createResponse.text();

    // POST /videos/create returns 200 with a video_id to poll.
    if (createResponse.status == 200) {
        const json = JSON.parse(createBody);
        const videoId = json.video_id ?? json.image_id;
        if (videoId) {
            await fs.appendFile(RESULTS_FILE, `${videoId},#${index}:${prompt}\n`);
            console.log(`✅ video_id`, videoId);
            return 200;
        } else {
            const error = `No video_id found in HTTP 200 response`;
            console.log(`❓ ${error}`, createBody);
            await fs.appendFile(ERRORS_FILE, `${error},#${index}:${prompt}\n`);
            return 500;
        }
    } else {
        switch (createResponse.status) {
            case 429:
                console.log(`🔄️ Account busy / concurrent limit, retry on HTTP ${createResponse.status}`, createBody);
                break;
            case 400:
                console.log(`🛑 Rejected request (validation)`, createBody);
                await fs.appendFile(ERRORS_FILE, `${createResponse.status},#${index}:${prompt}\n`);
                break;
            case 412:
                console.log(`🛑 Insufficient credits`, createBody);
                break;
            case 422:
                console.log(`🛑 Moderated prompt`, createBody);
                await fs.appendFile(ERRORS_FILE, `${createResponse.status},#${index}:${prompt}\n`);
                break;
            default:
                console.log(`❗ FAILED with HTTP ${createResponse.status}`, createBody);
                await fs.appendFile(ERRORS_FILE, `${createResponse.status},#${index}:${prompt}\n`);
        }
        return createResponse.status;
    }
}

// Submit a single prompt to POST /videos/create.
// first_frame_path (uploaded image) switches to image-to-video; aspect_ratio is then derived from the image.
async function submitVideo(apiToken, email, prompt, index) {
    const {
        model, prompt: text, first_frame_path, duration, quality, aspect_ratio,
        audio, multi_shot, preview_mode, off_peak_mode, seed, template_id
    } = prompt;

    const useModel = model ?? DEFAULT_MODEL;

    console.log(`🚀 ${useModel} » Prompt #${index} • account ${email} …`);

    const framePath = first_frame_path ? await uploadFile(apiToken, email, first_frame_path) : undefined;

    const body = JSON.stringify({
        model: useModel,
        email,
        prompt: text,
        first_frame_path: framePath,
        duration,
        quality,
        // aspect_ratio applies only to text-to-video and is optional (the server defaults to 16:9 when omitted); for image-to-video it is derived from the image
        aspect_ratio: framePath ? undefined : aspect_ratio,
        audio,
        multi_shot,
        preview_mode,
        off_peak_mode,
        seed,
        template_id
    });

    return await submit(apiToken, urlCreate, body, index, text);
}

// Function to poll and download videos
async function download(apiToken) {
    if (! await fileExists(RESULTS_FILE)) return;

    try {
        const resultsContent = await fs.readFile(RESULTS_FILE, 'utf8');
        const lines = resultsContent.trim().split('\n');

        for (const line of lines) {
            const [videoId, prompt] = line.split(',');

            console.log(`👉 ${videoId}`);

            while (true) {
                const response = await fetch(`${urlVideo}${videoId}`, {
                    headers: {
                        'Accept': 'application/json',
                        'Authorization': `Bearer ${apiToken}`
                    }
                });

                // 404 means the video is still processing — keep waiting.
                if (response.status == 404) {
                    console.log(`⌛ ${videoId} still processing, waiting…`);
                    await sleep(SLEEP_POLL);
                    continue;
                }

                if (!response.ok) {
                    console.log(`🛑 Poll failed ${videoId} (HTTP ${response.status}):\n${prompt}\n`, await response.text());
                    break;
                }

                const job = await response.json();
                const { url, video_status_name, video_status_final, error } = job;

                if (error) {
                    console.error(`🛑 FAILED ${videoId} (${error}):\n${prompt}\n`);
                    break;
                }

                // Poll until the status is final (COMPLETED, MODERATED, etc.).
                if (!video_status_final) {
                    console.log(`⌛ ${videoId} status (${video_status_name}) and is still in progress, waiting…`);
                    await sleep(SLEEP_POLL);
                    continue;
                }

                if (video_status_name !== 'COMPLETED' || !url) {
                    console.error(`🛑 ${videoId} finished as ${video_status_name} with no url:\n${prompt}\n`);
                    break;
                }

                const videoFilename = `${videoId.replace(/[:*]/g, '_')}.mp4`;

                try {
                    await fs.access(videoFilename);
                    console.log(`⚠️ ${videoFilename} already exists. Skipping download.`);
                    break;
                } catch {
                    // File does not exist, proceed with downloading
                }

                console.log(`✅ Downloading ${url} to ${videoFilename}`);
                try {
                    const videoResponse = await fetch(url);
                    if (!videoResponse.ok) {
                        console.error(`⛔ Unable to download ${videoId} (HTTP ${videoResponse.status}):\n${prompt}\n`, url);
                        break;
                    }
                    const stream = Readable.fromWeb(videoResponse.body);
                    await writeFile(videoFilename, stream);
                } catch (err) {
                    console.error(`⛔ Error during download: ${err}`);
                }

                break;
            }
        }
    } catch (error) {
        console.log(`⛔ Error during download:`, error.stack || error);
    }
}

// Main function
async function main() {
    const apiToken = process.argv[2];
    const email = process.argv[3];
    const promptFile = process.argv[4] || DEFAULT_PROMPTS_FILE;

    if (!apiToken || !email) {
        console.error('Usage: node pixverse.mjs <API_TOKEN> <EMAIL> [PROMPTS_FILE]');
        process.exit(1);
    }

    console.info('Script v1.0');

    console.info('Node version is: ' + process.version);

    try {
        if (await fileExists(RESULTS_FILE)) {
            let user_input;
            while (!['y', 'n'].includes(user_input)) {
                user_input = (await promptUser(`❔ ${RESULTS_FILE} file detected. Do you want to download the results now? (y/n): `))?.toLowerCase();
                if (user_input == 'y') {
                    await download(apiToken);
                    await fs.unlink(RESULTS_FILE);
                }
            }
        }

        const start = new Date();
        try {
            console.info('START EXECUTION', start);
            await execute(apiToken, email, promptFile); // Pass the promptFile to execute function
        }
        finally {
            console.info('COMPLETED', new Date());
            console.info('EXECUTION ELAPSED', diffInMinutesAndSeconds(start, new Date()));
        }

        try {
            console.info('START DOWNLOAD', start);
            await download(apiToken);
        }
        finally {
            console.info('TOTAL ELAPSED', diffInMinutesAndSeconds(start, new Date()));
        }
    } catch (error) {
        console.error('⛔ Error during execution:', error.stack || error);
    }
}

// Modify the execute function to accept promptFile as a parameter
async function execute(apiToken, email, promptFile) {
    const accounts = await fetchAccounts(apiToken);

    console.info(`Configured PixVerse API accounts (${Object.keys(accounts).length}):`, Object.keys(accounts).join(', '));

    if (Object.keys(accounts).length <= 0) {
        console.error(`⛔ No configured PixVerse accounts found. Please refer to https://useapi.net/docs/start-here/setup-pixverse`);
        process.exit(1);
    }

    if (!accounts[email]) {
        console.error(`⛔ Account ${email} not found. Please refer to https://useapi.net/docs/start-here/setup-pixverse`);
        process.exit(1);
    }

    const promptData = await fs.readFile(promptFile, 'utf8');
    const prompts = JSON.parse(promptData);
    console.log(`Total number of prompts to process`, prompts.length);

    let warnings = [];

    // Parameters accepted by this script for the POST /videos/create endpoint.
    // See https://useapi.net/docs/api-pixverse-v2/post-pixverse-videos-create-v4 for the full parameter set.
    const supportedParams = ['model', 'prompt', 'first_frame_path', 'duration', 'quality', 'aspect_ratio',
        'audio', 'multi_shot', 'preview_mode', 'off_peak_mode', 'seed', 'template_id'];

    const invalidKeys = (prompt) => Object.keys(prompt).filter(key => !key.startsWith('__') && !supportedParams.includes(key))

    for (let i = 1; i <= prompts.length; i++) {
        const prompt = prompts[i - 1];
        const { prompt: text, first_frame_path, template_id } = prompt;

        if (first_frame_path) {
            try {
                await fs.access(first_frame_path);
            } catch {
                warnings.push(`⚠️  Image '${first_frame_path}' does not exist. Prompt ${i}`);
            }

            const ext = first_frame_path.split('.').pop().toLowerCase();

            if (!supportedFileExtensions.includes(ext))
                warnings.push(`⚠️  Image ${first_frame_path} extension ${ext} not supported. Prompt ${i}`);
        }

        const notSupported = invalidKeys(prompt);
        if (notSupported.length)
            warnings.push(`⚠️  Following params not supported: ${notSupported.join(',')}. Prompt ${i}`);

        // prompt is optional only when a template_id supplies the generation.
        if (!text && !template_id)
            warnings.push(`⚠️  prompt is required (unless template_id is set). Prompt ${i}`);
    }

    if (warnings.length > 0) {
        warnings.forEach(warning => console.warn(warning));
        console.error(`⛔ Execution stopped due to warnings.`);
        process.exit(1);
    }

    for (let i = 0; i < prompts.length; i++) {
        const prompt = prompts[i];
        while (true) {
            const responseCode = await submitVideo(apiToken, email, prompt, i + 1);
            if (responseCode == 429)
                await sleep(SLEEP_429);
            else
                if (responseCode == 412) {
                    process.exit(1);
                } else
                    break;
        }
    }
}

// Utility function to check if a file exists
async function fileExists(path) {
    try {
        await fs.access(path);
        return true;
    } catch {
        return false;
    }
}

// Function to prompt user input
async function promptUser(query) {
    const rl = readline.createInterface({
        input: process.stdin,
        output: process.stdout
    });

    return new Promise((resolve) => rl.question(query, answer => {
        rl.close();
        resolve(answer);
    }));
}

function diffInMinutesAndSeconds(date1, date2) {
    const diffInSeconds = Math.floor((date2 - date1) / 1000);
    return `${Math.floor(diffInSeconds / 60)} minutes ${diffInSeconds % 60} seconds`;
};

main();

Examples

Real PixVerse clips generated through this PixVerse API, taken straight from our release notes.

v6 » Dramatic courtroom scene (text-to-video, 15s 1080p, multi-shot + audio)

From PixVerse v6: Multi-Shot Storytelling and Audio. Generated with multi_shot: true and audio: true — multiple camera angles and synchronized dialogue in a single 15-second clip.

v5.6 » Playing guitar and singing (image-to-video, integrated audio)

From PixVerse v5.6: Integrated Audio Generation. Animated from a still passed as first_frame_path with audio: true.

v5.6 » Motion Control (retarget motion onto a character image)

From PixVerse: Motion Control. A still character image (frame_1_path) is driven by motion extracted from a reference video (video_1_path) via POST videos/motion-control.

Frequently asked questions

Is there a PixVerse API? Yes — two ways. PixVerse.ai offers an official platform API billed against a separate API subscription. Or use useapi.net’s PixVerse API, which drives your own consumer PixVerse account through a standard REST endpoint. The differentiators: you spend your existing web subscription credits (with the Off-Peak discount, which the official API does not support), you get the full 900+ effect catalog instead of the official API’s 1/3/5 options, and the same single endpoint also exposes third-party models (Seedance, Kling, Veo 3.1, Sora 2, Grok, HappyHorse) that you would otherwise call through separate providers.

What models can I use? The default is native PixVerse V6. The same videos/create endpoint also accepts v5.6, v5.5, v5, v5-fast, and pixverse-c1, plus third-party seedance-2.0 / seedance-2.0-fast, kling-o3 / kling-v3, veo-3.1-lite / veo-3.1-standard / veo-3.1-fast, sora-2 / sora-2-pro, grok-imagine, and happyhorse-1.0. See Supported models and Model Capabilities.

Can I generate audio and multi-shot video? Yes. Set audio: true for integrated voice, dialogue, and background sound (a toggle on v6, v5.6, v5.5, pixverse-c1, Seedance, and Kling; always on for veo-3.1-standard / veo-3.1-fast / happyhorse-1.0). Multi-shot storytelling is v6-only via multi_shot: true. See Audio, multi-shot & effects.

Can I do image-to-video? Yes. Upload your starting frame with POST files, then pass the returned path as first_frame_path on videos/create. For image-to-video, aspect_ratio is derived from the image and must not be set.

How much does it cost? You keep your own PixVerse subscription and add a flat $15/month to useapi.net for API access to all services. Each generation then spends PixVerse credits from your plan — and native models can run far cheaper with off_peak_mode (Pro 30% off, Premium 50% off, Ultra free) or preview_mode (20% off). See Pricing and the interactive calculator.

Do I need PixVerse credits, or are they included? Credits come from your own PixVerse plan — useapi.net does not resell them. The $15/month is purely for API access. A 412 response from videos/create means your PixVerse credits are exhausted.

Conclusion

Visit our Discord Server or Telegram Channel for any support questions and concerns.

Check our GitHub repo with code examples.

Cross posted