curl --request GET \
--url https://apis.viggle.ai/v1/motions/{motion_id}/export \
--header 'Authorization: Bearer <token>'import requests
url = "https://apis.viggle.ai/v1/motions/{motion_id}/export"
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/motions/{motion_id}/export', 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/motions/{motion_id}/export",
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/motions/{motion_id}/export"
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/motions/{motion_id}/export")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://apis.viggle.ai/v1/motions/{motion_id}/export")
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",
"download_type": "mixamo",
"glb_url": "<string>",
"thumbnail_url": "<string>",
"created_at": "<string>",
"updated_at": "<string>",
"error": {
"code": "<string>",
"message": "<string>",
"request_id": "<string>"
}
}{
"error": {
"code": "authentication_required",
"message": "<string>",
"request_id": "<string>"
}
}{
"error": {
"code": "authentication_required",
"message": "<string>",
"request_id": "<string>"
}
}{
"error": {
"code": "authentication_required",
"message": "<string>",
"request_id": "<string>"
}
}{
"error": {
"code": "authentication_required",
"message": "<string>",
"request_id": "<string>"
}
}{
"error": {
"code": "authentication_required",
"message": "<string>",
"request_id": "<string>"
}
}{
"error": {
"code": "authentication_required",
"message": "<string>",
"request_id": "<string>"
}
}{
"error": {
"code": "authentication_required",
"message": "<string>",
"request_id": "<string>"
}
}Export 3D Motion (for 3D/game engines)
Get a signed download URL for a Motion’s 3D animation extraction or generation.
curl --request GET \
--url https://apis.viggle.ai/v1/motions/{motion_id}/export \
--header 'Authorization: Bearer <token>'import requests
url = "https://apis.viggle.ai/v1/motions/{motion_id}/export"
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/motions/{motion_id}/export', 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/motions/{motion_id}/export",
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/motions/{motion_id}/export"
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/motions/{motion_id}/export")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://apis.viggle.ai/v1/motions/{motion_id}/export")
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",
"download_type": "mixamo",
"glb_url": "<string>",
"thumbnail_url": "<string>",
"created_at": "<string>",
"updated_at": "<string>",
"error": {
"code": "<string>",
"message": "<string>",
"request_id": "<string>"
}
}{
"error": {
"code": "authentication_required",
"message": "<string>",
"request_id": "<string>"
}
}{
"error": {
"code": "authentication_required",
"message": "<string>",
"request_id": "<string>"
}
}{
"error": {
"code": "authentication_required",
"message": "<string>",
"request_id": "<string>"
}
}{
"error": {
"code": "authentication_required",
"message": "<string>",
"request_id": "<string>"
}
}{
"error": {
"code": "authentication_required",
"message": "<string>",
"request_id": "<string>"
}
}{
"error": {
"code": "authentication_required",
"message": "<string>",
"request_id": "<string>"
}
}{
"error": {
"code": "authentication_required",
"message": "<string>",
"request_id": "<string>"
}
}type glb or all, or generated from text via POST /v1/motions with application/json; a render-only Motion has nothing to export. This replaces the old, now-removed GET /v1/animations/{animation_id}.
download_type is the skeleton choice itself — mixamo or metahuman — not a separate output format. There is no fbx, and both skeleton variants are always generated up front, so switching between them on the same motion_id never triggers new work or waits on a regeneration. The exported GLB works with Mixamo, MetaHuman, and other Unity-compatible animation workflows.
Request parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
motion_id | string | Yes | Complete Motion ID returned by Create, Generate, Import, or List Motions. The Motion must contain a requested 3D animation output to expose an export. |
download_type | string | No | Selects which pre-generated skeleton variant’s glb to download: mixamo or metahuman. Omit to retrieve status only. |
Response parameters
Returns200 OK.
| Field | Type | Always present | Description |
|---|---|---|---|
id | string | Yes | Requested public Motion ID. |
status | string | Yes | queued, processing, ready, or failed. |
download_type | string or null | Yes | Echoes the requested download_type — also the skeleton variant selected. null if omitted. |
glb_url | string or null | Yes | Populated only when download_type is set and that skeleton variant is ready. |
thumbnail_url | null | Yes | Always null — animations have no thumbnail. |
created_at | string or null | Yes | ISO 8601 timestamp. May be an empty string on a task that has not recorded one. |
updated_at | string or null | Yes | ISO 8601 timestamp of the most recent update. |
error | object or null | Yes | Structured failure details when status is failed. |
{
"id": "mot_456def",
"status": "ready",
"download_type": "mixamo",
"glb_url": "https://assets.viggle.ai/results/animation_mixamo.glb",
"thumbnail_url": null,
"created_at": "2026-07-21T10:00:00+00:00",
"updated_at": "2026-07-21T10:01:00+00:00",
"error": null
}
metahuman for the same Motion instead — no regeneration needed:
{
"id": "mot_456def",
"status": "ready",
"download_type": "metahuman",
"glb_url": "https://assets.viggle.ai/results/animation_metahuman.glb",
"thumbnail_url": null,
"created_at": "2026-07-21T10:00:00+00:00",
"updated_at": "2026-07-21T10:01:00+00:00",
"error": null
}
Examples
req,_:=http.NewRequest("GET","https://apis.viggle.ai/v1/motions/mot_456def/export?download_type=mixamo",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()
const response=await fetch("https://apis.viggle.ai/v1/motions/mot_456def/export?download_type=mixamo",{headers:{Authorization:`Bearer ${process.env.VIGGLE_API_KEY}`}}); const exported=await response.json();
import os, requests
response=requests.get("https://apis.viggle.ai/v1/motions/mot_456def/export",params={"download_type":"mixamo"},headers={"Authorization":f"Bearer {os.environ['VIGGLE_API_KEY']}"}); response.raise_for_status(); exported=response.json()
curl "https://apis.viggle.ai/v1/motions/mot_456def/export?download_type=mixamo" -H "Authorization: Bearer $VIGGLE_API_KEY"
download_type again rather than caching the URL.Next step
A text-generated Motion has no 2D render counterpart — only this export applies. A video-sourcedglb/all Motion also keeps its video_render capability; use Render Video (from Character and/or Motion) with motion_id for that side once it’s ready.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 Motion ID returned by motion creation or listing. It normally begins with mot_ and must identify a Motion owned by the caller.
1Query Parameters
Selects which pre-generated skeleton rig to return as a signed GLB URL: mixamo for Mixamo-compatible rigs or metahuman for MetaHuman-compatible rigs.
mixamo, metahuman Response
Current animation export state.
A motion's 3D animation extraction (video source) or generation
(text source), for download. glb_url is populated only when the
request carried a download_type, which is itself the skeleton
choice — mixamo or metahuman — both generated up front, so
switching between them on the same motion_id never triggers new
work. There is no fbx. Download URLs are short-lived and
re-signed on every read. Formerly AnimationResource, returned by
the now-removed GET /v1/animations/{animation_id}.
ID of the Motion whose 3D animation export is represented.
1Current lifecycle of the 3D animation extraction or generation. Download only when this is ready and glb_url is populated.
queued, processing, ready, failed, cancelled Echoes the requested download_type — also the skeleton variant selected.
mixamo, metahuman, null Short-lived signed URL for the selected skeleton variant's GLB when the export is ready; otherwise null.
Reserved for response-shape compatibility and always null because Motion animation exports do not generate thumbnails.
ISO 8601 timestamp with a UTC offset. May be an empty string on a task that has not recorded one.
ISO 8601 timestamp with a UTC offset for the latest 3D animation state update; null if unavailable.
Structured extraction or generation failure when status is failed; otherwise null.
Show child attributes
Show child attributes

