Skip to content

API > Integrate API into your product

Video Generation API v2 — Text, Image & Reference to Video

Base URL: https://api.phantaisia.com

All requests require a Bearer token in the Authorization header.

Authorization: Bearer <your_token>

Looking for the previous video endpoints? single_frame_video and first_last_frame_video are still available and documented in How to Use the Video API Endpoint. The endpoints on this page are the new generation: per-second pricing, free duration from 5 to 15 seconds, selectable resolution, and three generation modes.


Overview

There are three ways to generate a video:

Endpoint What it does Input you provide
POST /v1/run/text_to_video Generates a video from a text prompt alone Prompt (or a Video Filter)
POST /v1/run/image_to_video Animates a start image, optionally towards an end image Start frame + optional end frame — or a selfie + Video Filter
POST /v1/run/reference_to_video Transfers the motion of a reference video onto your subject, guided by up to three reference images Guest photo + optional reference images, reference video and audio

Every run endpoint returns a job ID immediately. Each mode has its own status endpoint — see Check Job Status.


Two ways to call each endpoint

Every endpoint can be called directly or with a Video Filter.

Mode How When to use it
Direct You send the prompt and all render settings in the request Full control, you manage prompts and settings in your own system
Video Filter You send a video_filter_id (UUID of a Video AI Filter created in the Filter Creator) and the gateway pulls the prompt, render settings and face swap filters from it Recommended. Set up the look once in the Filter Creator, then only send the guest's photo and the duration

Video Filters are created in the Filter Creator — see Faceswap: Video Filter. Each Video Filter has a generation mode that determines which endpoint it works with:

Video Filter generation mode Endpoint to call What the filter supplies
text_to_video /v1/run/text_to_video Prompt, resolution, aspect ratio
single_frame /v1/run/image_to_video Prompt, resolution, aspect ratio, one face swap filter (start frame)
first_last_frame /v1/run/image_to_video Prompt, resolution, aspect ratio, two face swap filters (start + end frame)
reference_to_video /v1/run/reference_to_video Prompt, resolution, aspect ratio, reference images, reference video, audio

Override rules when a filter is used:

  • resolution_mp and aspect_ratio sent in the request override the filter's values.
  • prompt in the request overrides the filter's prompt on text_to_video only. On image_to_video and reference_to_video the filter's prompt is always used.
  • On reference_to_video, video, audio and include_video_audio in the request override the filter's own. The filter's reference images win over reference_images in the request when the filter has any.

Shared parameters

These fields behave the same on all three endpoints.

Field Type Description
duration integer Video length in seconds, 5 to 15. Always required. Note: an integer, not "8s" as on the previous endpoints
resolution_mp number Output size in megapixels: 0.7 (Draft), 0.9 (HD), 1.5 (HD+), 2.0 (Full HD). Required for direct calls on text_to_video and image_to_video; otherwise falls back to the filter's quality, then 1.5
aspect_ratio string "1:1", "2:3", "3:2", "3:4", "4:3", "9:16", "16:9", "21:9". Required for direct calls on text_to_video and image_to_video; otherwise falls back to the filter's ratio, then "16:9"
video_filter_id string UUID of a Video AI Filter. Optional — see Two ways to call each endpoint
email string Optional. When set, the finished video link is emailed to this address once, as soon as the job completes. Useful for guests who leave the booth before the video is ready

Images (first_frame, last_frame, img, prompt_image, reference_images) accept either a base64 data URI (data:image/jpeg;base64,...) or a publicly accessible URL. Base64 images are uploaded to our storage before generation. Reference videos and audio (video, audio) must be public URLs.


Pricing

The new endpoints are billed per second of generated video, so the cost scales linearly with duration. Credits are deducted when the job is accepted; if you do not have enough credits the request fails with 422 before any job is created.

Endpoint Pricing category Quick rate Advanced rate Face swaps
text_to_video Text2filter 1 credit / second 1.5 credits / second none
image_to_video Text2filter 1 credit / second 1.5 credits / second + 1 credit per face swap run by the filter (1 for a single_frame filter, 2 for a first_last_frame filter)
reference_to_video Video2video 2 credits / second 2.5 credits / second none

