curl --request GET \
--url https://apis.viggle.ai/v1/videos/{video_id} \
--header 'Authorization: Bearer <token>'import requests
url = "https://apis.viggle.ai/v1/videos/{video_id}"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://apis.viggle.ai/v1/videos/{video_id}', 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/{video_id}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://apis.viggle.ai/v1/videos/{video_id}"
req, _ := http.NewRequest("GET", url, nil)
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.get("https://apis.viggle.ai/v1/videos/{video_id}")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://apis.viggle.ai/v1/videos/{video_id}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"id": "<string>",
"status": "queued",
"stage": "queued",
"progress": 50,
"video_url": "<string>",
"alpha_url": "<string>",
"created_at": "2023-11-07T05:31:56Z",
"completed_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
}
}
}Get Video
Retrieve the full state of a Render or an H3 text-to-video generation through one unified endpoint.
curl --request GET \
--url https://apis.viggle.ai/v1/videos/{video_id} \
--header 'Authorization: Bearer <token>'import requests
url = "https://apis.viggle.ai/v1/videos/{video_id}"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://apis.viggle.ai/v1/videos/{video_id}', 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/{video_id}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://apis.viggle.ai/v1/videos/{video_id}"
req, _ := http.NewRequest("GET", url, nil)
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.get("https://apis.viggle.ai/v1/videos/{video_id}")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://apis.viggle.ai/v1/videos/{video_id}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"id": "<string>",
"status": "queued",
"stage": "queued",
"progress": 50,
"video_url": "<string>",
"alpha_url": "<string>",
"created_at": "2023-11-07T05:31:56Z",
"completed_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
}
}
}GET /v1/videos/{video_id} accepts either a character+motion Render ID (render_ prefix, from Render Video (from Character and/or Motion)) or a MiniMax H3 text-to-video generation ID (vid_ prefix, from Generate Video (from Text), First Frame, or First-Last Frames) and returns its current durable state. Poll every 3–5 seconds until the status is terminal, or use Watch Render for a push-based alternative on a Render-sourced ID.
This endpoint replaces the retired GET /v1/renders/{render_id}.
GET /v1/renders/{render_id} no longer accepts GET — it now answers 405 Method Not Allowed, not 404, because other methods remain registered on that path. A router only answers 404 when a path has no method registered at all; here it still has at least one, so the retired method 405s instead. If your integration checked for a 404 to detect that the old route was gone, accept 405 too, or switch straight to this endpoint.Request parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
video_id | string | Yes | Full public video ID returned by Render Video (from Character and/or Motion) or List Videos — render_... or vid_.... Poll with the same value every 3–5 seconds until the video reaches a terminal status. |
Response parameters
Returns200 OK with a Video object.
| Field | Type | Always present | Description |
|---|---|---|---|
id | string | Yes | Requested video ID. |
status | string | Yes | queued, processing, ready, failed, or cancelled. |
stage | string or null | Yes | Populated only when this is a Render-sourced video and status is processing; null otherwise, and always null for an H3 video. The server returns null for every non-processing status by design — there is no stage: "ready". |
progress | integer or null | Yes | Progress from 0 to 100, or null. |
video_url | string or null | Yes | Downloadable output once ready; null until then. |
alpha_url | string or null | Yes | Render-only: populated only when a Render was created with background_mode=transparent and is ready. Always null for an H3 video. |
created_at | string or null | Yes | Render-sourced timestamps carry nanosecond precision; H3-sourced timestamps carry second precision. |
completed_at | string or null | Yes | See Completion timing below. |
error | object or null | Yes | Structured failure details when status is failed. |
links — unlike the Render object returned by Render Video (from Character and/or Motion), a video fetched here carries no self/events/download shortcut. Use the same video_id for further Get Video calls, and Download Render for a Render-sourced ID.
Completion timing
On a Render-sourced video,completed_at can lag status reaching ready by up to roughly 30 seconds before it backfills — it isn’t a permanent gap. Decide whether a video is finished by checking status == "ready" or whether video_url is populated, not by whether completed_at is non-null.
Render-sourced example
{
"id": "render_a1b2c3",
"status": "ready",
"stage": null,
"progress": 100,
"video_url": "https://assets.viggle.ai/render_a1b2c3.mp4",
"alpha_url": null,
"created_at": "2026-08-25T09:12:03.123456789Z",
"completed_at": "2026-08-25T09:13:47Z",
"error": null
}
H3-sourced example
{
"id": "vid_3f2a9c",
"status": "ready",
"stage": null,
"progress": 100,
"video_url": "https://storage.googleapis.com/...signed...",
"alpha_url": null,
"created_at": "2026-08-24T09:12:03Z",
"completed_at": "2026-08-24T09:13:47Z",
"error": null
}
Failed example
{
"id": "render_a1b2c3",
"status": "failed",
"stage": null,
"progress": null,
"video_url": null,
"alpha_url": null,
"created_at": "2026-08-25T09:12:03.123456789Z",
"completed_at": "2026-08-25T09:13:50Z",
"error": {
"code": "TASK_FAILED",
"message": "The render could not be completed.",
"retryable": false,
"request_id": "req_123abc",
"details": {},
"remediation": { "action": "contact_support", "retry_after_ms": null }
}
}
Examples
curl "https://apis.viggle.ai/v1/videos/render_a1b2c3" -H "Authorization: Bearer $VIGGLE_API_KEY"
import os, requests
response = requests.get("https://apis.viggle.ai/v1/videos/render_a1b2c3", headers={"Authorization": f"Bearer {os.environ['VIGGLE_API_KEY']}"})
response.raise_for_status()
video = response.json()
const response = await fetch("https://apis.viggle.ai/v1/videos/render_a1b2c3", { headers: { Authorization: `Bearer ${process.env.VIGGLE_API_KEY}` } });
const video = await response.json();
req, _ := http.NewRequest("GET", "https://apis.viggle.ai/v1/videos/render_a1b2c3", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("VIGGLE_API_KEY"))
resp, err := http.DefaultClient.Do(req)
if err != nil { panic(err) }
defer resp.Body.Close()
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 - 128Path Parameters
Public video ID owned by the caller — a Render (render_ prefix) or an H3 text-to-video generation (vid_ prefix).
1Response
Current video state.
Unified, read-only full-state resource for a finished
character+motion Render (render_ prefix) or a MiniMax H3
text-to-video generation (vid_ prefix). It carries no links
field — unlike the Render object returned by POST /v1/renders,
there is no unified self/events/download shortcut here.
stage is populated only when this is a Render-sourced video and
status is processing; it is null in every other case, including
every H3 video. alpha_url is populated only on a Render-sourced
video created with background_mode=transparent once it is
ready; it is always null on an H3 video.
Public video ID — a Render (render_ prefix) or an H3 generation (vid_ prefix).
1Overall lifecycle. ready, failed, and cancelled are terminal states.
queued, processing, ready, failed, cancelled Coarse pipeline phase, Render-only. Null except while a Render-sourced video's status is processing; always null for an H3 video.
queued, preparing, generating, finalizing, ready, failed, cancelled, analyzing, rendering, finishing Best-effort completion percentage from 0 through 100; null when the source pipeline reports no estimate.
0 <= x <= 100Short-lived URL of the completed video; null until ready or when no output was produced.
Render-only alpha/mask video URL for transparent-background output; null on every H3 video and on any Render not created with background_mode=transparent.
Creation timestamp. Render-sourced videos report nanosecond precision; H3 videos report second precision.
Terminal-state timestamp; null while the video is active. On a
Render-sourced video this can lag status reaching ready by
up to roughly 30 seconds before it is backfilled — use status
(or check video_url), not the presence of this field, to
detect completion.
Structured failure details when status is failed; otherwise null.
Show child attributes
Show child attributes

