Character preprocessing costs 1 credit. Scene creation is free. Rendered video costs 1 credit/second.
Prerequisites
- A Viggle API key (get one here)
- A reference image (PNG or JPG)
- A driving video (MP4)
Workflow
Create a character
Upload a reference image to extract a reusable character embedding. Free — ~5s to process.Poll until ready:
import requests, time
API_KEY, BASE = "YOUR_API_KEY", "https://apis.viggle.ai"
headers = {"Authorization": f"Bearer {API_KEY}"}
resp = requests.post(f"{BASE}/api/characters/preprocess", headers=headers,
files={"image": open("character.png", "rb")},
data={"name": "My Character"})
character = resp.json()
character_id = character["id"]
const API_KEY = "YOUR_API_KEY", BASE = "https://apis.viggle.ai";
const headers = { Authorization: `Bearer ${API_KEY}` };
const form = new FormData();
form.append("name", "My Character");
form.append("image", imageFile);
const resp = await fetch(`${BASE}/api/characters/preprocess`, {
method: "POST", headers, body: form
}).then(r => r.json());
const characterId = resp.id;
curl -X POST "https://apis.viggle.ai/api/characters/preprocess" \
-H "Authorization: Bearer YOUR_API_KEY" \
-F "name=My Character" -F "[email protected]"
while True:
status = requests.get(f"{BASE}/api/characters/{character_id}", headers=headers).json()
if status["status"] == "ready": break
elif status["status"] == "failed": raise Exception(status.get("error_message"))
time.sleep(5)
const sleep = (ms) => new Promise(r => setTimeout(r, ms));
while (true) {
const status = await fetch(`${BASE}/api/characters/${characterId}`, { headers })
.then(r => r.json());
if (status.status === "ready") break;
if (status.status === "failed") throw new Error(status.error_message);
await sleep(5000);
}
# Poll until status is "ready"
curl "https://apis.viggle.ai/api/characters/CHAR_ID" \
-H "Authorization: Bearer YOUR_API_KEY"
# Repeat every 5 seconds until: "status": "ready"
Create a scene
Upload a driving video to extract motion data. Free — takes ~8x the video length (motion extraction + background inpainting + upload).Poll until ready:
No driving video? Import a template from viggle.ai instead.
resp = requests.post(f"{BASE}/api/scenes/preprocess", headers=headers,
files={"video": open("dance.mp4", "rb")},
data={"name": "Dance Sequence"})
scene = resp.json()
scene_id = scene["id"]
const sceneForm = new FormData();
sceneForm.append("name", "Dance Sequence");
sceneForm.append("video", videoFile);
const sceneResp = await fetch(`${BASE}/api/scenes/preprocess`, {
method: "POST", headers, body: sceneForm
}).then(r => r.json());
const sceneId = sceneResp.id;
curl -X POST "https://apis.viggle.ai/api/scenes/preprocess" \
-H "Authorization: Bearer YOUR_API_KEY" \
-F "name=Dance Sequence" -F "[email protected]"
while True:
status = requests.get(f"{BASE}/api/scenes/{scene_id}", headers=headers).json()
if status["status"] == "ready": break
elif status["status"] == "failed": raise Exception(status.get("error_message"))
time.sleep(5)
while (true) {
const status = await fetch(`${BASE}/api/scenes/${sceneId}`, { headers })
.then(r => r.json());
if (status.status === "ready") break;
if (status.status === "failed") throw new Error(status.error_message);
await sleep(5000);
}
# Poll until status is "ready"
curl "https://apis.viggle.ai/api/scenes/SCENE_ID" \
-H "Authorization: Bearer YOUR_API_KEY"
# Repeat every 5 seconds until: "status": "ready"
Render
Combine character + scene to generate video.
job = requests.post(f"{BASE}/api/render", headers=headers, data={
"character_id": character_id,
"scene_id": scene_id,
}).json()
print(f"Job: {job['job_id']}")
const renderForm = new URLSearchParams();
renderForm.append("character_id", characterId);
renderForm.append("scene_id", sceneId);
const job = await fetch(`${BASE}/api/render`, {
method: "POST", headers, body: renderForm
}).then(r => r.json());
curl -X POST "https://apis.viggle.ai/api/render" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d "character_id=CHAR_ID" -d "scene_id=SCENE_ID"
Poll and download
Poll every 3-5 seconds until complete, then use
cdn_url to download the final video.while True:
status = requests.get(f"{BASE}/api/render/{job['job_id']}").json()
if status["status"] == "complete":
cdn_url = status["cdn_url"]
break
elif status["status"] == "failed": raise Exception(status.get("error_message"))
time.sleep(3)
video = requests.get(cdn_url)
open("output.mp4", "wb").write(video.content)
const sleep = (ms) => new Promise(r => setTimeout(r, ms));
let cdnUrl;
while (true) {
const s = await fetch(`${BASE}/api/render/${job.job_id}`).then(r => r.json());
if (s.status === "complete") { cdnUrl = s.cdn_url; break; }
if (s.status === "failed") throw new Error(s.error_message);
await sleep(3000);
}
const vid = await fetch(cdnUrl);
require("fs").writeFileSync("output.mp4", Buffer.from(await vid.arrayBuffer()));
# Poll until complete
curl "https://apis.viggle.ai/api/render/JOB_ID"
# Download using the cdn_url from the response
curl -o output.mp4 "CDN_URL_FROM_RESPONSE"
Reusing assets
Render same character with different scenes
Render same character with different scenes
job2 = requests.post(f"{BASE}/api/render", headers=headers, data={
"character_id": character_id,
"scene_id": another_scene_id,
}).json()
const form2 = new URLSearchParams();
form2.append("character_id", characterId);
form2.append("scene_id", anotherSceneId);
const job2 = await fetch(`${BASE}/api/render`, {
method: "POST", headers, body: form2
}).then(r => r.json());
curl -X POST "https://apis.viggle.ai/api/render" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d "character_id=CHAR_ID" -d "scene_id=ANOTHER_SCENE_ID"
Render different character with same scene
Render different character with same scene
job3 = requests.post(f"{BASE}/api/render", headers=headers, data={
"character_id": another_character_id,
"scene_id": scene_id,
}).json()
const form3 = new URLSearchParams();
form3.append("character_id", anotherCharacterId);
form3.append("scene_id", sceneId);
const job3 = await fetch(`${BASE}/api/render`, {
method: "POST", headers, body: form3
}).then(r => r.json());
curl -X POST "https://apis.viggle.ai/api/render" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d "character_id=ANOTHER_CHAR_ID" -d "scene_id=SCENE_ID"
List your assets
List your assets
characters = requests.get(f"{BASE}/api/characters", headers=headers).json()
scenes = requests.get(f"{BASE}/api/scenes", headers=headers).json()
const characters = await fetch(`${BASE}/api/characters`, { headers })
.then(r => r.json());
const scenes = await fetch(`${BASE}/api/scenes`, { headers })
.then(r => r.json());
curl "https://apis.viggle.ai/api/characters" \
-H "Authorization: Bearer YOUR_API_KEY"
curl "https://apis.viggle.ai/api/scenes" \
-H "Authorization: Bearer YOUR_API_KEY"
What’s next?
On-Demand Rendering
Render without preprocessing — one API call
Import Templates
Use pre-made templates from viggle.ai
Render Options
Background mode and more

