curl --request GET \
--url https://apis.viggle.ai/v1/videos \
--header 'Authorization: Bearer <token>'import requests
url = "https://apis.viggle.ai/v1/videos"
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', 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 => "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"
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")
.header("Authorization", "Bearer <token>")
.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::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"items": [
{
"id": "<string>",
"status": "queued",
"stage": "queued",
"progress": 50,
"created_at": "2023-11-07T05:31:56Z",
"completed_at": "2023-11-07T05:31:56Z"
}
],
"next_cursor": "<string>",
"has_more": true
}{
"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
}
}
}List Videos
List the current principal’s videos — character+motion Renders and H3 text-to-video generations — with cursor pagination.
curl --request GET \
--url https://apis.viggle.ai/v1/videos \
--header 'Authorization: Bearer <token>'import requests
url = "https://apis.viggle.ai/v1/videos"
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', 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 => "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"
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")
.header("Authorization", "Bearer <token>")
.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::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"items": [
{
"id": "<string>",
"status": "queued",
"stage": "queued",
"progress": 50,
"created_at": "2023-11-07T05:31:56Z",
"completed_at": "2023-11-07T05:31:56Z"
}
],
"next_cursor": "<string>",
"has_more": true
}{
"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
}
}
}created_at first, through opaque cursor pagination. A video is either a character+motion Render (render_ prefix, created with Render Video (from Character and/or Motion)) or a MiniMax H3 text-to-video generation (vid_ prefix, created with Generate Video (from Text), First Frame, or First-Last Frames); the two sources are merged into one list by created_at. This endpoint replaces the retired GET /v1/renders.
GET /v1/renders no longer accepts GET — it now answers 405 Method Not Allowed, not 404, because POST /v1/renders is still registered on the same path. 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 | Default | Description |
|---|---|---|---|---|
status | string | No | — | Optional exact lifecycle filter: queued, processing, ready, failed, or cancelled. Omit it to include videos in every status. |
cursor | string | No | — | Opaque continuation token from the previous response’s next_cursor. Pass it unchanged and keep the same filters while paging; maximum length is 512 characters. |
limit | integer | No | 20 | Maximum video summaries returned in one page. Accepts 1–100; use next_cursor rather than calculating an offset for later pages. |
Response parameters
Returns200 OK.
| Field | Type | Always present | Description |
|---|---|---|---|
items | array | Yes | One page of video summaries, newest first, mixing Render- and H3-sourced items. |
items[].id | string | Yes | render_... for a character+motion Render, or vid_... for an H3 generation. |
items[].status | string | Yes | queued, processing, ready, failed, or cancelled. |
items[].stage | string or null | Yes | Populated only for a Render-sourced item while status is processing. null in every other case — including a Render in ready/queued/failed/cancelled, and every H3 item regardless of status. This is intentional, not a gap in the data. |
items[].progress | integer or null | Yes | Progress from 0 to 100, or null. |
items[].created_at | string or null | Yes | Render-sourced timestamps carry nanosecond precision; H3-sourced timestamps carry second precision. Two items created within the same second can look out of order if you diff the raw strings — the list order itself is correct, only the printed precision differs between the two sources. |
items[].completed_at | string or null | Yes | See Completion timing below. |
{
"items": [
{"id":"render_a1b2c3","status":"ready","stage":null,"progress":100,"created_at":"2026-08-25T09:12:03.123456789Z","completed_at":"2026-08-25T09:13:47Z"},
{"id":"vid_3f2a9c","status":"ready","stage":null,"progress":100,"created_at":"2026-08-24T09:12:03Z","completed_at":"2026-08-24T09:13:47Z"}
],
"next_cursor": null,
"has_more": false
}
Completion timing
On a Render-sourced item,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", not by whether completed_at is non-null.
Examples
curl "https://apis.viggle.ai/v1/videos?status=ready&limit=20" -H "Authorization: Bearer $VIGGLE_API_KEY"
import os, requests
response = requests.get("https://apis.viggle.ai/v1/videos", params={"status": "ready", "limit": 20}, headers={"Authorization": f"Bearer {os.environ['VIGGLE_API_KEY']}"})
response.raise_for_status()
page = response.json()
const response = await fetch("https://apis.viggle.ai/v1/videos?status=ready&limit=20", { headers: { Authorization: `Bearer ${process.env.VIGGLE_API_KEY}` } });
const page = await response.json();
req, _ := http.NewRequest("GET", "https://apis.viggle.ai/v1/videos?status=ready&limit=20", 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()
next_cursor as cursor on the next request; stop once has_more is false.
Next step
Use Get Video on anyitems[].id for the full state, including video_url.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 - 128Query Parameters
Optional lifecycle filter. When supplied, the page contains only videos whose current status exactly matches this value.
The public lifecycle shared by every asynchronous resource. It carries
the same values as RenderStatus.
queued, processing, ready, failed, cancelled Opaque continuation token from the previous page's next_cursor. Pass it unchanged and keep all other filters consistent while paging.
1 - 512Maximum number of video summaries to return in one page, from 1 through 100. The default is 20.
1 <= x <= 100Response
One page of the principal's videos.
Video summaries in reverse chronological order for this page, merging Render-sourced and H3-sourced videos owned by the caller into one feed.
Show child attributes
Show child attributes
Opaque cursor for the next page; null when there is no later page. Pass it unchanged as the next cursor query value.
Whether another page is available after the current one.