Which rate applies? The rate is chosen by the Video Filter's video_model:

  • advanced → Advanced rate
  • simple, performance or any other value → Quick rate
  • Direct calls without a filter → always Quick rate

Examples

Request Calculation Cost
text_to_video, direct, 10 s 10 × 1 10 credits
image_to_video, single_frame filter on simple, 8 s 1 face swap + 8 × 1 9 credits
image_to_video, first_last_frame filter on advanced, 8 s 2 face swaps + 8 × 1.5 14 credits
reference_to_video, direct, 8 s 8 × 2 16 credits
reference_to_video, filter on advanced, 15 s 15 × 2.5 37.5 credits

For general credit information see What are the API prices and how much are my credits worth.


1. Text to Video

POST /v1/run/text_to_video

Generates a video from a text prompt. No input image is needed, so no face swap is run — this mode is for scenery, intros, idents and other non-personalized clips.

Request body — direct

{
  "prompt": "A hot air balloon drifts over a misty valley at sunrise.",
  "duration": 8,
  "resolution_mp": 1.5,
  "aspect_ratio": "16:9"
}

Request body — with a Video Filter

{
  "video_filter_id": "uuid-of-video-ai-filter",
  "duration": 8
}
Field Type Required Description
prompt string Direct: Yes Text description of the scene and motion. With a filter it is optional and overrides the filter's prompt
duration integer Yes 5–15 seconds
resolution_mp number Direct: Yes 0.7, 0.9, 1.5 or 2.0
aspect_ratio string Direct: Yes See Shared parameters
video_filter_id string No UUID of a text_to_video Video Filter
email string No Finished video link is emailed here once

Response — 200 OK

{
  "id": "t2v-3f9a1c2b7d4e",
  "status": "IN_QUEUE",
  "output": {}
}

Poll POST /v1/status/text_to_video/{job_id} with the id.

Errors

HTTP error_code Meaning
404 VIDEO_FILTER_NOT_FOUND No Video Filter with that video_filter_id
422 INSUFFICIENT_CREDITS Not enough credits for this request
422 — Validation error: duration outside 5–15, invalid resolution_mp / aspect_ratio, or prompt, resolution_mp, aspect_ratio missing on a direct call

2. Image to Video

POST /v1/run/image_to_video

Animates a start image into a video. Add an end image and the model interpolates the motion between the two frames, guided by your prompt. This is the successor of both single_frame_video and first_last_frame_video.

Request body — direct

{
  "prompt": "The subject turns towards the camera and smiles.",
  "duration": 8,
  "resolution_mp": 1.5,
  "aspect_ratio": "9:16",
  "first_frame": "<base64-or-url>",
  "last_frame": "<base64-or-url>"
}

Leave out last_frame to animate from the first frame alone.

Request body — with a Video Filter

{
  "video_filter_id": "uuid-of-video-ai-filter",
  "duration": 8,
  "img": "<base64-or-url-of-selfie>",
  "gender": 1
}

With a filter the gateway runs the face swap for you: the guest's selfie (img) is swapped through the filter's start-frame AI filter and, if the filter has one, its end-frame AI filter. The swapped images become the first and last frame — you do not send first_frame / last_frame yourself.

