curl --request POST \
--url https://apis.viggle.ai/v1/videos \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: multipart/form-data' \
--form 'prompt=A paper airplane gliding through a sunlit office' \
--form quality=low \
--form first_frame_image=@example-file \
--form 'first_frame_image_url=<string>' \
--form last_frame_image=@example-file \
--form 'last_frame_image_url=<string>' \
--form reference_video=@example-file \
--form 'reference_video_url=<string>' \
--form reference_image=@example-file \
--form 'reference_image_url=<string>' \
--form driving_video=@example-file \
--form 'driving_video_url=<string>' \
--form character_image=@example-file \
--form 'character_image_url=<string>' \
--form 'character_image_urls=<string>' \
--form priority=1000 \
--form duration_s=5 \
--form resolution=768p \
--form aspect_ratio=16:9 \
--form seed=1 \
--form watermark=false \
--form enhance=trueimport requests
url = "https://apis.viggle.ai/v1/videos"
files = {
"first_frame_image": ("example-file", open("example-file", "rb")),
"last_frame_image": ("example-file", open("example-file", "rb")),
"reference_video": ("example-file", open("example-file", "rb")),
"reference_image": ("example-file", open("example-file", "rb")),
"driving_video": ("example-file", open("example-file", "rb")),
"character_image": ("example-file", open("example-file", "rb"))
}
payload = {
"prompt": "A paper airplane gliding through a sunlit office",
"quality": "low",
"first_frame_image_url": "<string>",
"last_frame_image_url": "<string>",
"reference_video_url": "<string>",
"reference_image_url": "<string>",
"driving_video_url": "<string>",
"character_image_url": "<string>",
"character_image_urls": "<string>",
"priority": "1000",
"duration_s": "5",
"resolution": "768p",
"aspect_ratio": "16:9",
"seed": "1",
"watermark": "false",
"enhance": "true"
}
headers = {"Authorization": "Bearer <token>"}
response = requests.post(url, data=payload, files=files, headers=headers)
print(response.text)const form = new FormData();
form.append('prompt', 'A paper airplane gliding through a sunlit office');
form.append('quality', 'low');
form.append('first_frame_image', '<string>');
form.append('first_frame_image_url', '<string>');
form.append('last_frame_image', '<string>');
form.append('last_frame_image_url', '<string>');
form.append('reference_video', '<string>');
form.append('reference_video_url', '<string>');
form.append('reference_image', '<string>');
form.append('reference_image_url', '<string>');
form.append('driving_video', '<string>');
form.append('driving_video_url', '<string>');
form.append('character_image', '<string>');
form.append('character_image_url', '<string>');
form.append('character_image_urls', '<string>');
form.append('priority', '1000');
form.append('duration_s', '5');
form.append('resolution', '768p');
form.append('aspect_ratio', '16:9');
form.append('seed', '1');
form.append('watermark', 'false');
form.append('enhance', 'true');
const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};
options.body = form;
fetch('https://apis.viggle.ai/v1/videos', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://apis.viggle.ai/v1/videos",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"prompt\"\r\n\r\nA paper airplane gliding through a sunlit office\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"quality\"\r\n\r\nlow\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"first_frame_image\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"first_frame_image_url\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"last_frame_image\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"last_frame_image_url\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"reference_video\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"reference_video_url\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"reference_image\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"reference_image_url\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"driving_video\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"driving_video_url\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"character_image\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"character_image_url\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"character_image_urls\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"priority\"\r\n\r\n1000\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"duration_s\"\r\n\r\n5\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"resolution\"\r\n\r\n768p\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"aspect_ratio\"\r\n\r\n16:9\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"seed\"\r\n\r\n1\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"watermark\"\r\n\r\nfalse\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"enhance\"\r\n\r\ntrue\r\n-----011000010111000001101001--",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: multipart/form-data"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://apis.viggle.ai/v1/videos"
payload := strings.NewReader("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"prompt\"\r\n\r\nA paper airplane gliding through a sunlit office\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"quality\"\r\n\r\nlow\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"first_frame_image\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"first_frame_image_url\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"last_frame_image\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"last_frame_image_url\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"reference_video\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"reference_video_url\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"reference_image\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"reference_image_url\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"driving_video\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"driving_video_url\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"character_image\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"character_image_url\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"character_image_urls\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"priority\"\r\n\r\n1000\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"duration_s\"\r\n\r\n5\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"resolution\"\r\n\r\n768p\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"aspect_ratio\"\r\n\r\n16:9\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"seed\"\r\n\r\n1\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"watermark\"\r\n\r\nfalse\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"enhance\"\r\n\r\ntrue\r\n-----011000010111000001101001--")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://apis.viggle.ai/v1/videos")
.header("Authorization", "Bearer <token>")
.body("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"prompt\"\r\n\r\nA paper airplane gliding through a sunlit office\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"quality\"\r\n\r\nlow\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"first_frame_image\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"first_frame_image_url\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"last_frame_image\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"last_frame_image_url\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"reference_video\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"reference_video_url\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"reference_image\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"reference_image_url\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"driving_video\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"driving_video_url\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"character_image\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"character_image_url\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"character_image_urls\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"priority\"\r\n\r\n1000\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"duration_s\"\r\n\r\n5\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"resolution\"\r\n\r\n768p\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"aspect_ratio\"\r\n\r\n16:9\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"seed\"\r\n\r\n1\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"watermark\"\r\n\r\nfalse\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"enhance\"\r\n\r\ntrue\r\n-----011000010111000001101001--")
.asString();require 'uri'
require 'net/http'
url = URI("https://apis.viggle.ai/v1/videos")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request.body = "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"prompt\"\r\n\r\nA paper airplane gliding through a sunlit office\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"quality\"\r\n\r\nlow\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"first_frame_image\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"first_frame_image_url\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"last_frame_image\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"last_frame_image_url\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"reference_video\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"reference_video_url\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"reference_image\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"reference_image_url\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"driving_video\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"driving_video_url\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"character_image\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"character_image_url\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"character_image_urls\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"priority\"\r\n\r\n1000\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"duration_s\"\r\n\r\n5\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"resolution\"\r\n\r\n768p\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"aspect_ratio\"\r\n\r\n16:9\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"seed\"\r\n\r\n1\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"watermark\"\r\n\r\nfalse\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"enhance\"\r\n\r\ntrue\r\n-----011000010111000001101001--"
response = http.request(request)
puts response.read_body{
"id": "<string>",
"status": "queued",
"progress": 50,
"created_at": "2023-11-07T05:31:56Z"
}{
"error": {
"code": "UNAUTHENTICATED",
"message": "<string>",
"retryable": true,
"request_id": "<string>",
"details": {},
"remediation": {
"action": "<string>",
"retry_after_ms": 1
}
}
}{
"error": {
"code": "UNAUTHENTICATED",
"message": "<string>",
"retryable": true,
"request_id": "<string>",
"details": {},
"remediation": {
"action": "<string>",
"retry_after_ms": 1
}
}
}{
"error": {
"code": "UNAUTHENTICATED",
"message": "<string>",
"retryable": true,
"request_id": "<string>",
"details": {},
"remediation": {
"action": "<string>",
"retry_after_ms": 1
}
}
}{
"error": {
"code": "UNAUTHENTICATED",
"message": "<string>",
"retryable": true,
"request_id": "<string>",
"details": {},
"remediation": {
"action": "<string>",
"retry_after_ms": 1
}
}
}{
"error": {
"code": "UNAUTHENTICATED",
"message": "<string>",
"retryable": true,
"request_id": "<string>",
"details": {},
"remediation": {
"action": "<string>",
"retry_after_ms": 1
}
}
}{
"error": {
"code": "UNAUTHENTICATED",
"message": "<string>",
"retryable": true,
"request_id": "<string>",
"details": {},
"remediation": {
"action": "<string>",
"retry_after_ms": 1
}
}
}{
"error": {
"code": "UNAUTHENTICATED",
"message": "<string>",
"retryable": true,
"request_id": "<string>",
"details": {},
"remediation": {
"action": "<string>",
"retry_after_ms": 1
}
}
}Using Viggle-Animate
Generate a character-animation video from character images — up to four on their own, or up to five with a driving video.
curl --request POST \
--url https://apis.viggle.ai/v1/videos \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: multipart/form-data' \
--form 'prompt=A paper airplane gliding through a sunlit office' \
--form quality=low \
--form first_frame_image=@example-file \
--form 'first_frame_image_url=<string>' \
--form last_frame_image=@example-file \
--form 'last_frame_image_url=<string>' \
--form reference_video=@example-file \
--form 'reference_video_url=<string>' \
--form reference_image=@example-file \
--form 'reference_image_url=<string>' \
--form driving_video=@example-file \
--form 'driving_video_url=<string>' \
--form character_image=@example-file \
--form 'character_image_url=<string>' \
--form 'character_image_urls=<string>' \
--form priority=1000 \
--form duration_s=5 \
--form resolution=768p \
--form aspect_ratio=16:9 \
--form seed=1 \
--form watermark=false \
--form enhance=trueimport requests
url = "https://apis.viggle.ai/v1/videos"
files = {
"first_frame_image": ("example-file", open("example-file", "rb")),
"last_frame_image": ("example-file", open("example-file", "rb")),
"reference_video": ("example-file", open("example-file", "rb")),
"reference_image": ("example-file", open("example-file", "rb")),
"driving_video": ("example-file", open("example-file", "rb")),
"character_image": ("example-file", open("example-file", "rb"))
}
payload = {
"prompt": "A paper airplane gliding through a sunlit office",
"quality": "low",
"first_frame_image_url": "<string>",
"last_frame_image_url": "<string>",
"reference_video_url": "<string>",
"reference_image_url": "<string>",
"driving_video_url": "<string>",
"character_image_url": "<string>",
"character_image_urls": "<string>",
"priority": "1000",
"duration_s": "5",
"resolution": "768p",
"aspect_ratio": "16:9",
"seed": "1",
"watermark": "false",
"enhance": "true"
}
headers = {"Authorization": "Bearer <token>"}
response = requests.post(url, data=payload, files=files, headers=headers)
print(response.text)const form = new FormData();
form.append('prompt', 'A paper airplane gliding through a sunlit office');
form.append('quality', 'low');
form.append('first_frame_image', '<string>');
form.append('first_frame_image_url', '<string>');
form.append('last_frame_image', '<string>');
form.append('last_frame_image_url', '<string>');
form.append('reference_video', '<string>');
form.append('reference_video_url', '<string>');
form.append('reference_image', '<string>');
form.append('reference_image_url', '<string>');
form.append('driving_video', '<string>');
form.append('driving_video_url', '<string>');
form.append('character_image', '<string>');
form.append('character_image_url', '<string>');
form.append('character_image_urls', '<string>');
form.append('priority', '1000');
form.append('duration_s', '5');
form.append('resolution', '768p');
form.append('aspect_ratio', '16:9');
form.append('seed', '1');
form.append('watermark', 'false');
form.append('enhance', 'true');
const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};
options.body = form;
fetch('https://apis.viggle.ai/v1/videos', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://apis.viggle.ai/v1/videos",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"prompt\"\r\n\r\nA paper airplane gliding through a sunlit office\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"quality\"\r\n\r\nlow\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"first_frame_image\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"first_frame_image_url\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"last_frame_image\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"last_frame_image_url\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"reference_video\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"reference_video_url\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"reference_image\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"reference_image_url\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"driving_video\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"driving_video_url\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"character_image\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"character_image_url\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"character_image_urls\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"priority\"\r\n\r\n1000\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"duration_s\"\r\n\r\n5\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"resolution\"\r\n\r\n768p\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"aspect_ratio\"\r\n\r\n16:9\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"seed\"\r\n\r\n1\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"watermark\"\r\n\r\nfalse\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"enhance\"\r\n\r\ntrue\r\n-----011000010111000001101001--",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: multipart/form-data"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://apis.viggle.ai/v1/videos"
payload := strings.NewReader("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"prompt\"\r\n\r\nA paper airplane gliding through a sunlit office\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"quality\"\r\n\r\nlow\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"first_frame_image\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"first_frame_image_url\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"last_frame_image\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"last_frame_image_url\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"reference_video\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"reference_video_url\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"reference_image\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"reference_image_url\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"driving_video\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"driving_video_url\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"character_image\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"character_image_url\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"character_image_urls\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"priority\"\r\n\r\n1000\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"duration_s\"\r\n\r\n5\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"resolution\"\r\n\r\n768p\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"aspect_ratio\"\r\n\r\n16:9\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"seed\"\r\n\r\n1\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"watermark\"\r\n\r\nfalse\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"enhance\"\r\n\r\ntrue\r\n-----011000010111000001101001--")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://apis.viggle.ai/v1/videos")
.header("Authorization", "Bearer <token>")
.body("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"prompt\"\r\n\r\nA paper airplane gliding through a sunlit office\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"quality\"\r\n\r\nlow\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"first_frame_image\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"first_frame_image_url\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"last_frame_image\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"last_frame_image_url\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"reference_video\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"reference_video_url\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"reference_image\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"reference_image_url\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"driving_video\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"driving_video_url\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"character_image\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"character_image_url\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"character_image_urls\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"priority\"\r\n\r\n1000\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"duration_s\"\r\n\r\n5\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"resolution\"\r\n\r\n768p\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"aspect_ratio\"\r\n\r\n16:9\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"seed\"\r\n\r\n1\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"watermark\"\r\n\r\nfalse\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"enhance\"\r\n\r\ntrue\r\n-----011000010111000001101001--")
.asString();require 'uri'
require 'net/http'
url = URI("https://apis.viggle.ai/v1/videos")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request.body = "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"prompt\"\r\n\r\nA paper airplane gliding through a sunlit office\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"quality\"\r\n\r\nlow\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"first_frame_image\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"first_frame_image_url\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"last_frame_image\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"last_frame_image_url\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"reference_video\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"reference_video_url\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"reference_image\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"reference_image_url\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"driving_video\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"driving_video_url\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"character_image\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"character_image_url\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"character_image_urls\"\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"priority\"\r\n\r\n1000\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"duration_s\"\r\n\r\n5\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"resolution\"\r\n\r\n768p\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"aspect_ratio\"\r\n\r\n16:9\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"seed\"\r\n\r\n1\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"watermark\"\r\n\r\nfalse\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"enhance\"\r\n\r\ntrue\r\n-----011000010111000001101001--"
response = http.request(request)
puts response.read_body{
"id": "<string>",
"status": "queued",
"progress": 50,
"created_at": "2023-11-07T05:31:56Z"
}{
"error": {
"code": "UNAUTHENTICATED",
"message": "<string>",
"retryable": true,
"request_id": "<string>",
"details": {},
"remediation": {
"action": "<string>",
"retry_after_ms": 1
}
}
}{
"error": {
"code": "UNAUTHENTICATED",
"message": "<string>",
"retryable": true,
"request_id": "<string>",
"details": {},
"remediation": {
"action": "<string>",
"retry_after_ms": 1
}
}
}{
"error": {
"code": "UNAUTHENTICATED",
"message": "<string>",
"retryable": true,
"request_id": "<string>",
"details": {},
"remediation": {
"action": "<string>",
"retry_after_ms": 1
}
}
}{
"error": {
"code": "UNAUTHENTICATED",
"message": "<string>",
"retryable": true,
"request_id": "<string>",
"details": {},
"remediation": {
"action": "<string>",
"retry_after_ms": 1
}
}
}{
"error": {
"code": "UNAUTHENTICATED",
"message": "<string>",
"retryable": true,
"request_id": "<string>",
"details": {},
"remediation": {
"action": "<string>",
"retry_after_ms": 1
}
}
}{
"error": {
"code": "UNAUTHENTICATED",
"message": "<string>",
"retryable": true,
"request_id": "<string>",
"details": {},
"remediation": {
"action": "<string>",
"retry_after_ms": 1
}
}
}{
"error": {
"code": "UNAUTHENTICATED",
"message": "<string>",
"retryable": true,
"request_id": "<string>",
"details": {},
"remediation": {
"action": "<string>",
"retry_after_ms": 1
}
}
}multipart/form-data — or a plain application/json body — with one or more character images (character_image/character_image_url/character_image_urls) and, optionally, a driving video (driving_video/driving_video_url). Supplying any of these fields selects the Viggle-Animate character-animation mode of POST /v1/videos.
multipart/form-data.POST /v1/videos endpoint as Generate Video (from Text), Generate Video (from First Frame and/or Text), Generate Video (from First-Last Frames and/or Text), and Generate Video (from Reference Video/Image) — which mode runs is selected by which fields you send, not by a separate parameter. Character-animation fields cannot be combined with quality or any frame/reference field from the other four modes; doing so answers 400 INVALID_REQUEST with “driving_video_url/character_image_url cannot be combined with quality”.
duration_s for this mode, and no quality, resolution, aspect_ratio, or seed. See Pricing.Request parameters
Sendmultipart/form-data, or JSON (see below).
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
driving_video | file | No | — | Optional driving video uploaded directly. Mutually exclusive with driving_video_url. When present, up to five character images are allowed, one per person to replace. Must be 5–15 seconds, at least 64x64, with even dimensions. |
driving_video_url | string (URI) | No | — | Optional https:// driving-video URL. Mutually exclusive with driving_video. When present, up to five character images are allowed, one per person to replace. |
character_image | file (repeatable) | One of | — | Uploaded character reference images: one to four without a driving video, one to five with one. Mutually exclusive with URL-form character images. |
character_image_url | string (URI), repeatable | One of | — | Character image URLs: one to four without a driving video, one to five with one. Mutually exclusive with character_image_urls and uploaded images. |
character_image_urls | array of string (URI) | One of | — | Same as character_image_url, supplied as one JSON-array field. One to four entries without a driving video; one to five with it. |
prompt | string | No | — | Optional style/content guidance. Unlike every other mode, not required and not enforced non-empty. |
watermark | boolean | No | false | Whether to burn in a Viggle watermark. Same field and convention as the other four modes. |
priority | integer | No | 1000 | Admission-queue priority tier, shared with H3 modes. Must be one of the server’s configured tiers (currently 1000 or 0); an unsupported value answers 400 INVALID_REQUEST — “unsupported priority”, and a non-integer answers “priority must be an integer”. Most callers never need to set this. |
400 INVALID_REQUEST — “at most 4 character images are allowed” (or “at most 5 …” with a driving video).
Driving video vs. character image
- Driving video (
driving_videoordriving_video_url): optional. If supplied, use exactly one of the file or URL form. It must be 5–15 seconds long (inclusive, truncated to whole seconds first), at least 64x64, with even width and height. - Character images (
character_image,character_image_url, orcharacter_image_urls): at least one is required. Supply uploads or URLs, not both; the two URL spellings are mutually exclusive. Without a driving video, up to four images are accepted, all references of one character. With a driving video, up to five are accepted, and each image replaces one person in the video.
https://; anything else (a bare http:// or a gs:// path, for example) answers 400 INVALID_REQUEST with “each character_image_url must be an https:// URL” (or the driving_video_url equivalent). An uploaded character_image that isn’t a valid, decodable image answers 400 INVALID_REQUEST — “character_image must be a valid image”.
JSON request body
Character-animation mode alone also accepts a plain JSON body (Content-Type: application/json) as an alternative to multipart/form-data, native arrays included. JSON requests can only carry URL-form fields — driving_video/character_image uploads have no bytes to attach to a JSON body, so use driving_video_url and character_image_url/character_image_urls instead:
{
"driving_video_url": "https://example.test/driving.mp4",
"character_image_url": "https://example.test/char1.png",
"prompt": "keep the same outfit and background",
"watermark": false
}
character_image_url or character_image_urls; driving_video_url is optional. An empty or malformed body answers 400 INVALID_REQUEST — “request body must be a JSON object” (or “JSON animation requests require character images” when no character-image field is present).
Since JSON object keys cannot repeat, use character_image_urls when sending two to four character images in JSON.
Response parameters
Returns200 OK — an acceptance acknowledgment, not the final result.
| Field | Type | Always present | Description |
|---|---|---|---|
id | string | Yes | Public video ID, anim_-prefixed. |
status | string | Yes | Always queued on acceptance. |
progress | integer or null | Yes | Always null on acceptance. |
created_at | string | Yes | ISO 8601 creation timestamp, second precision. |
{
"id": "anim_7c1e4b",
"status": "queued",
"progress": null,
"created_at": "2026-09-09T09:12:03Z"
}
GET /v1/videos/{video_id} returns (video_url, completed_at, error). See Get Video for the full shape once the video is processing or done — character-animation’s response there carries no seed (H3-only) alongside the fields every video shares.
Pricing
Character-animation mode is billed at 1 credit ($0.01) per second, rounded up. What the request reserves depends on whether it has a driving video:- With a driving video, the service measures the driving video’s duration at submission and reserves ⌈duration⌉ credits — 5–16 credits (0.05–0.16). The duration check truncates to whole seconds before comparing against 5–15, so a 15.9-second clip is accepted and reserves 16.
- Without a driving video (images only), there’s nothing to measure, so every request reserves a fixed 11 credits ($0.11).
ready, the charge is settled against the finished video’s duration at the same rate, capped at the reservation — settlement can only refund, never charge more than was reserved. A failed task is refunded in full. watermark does not affect price. See Pricing and retention.
Examples
curl https://apis.viggle.ai/v1/videos \
-H "Authorization: Bearer $VIGGLE_API_KEY" \
-F character_image_url=https://example.test/char1.png \
-F character_image_url=https://example.test/char2.png
curl https://apis.viggle.ai/v1/videos \
-H "Authorization: Bearer $VIGGLE_API_KEY" \
-F driving_video_url=https://example.test/driving.mp4 \
-F character_image_url=https://example.test/char1.png
curl https://apis.viggle.ai/v1/videos \
-H "Authorization: Bearer $VIGGLE_API_KEY" \
-F prompt="keep the same outfit and background" \
-F [email protected] \
-F [email protected]
curl https://apis.viggle.ai/v1/videos \
-H "Authorization: Bearer $VIGGLE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"driving_video_url": "https://example.test/driving.mp4",
"character_image_url": "https://example.test/char1.png"
}'
import os, requests
response = requests.post(
"https://apis.viggle.ai/v1/videos",
headers={"Authorization": f"Bearer {os.environ['VIGGLE_API_KEY']}", "Content-Type": "application/json"},
json={
"driving_video_url": "https://example.test/driving.mp4",
"character_image_url": "https://example.test/char1.png",
},
)
response.raise_for_status()
video = response.json()
const response = await fetch("https://apis.viggle.ai/v1/videos", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.VIGGLE_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
driving_video_url: "https://example.test/driving.mp4",
character_image_url: "https://example.test/char1.png",
}),
});
const video = await response.json();
Common errors
| Error | Cause |
|---|---|
400 INVALID_REQUEST — “character images are required” | Neither character_image_url nor character_image_urls supplied, and no character_image uploaded. |
400 INVALID_REQUEST — “supply only one of character_image_url or character_image_urls” | Both URL fields supplied together. |
400 INVALID_REQUEST — “supply character images as uploads or as character_image_url/character_image_urls, not both” | A character_image upload supplied alongside character_image_url/character_image_urls. |
400 INVALID_REQUEST — “at most 4 character images are allowed” | More than four character images supplied without a driving video. |
400 INVALID_REQUEST — “at most 5 character images are allowed” | More than five character images supplied together with a driving video. |
400 INVALID_REQUEST — “each character_image_url must be an https:// URL” (or “…character_image_urls…”, matching whichever field you used) | A character image URL is not an https:// URL. |
400 INVALID_REQUEST — “character_image must be a valid image” | An uploaded character_image file couldn’t be decoded as an image. |
400 INVALID_REQUEST — “supply only one of driving_video or driving_video_url” | Both the uploaded file and URL form of the driving video supplied together. |
400 INVALID_REQUEST — “driving_video_url must be an https:// URL” | driving_video_url is not an https:// URL. |
400 INVALID_REQUEST — “driving_video_url must be a single URL” | driving_video_url supplied more than once. |
400 INVALID_REQUEST — “failed to fetch driving_video_url” | The driving video URL couldn’t be fetched. |
400 INVALID_REQUEST — “invalid or oversized driving_video upload” | An uploaded driving_video was empty, unreadable, or exceeded the 100 MiB video limit. |
400 INVALID_REQUEST — “driving_video_url duration must be between 5 and 15 seconds (inclusive)“ | The driving video (uploaded or fetched from driving_video_url) was outside the 5–15 second window — the message names driving_video_url even when the video was uploaded via driving_video. |
400 INVALID_REQUEST — “driving_video_url/character_image_url cannot be combined with quality” | quality (or another H3-mode field) supplied alongside a character-animation field. |
400 INVALID_REQUEST — “unsupported priority” | priority set to a value outside the server’s configured tiers. |
400 INVALID_REQUEST — “priority must be an integer” | priority isn’t parseable as an integer. |
Next step
Use Get Video with the returnedid to poll for the result.
Generate Video (from Text)
Generate Video (from Reference Video/Image)
Authorizations
Server-side SDK clients use a project API key. Remote MCP clients use an OAuth access token. Never expose a project API key in browser code.
Headers
Optional caller-supplied correlation ID, up to 128 characters. The service returns the effective value in the response header for tracing and support.
1 - 128Optional source-channel label, up to 128 characters, used to attribute traffic to an SDK, integration, product surface, or internal workflow.
1 - 128Body
Body of POST /v1/videos, multipart/form-data only (character-
animation mode below also accepts a plain application/json body —
see Generate Video (from Character Animation)). Which fields are
supplied selects the generation mode:
- Neither
first_frame_image/first_frame_image_urlnorlast_frame_image/last_frame_image_urlnor any reference or character-animation field supplied: text-to-video. - A first frame supplied, no last frame: first-frame-to-video.
- Both a first frame and a last frame supplied: first+last-frame-to-video.
- Any of
reference_video/reference_video_url/reference_image/reference_image_urlsupplied: reference-video (omni-reference) generation. driving_video/driving_video_urland/orcharacter_image/character_image_url/character_image_urlssupplied: character-animation generation, dispatched to a separate backend from the four modes above. A driving video is optional. Without one, up to 4 character images are accepted, all references of one character; with one, up to 5 are accepted, each replacing one person in the driving video. It has noqualitytier, noduration_s/resolution/aspect_ratio/seed, andpromptis optional rather than required; see below andGenerate Video (from Character Animation)for its own fields, pricing, and constraints.
Within a frame slot, supply at most one of the file and the URL
form. Supplying both first_frame_image and first_frame_image_url
(or both last_frame_image and last_frame_image_url) answers
400 INVALID_REQUEST with "supply only one of first_frame_image or
first_frame_image_url" (or the last_frame_* equivalent). A last
frame requires a first frame; supplying a last frame with no first
frame answers 400 INVALID_REQUEST with "a last frame requires a
first frame".
Reference-video mode is selected purely by whether any of the four
reference fields is present — there is no separate source/mode
parameter, and no error if you meant to but forgot: supplying none
of reference_video/reference_video_url/reference_image/
reference_image_url simply generates text-to-video instead (the
first bullet above), the same way omitting first_frame_image/
last_frame_image falls back to text-to-video rather than
erroring. reference_video and reference_video_url are declared
as arrays for forward compatibility, but at most 1 reference video
is accepted today; supplying any combination of the two fields that
adds up to more than 1 (two files, two URLs, or one of each) answers
400 INVALID_REQUEST with "at most 1 reference video(s) allowed".
reference_image and reference_image_url may be mixed freely, but
their combined count cannot exceed 4; going over answers
400 INVALID_REQUEST with "at most 4 reference images are allowed".
Reference fields cannot be combined with
first_frame_image/last_frame_image (file or URL form); doing so
answers 400 INVALID_REQUEST with
"reference_video/reference_image cannot be combined with
first_frame_image/last_frame_image". The reference video itself
(independent of duration_s, the target duration of the generated
video) must be 0.5–600 seconds long and at least 64x64 with even
width and height, the same constraints enforced on uploaded video
elsewhere in this API, and at most 100 MiB whether uploaded or
fetched from reference_video_url.
Frame and reference images (first_frame_image, last_frame_image,
reference_image) may be up to 20 MiB each when uploaded, and up to
30 MiB when fetched from the matching *_url field. The format is
detected from the image bytes — JPEG, PNG, WebP, GIF, BMP, TIFF, or
HEIC/HEIF/AVIF — and there is no minimum or maximum dimension; an
image at or above 4K (long edge >= 3840 px or short edge >= 2160 px)
is downscaled to fit 1920x1080, keeping its aspect ratio. An
oversized upload answers 400 INVALID_REQUEST with "invalid or
oversized first_frame_image upload" (naming the field), an
undecodable one with "first_frame_image must be a valid image"; a
URL that can't be decoded answers "first_frame_image_url is not a
valid image", and an unreadable reference video URL answers
"reference_video_url is not a valid video".
Character-animation mode cannot be combined with quality (or any
frame/reference field above) — doing so answers
400 INVALID_REQUEST with "driving_video_url/character_image_url
cannot be combined with quality". A driving video is optional; when
supplied, use driving_video (an uploaded file) or
driving_video_url, not both. It is checked against a 5–15 second
(inclusive) duration window (truncated to whole seconds first, so
15.9s is accepted as 15s), on top of the same at-least-64x64 and
even-dimension checks. At least one character image is required, as
character_image uploads or values supplied through
character_image_url/character_image_urls
— uploads and URL fields cannot be combined ("supply character
images as uploads or as character_image_url/character_image_urls,
not both"), character_image_url/character_image_urls remain
mutually exclusive with each other ("supply only one of
character_image_url or character_image_urls"). Up to 4 images are
accepted without a driving video, and up to 5 with one; going over
answers 400 INVALID_REQUEST with "at most 4 character images are
allowed" (or "at most 5 ..." with a driving video).
Billed at ⌈duration_s⌉ × the per-second credit rate for
resolution (currently 1 credit/second — $0.01/sec — at 480p,
768p, 1080p, and 2k, rising to 5 credits/second — $0.05/sec —
at 4k; the same rate applies to both quality tiers). quality,
aspect_ratio, and enhance do not affect price, and
image/video-conditioned generation (first-frame, first-last-frame,
or reference-video) costs the same as text-only for the same
quality, resolution, and duration. Character-animation mode is billed
at 1 credit/second instead: with a driving video it reserves the
driving video's measured duration rounded up (5–16 credits); without
one it reserves a flat 11 credits. On completion the charge is
settled against the finished video's duration, never above the
reservation. It is not tied to duration_s; see
Generate Video (from Character Animation).
Every generated video includes native audio; there is no separate audio flag or field.
Text description of the desired video. Required and non-empty for text, first-frame, first+last-frame, and reference-video modes. Optional (and merely a style/content hint, not enforced non-empty) for character-animation mode.
1"A paper airplane gliding through a sunlit office"
Generation quality tier. low generates faster, for quicker iteration; high generates slower but produces higher-fidelity output. Both tiers are billed at the same per-second rate for a given resolution — see resolution below. Required for every mode except character-animation, which has no quality tiers and rejects this field outright if supplied.
low, high "low"
First-frame image uploaded directly. Mutually exclusive with first_frame_image_url. Supplying either switches on first-frame-to-video mode. Cannot be combined with any reference_video/reference_image field. Up to 20 MiB; JPEG, PNG, WebP, GIF, BMP, TIFF, or HEIC/HEIF/AVIF, detected from the file contents. An image with a long edge of 3840 px or more, or a short edge of 2160 px or more, is downscaled to fit 1920x1080, keeping its aspect ratio.
Publicly reachable URL of the first-frame image; the service fetches and re-hosts it. Mutually exclusive with first_frame_image. Cannot be combined with any reference_video/reference_image field. The fetched image may be up to 30 MiB, in the same formats and with the same downscaling as the upload form.
Last-frame image uploaded directly. Mutually exclusive with last_frame_image_url. Valid only alongside a first frame. Cannot be combined with any reference_video/reference_image field. Up to 20 MiB; JPEG, PNG, WebP, GIF, BMP, TIFF, or HEIC/HEIF/AVIF, detected from the file contents. An image with a long edge of 3840 px or more, or a short edge of 2160 px or more, is downscaled to fit 1920x1080, keeping its aspect ratio.
Publicly reachable URL of the last-frame image; the service fetches and re-hosts it. Mutually exclusive with last_frame_image. Valid only alongside a first frame. Cannot be combined with any reference_video/reference_image field. The fetched image may be up to 30 MiB, in the same formats and with the same downscaling as the upload form.
Reference video uploaded directly, for reference-video (omni-reference) generation. Declared as an array for forward compatibility, but at most 1 is accepted today — combined with reference_video_url. Not required if at least one reference image is supplied instead. Up to 100 MiB, in any container with a readable video stream (MP4, MOV, WebM, etc.). Must be 0.5–600 seconds long and at least 64x64 with even width/height. Cannot be combined with first_frame_image/last_frame_image.
1Publicly reachable URL of the reference video; the service fetches and re-hosts it. Declared as an array for forward compatibility, but at most 1 is accepted today — combined with reference_video. Same size, duration, and resolution constraints as reference_video.
1Reference images uploaded directly, for reference-video (omni-reference) generation. Up to 4 total, combined with reference_image_url. Can be freely mixed with reference_image_url. Up to 20 MiB; JPEG, PNG, WebP, GIF, BMP, TIFF, or HEIC/HEIF/AVIF, detected from the file contents. An image with a long edge of 3840 px or more, or a short edge of 2160 px or more, is downscaled to fit 1920x1080, keeping its aspect ratio.
4Publicly reachable URLs of reference images; the service fetches and re-hosts each one. Up to 4 total, combined with reference_image. Can be freely mixed with reference_image. Each fetched image may be up to 30 MiB, in the same formats and with the same downscaling as reference_image.
4Optional driving video uploaded directly for character-animation generation. Mutually exclusive with driving_video_url. When present, the request may carry up to 5 character images, each replacing one person in the video; it is validated for 5–15 second duration, at least 64x64, and even dimensions, then staged to object storage.
Optional https:// driving-video URL for character-animation generation. Mutually exclusive with driving_video. When present, the request may carry up to 5 character images, each replacing one person in the video; it must be 5–15 seconds long, at least 64x64, with even dimensions.
Character reference images uploaded directly for character-animation generation. Up to 4 are accepted without a driving video, or 5 when a driving video is present. Mutually exclusive with URL-form character images.
5Repeatable character reference image https:// URL. Up to 4 are accepted without a driving video, or 5 when a driving video is present. Mutually exclusive with character_image_urls and uploaded character images.
5Same as character_image_url, supplied as one JSON-array field. Up to 4 are accepted without a driving video, or 5 when a driving video is present. Mutually exclusive with character_image_url and uploaded character images.
5Admission-queue priority tier, shared by H3 and character-animation modes. Defaults to 1000 (top tier) — most callers never need to set this. Must be one of the server's configured tiers (currently 1000 or 0). An unsupported integer answers 400 INVALID_REQUEST with "invalid render request: unsupported priority 500" (ending with the value sent) in H3 modes, or "unsupported priority" in character-animation mode; a non-integer answers "priority must be an integer".
Requested video duration in seconds, between 5 and 15 inclusive. Defaults to 5.
5 <= x <= 15Output resolution tier — also selects the per-second credit rate charged for this request. 480p, 768p, 1080p, and 2k are billed at $0.01/sec (1 credit/second); 4k is billed at $0.05/sec (5 credits/second).
480p, 768p, 1080p, 2k, 4k Output aspect ratio.
16:9, 9:16, 1:1, 4:3, 3:4, 21:9 Deterministic generation seed. Omit for a random seed. H3 modes only — not applicable to character-animation mode.
x >= 0Whether to burn in a Viggle watermark.
Whether to run a low-effort AI Planner pass that rewrites only prompt before generation; any supplied frame or reference video/image is left unchanged. H3 modes only — not applicable to character-animation mode. Defaults to true; pass false to use prompt exactly as given. Does not affect price.
Response
Video generation accepted and queued.
Acceptance acknowledgment for POST /v1/videos, shared by all five
generation modes. This is a queued confirmation, not the final
result — poll GET /v1/videos/{video_id} (or watch it via
GET /v1/videos) until status reaches ready, then read
video_url from that response.
Public video ID — vid_-prefixed for the four H3 modes, anim_-prefixed for character-animation mode.
1Lifecycle state at acceptance time; always queued.
queued, processing, ready, failed, cancelled Always null on acceptance.
0 <= x <= 100ISO 8601 creation timestamp, second precision.