Field Type Required Description
prompt string Direct: Yes Text description of the motion. Ignored when a filter is used (the filter's prompt applies)
duration integer Yes 5–15 seconds
resolution_mp number Direct: Yes 0.7, 0.9, 1.5 or 2.0
aspect_ratio string Direct: Yes See Shared parameters
first_frame string Direct: Yes Start image, base64 or public URL
last_frame string No End image, base64 or public URL. Omit to animate from the start frame only
video_filter_id string No UUID of a single_frame or first_last_frame Video Filter
img string Filter: Yes The guest's selfie, base64 or public URL — the face that gets swapped in
gender integer No (default 0) 0 = Male, 1 = Female, 2 = Boy, 3 = Girl. Used for the face swaps
email string No Finished video link is emailed here once

Response — 200 OK

{
  "id": "i2v-8c21e4a90b7f",
  "status": "IN_QUEUE",
  "output": {}
}

Poll POST /v1/status/image_to_video/{job_id} with the id.

Errors

HTTP error_code Meaning
400 VIDEO_FILTER_MISSING_FILTER The Video Filter has no AI filter attached, so there is nothing to face swap (for example a text_to_video filter)
404 VIDEO_FILTER_NOT_FOUND No Video Filter with that video_filter_id
422 INSUFFICIENT_CREDITS Not enough credits for face swap(s) + video
422 — Validation error: duration outside 5–15, missing img with a filter, or prompt, resolution_mp, aspect_ratio, first_frame missing on a direct call

If a face swap fails, the video job ends in failed — see Check Job Status.


3. Reference to Video

POST /v1/run/reference_to_video

Generates a video where a reference video drives the motion and up to three reference images carry the subject. The guest's photo (prompt_image) is always the first reference image; up to two more can follow. No face swap is run in this mode — the model works from the reference images directly.

Request body — direct

{
  "prompt": "The subject walks through a neon-lit street.",
  "duration": 8,
  "resolution_mp": 1.5,
  "aspect_ratio": "9:16",
  "prompt_image": "<base64-or-url-of-guest-photo>",
  "reference_images": ["<base64-or-url-of-ref-1>", "<base64-or-url-of-ref-2>"],
  "video": "https://example.com/reference.mp4",
  "include_video_audio": true
}

Request body — with a Video Filter

{
  "video_filter_id": "uuid-of-video-ai-filter",
  "duration": 8,
  "prompt_image": "<base64-or-url-of-guest-photo>"
}
Field Type Required Description
prompt_image string Yes The guest's photo, base64 or public URL. Always the first reference image
duration integer Yes 5–15 seconds
prompt string Direct: Yes Text description of the scene and motion. Ignored when a filter is used (the filter's prompt applies)
reference_images string[] No Up to 2 further reference images, in order after the guest photo. Ignored when the filter has its own reference images
video string No Public URL of the reference video that drives the motion. Overrides the filter's reference video
include_video_audio boolean No Keep the reference video's audio in the output. Only has an effect together with a video. Overrides the filter's setting
audio string No Public URL of an audio track to use instead of the video's own audio. Overrides the filter's audio
resolution_mp number No 0.7, 0.9, 1.5 or 2.0. Falls back to the filter's quality, then 1.5
aspect_ratio string No (default "auto") "auto" takes the filter's ratio, otherwise "16:9". Or set one of the fixed ratios from Shared parameters
video_filter_id string No UUID of a reference_to_video Video Filter
email string No Finished video link is emailed here once

Response — 200 OK

{
  "id": "r2v-5d0b7e3a1c48",
  "status": "IN_QUEUE",
  "output": {}
}

Poll POST /v1/status/reference_to_video/{job_id} with the id.

Errors

HTTP error_code Meaning
400 VIDEO_FILTER_WRONG_MODE The Video Filter is not a reference_to_video filter
404 VIDEO_FILTER_NOT_FOUND No Video Filter with that video_filter_id
422 INSUFFICIENT_CREDITS Not enough credits for this request
422 — Validation error: duration outside 5–15, more than 2 reference_images, or prompt missing on a direct call

4. Check Job Status

Each mode has its own status endpoint. Use the one matching the run endpoint you called:

Run endpoint Status endpoint
/v1/run/text_to_video POST /v1/status/text_to_video/{job_id}
/v1/run/image_to_video POST /v1/status/image_to_video/{job_id}
/v1/run/reference_to_video POST /v1/status/reference_to_video/{job_id}

The status endpoints are non-blocking — they return the current state immediately. Keep polling until the job reaches a terminal status.

Recommended polling interval: every 5–10 seconds. Video jobs typically take 1–3 minutes; longer and higher-resolution videos take longer. A job that has not finished after 6 minutes of generation is marked failed.

Path parameter

Parameter Type Description
job_id string The id returned from the run endpoint (t2v-…, i2v-… or r2v-…)

Response — in progress

{
  "id": "i2v-8c21e4a90b7f",
  "status": "pending",
  "output": null
}

Response — completed

{
  "id": "i2v-8c21e4a90b7f",
  "status": "completed",
  "output": {
    "video_url": "https://..."
  }
}

Response — failed

{
  "id": "i2v-8c21e4a90b7f",
  "status": "failed",
  "output": null
}

Status values

Status Terminal Meaning
pending No Job is queued or actively generating (including any face swaps)
completed Yes Video is ready — output.video_url contains the download link
failed Yes Generation failed or timed out — stop polling. Credits are not refunded automatically; contact support if a job fails repeatedly

Note: The run endpoints answer with "status": "IN_QUEUE", while the status endpoints report the job state in lowercase (pending, completed, failed). Compare statuses case-insensitively to be safe.


End-to-end example (JavaScript)

const TOKEN = 'your_token_here';
const BASE  = 'https://api.phantaisia.com';

async function generateVideo(mode, body) {
  // 1. Submit job
  const runRes = await fetch(`${BASE}/v1/run/${mode}`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${TOKEN}`,
    },
    body: JSON.stringify(body),
  });

  if (!runRes.ok) throw new Error(`Submit failed: ${await runRes.text()}`);
  const { id } = await runRes.json();
  console.log('Job submitted:', id);

  // 2. Poll for result on the matching status endpoint
  while (true) {
    await new Promise(r => setTimeout(r, 7000));

    const statusRes = await fetch(`${BASE}/v1/status/${mode}/${id}`, {
      method: 'POST',
      headers: { 'Authorization': `Bearer ${TOKEN}` },
    });

    if (!statusRes.ok) throw new Error(`Status error: ${await statusRes.text()}`);
    const data = await statusRes.json();
    const status = String(data.status).toLowerCase();
    console.log('Status:', status);

    if (status === 'completed') {
      return data.output.video_url;
    }
    if (status === 'failed') {
      throw new Error('Generation failed');
    }
  }
}

// Text to video (direct)
const introUrl = await generateVideo('text_to_video', {
  prompt: 'A hot air balloon drifts over a misty valley at sunrise.',
  duration: 8,
  resolution_mp: 1.5,
  aspect_ratio: '16:9',
});

// Image to video with a Video Filter (face swap + animation)
const guestUrl = await generateVideo('image_to_video', {
  video_filter_id: 'uuid-of-video-ai-filter',
  duration: 8,
  img: 'https://example.com/selfie.jpg',
  gender: 1,
  email: 'guest@example.com', // optional: emails the link when ready
});

// Reference to video (direct)
const refUrl = await generateVideo('reference_to_video', {
  prompt: 'The subject walks through a neon-lit street.',
  duration: 8,
  resolution_mp: 1.5,
  aspect_ratio: '9:16',
  prompt_image: 'https://example.com/selfie.jpg',
  video: 'https://example.com/reference.mp4',
  include_video_audio: true,
});

Migrating from single_frame_video / first_last_frame_video

Previous endpoints New endpoints
/v1/run/single_frame_video with prompt_image /v1/run/image_to_video with first_frame
/v1/run/first_last_frame_video with first_frame + last_frame /v1/run/image_to_video with first_frame + last_frame
video_filter_id + prompt_image / first_frame (selfie) video_filter_id + img (selfie)
model: simple / advanced / performance No model field. Quality and price tier come from the Video Filter's video_model; direct calls are billed at the quick rate
duration: "4s" / "6s" / "8s" duration: integer 5–15
Fixed resolution resolution_mp: 0.7 / 0.9 / 1.5 / 2.0
aspect_ratio only on performance aspect_ratio on every request, 8 ratios
Flat price per model and duration Per-second pricing, see Pricing
/v1/status/single_frame_video/{job_id} for both One status endpoint per mode