Quickstart guide
Get started with the Uthana API in minutes. This guide will help you create your first AI-powered animation.
Prerequisites
- An Uthana account (sign up for free here)
- An API key (get it from your account settings once logged in)
Step 1: Verify your API key is valid
Test your API key by making a simple query to the GraphQL endpoint:
- Shell
- Python
- TypeScript
- C#
API_KEY="{{apiKey}}"
curl 'https://uthana.com/graphql' \
-u $API_KEY: \
-H "Content-Type: application/json" \
-d '{"query": "{ __typename }"}'
import requests
API_URL = 'https://uthana.com/graphql'
API_KEY = '{{apiKey}}'
response = requests.post(
API_URL,
auth=(API_KEY, ''),
json={'query': '{ __typename }'}
)
print(response.json())
const API_URL = "https://uthana.com/graphql";
const API_KEY = "{{apiKey}}";
const authString = btoa(`${API_KEY}:`);
const response = await fetch(API_URL, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Basic ${authString}`,
},
body: JSON.stringify({
query: "{ __typename }",
}),
});
const json = await response.json();
console.log(json);
using System.Net.Http;
using System.Text;
using System.Text.Json;
private const string ApiUrl = "https://uthana.com/graphql";
private readonly string _apiKey = "{{apiKey}}";
var authValue = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{_apiKey}:"));
_httpClient.DefaultRequestHeaders.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", authValue);
var request = new { query = "{ __typename }" };
var json = JsonSerializer.Serialize(request);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await _httpClient.PostAsync(ApiUrl, content);
var responseJson = await response.Content.ReadAsStringAsync();
Console.WriteLine(responseJson);
After running this, you should see a response like: {"data":{"__typename":"Query"}}. This confirms your API key is working correctly.
Step 2: Prepare your character model
(Note: You can skip this step if you're using the default character, "Tar", with the character ID cXi2eAP19XwQ.)
In order to generate motion for your character, you need to upload a 3D character model in FBX (.fbx) or glTF (.glb/.gltf) format. The easiest way to do this when you're just getting started is to use the Web UI to upload a new character.
- Go to the Web UI and sign in with your Uthana account.
- Click the button.
- Upload your character model in FBX (.fbx) or glTF (.glb/.gltf) format.
- Verify that the character model is loaded correctly by viewing an existing motion or generating a new one.
- Copy the character ID from the URL. It will look like
https://uthana.com/app/<character-id>/<motion-id>. - Save the character ID for later use.
You can also upload a character via the API. See the API documentation for more details.
Auto-rigging
If your character doesn't have a skeleton rig, Uthana will automatically attempt to create one for you. The auto-rigging process typically takes an additional 30-60 seconds to complete, but allows you to use characters that weren't originally rigged for animation. If auto-rigging fails, you'll receive a detailed error message in the GraphQL response.
Step 3: Generate a new text-to-motion animation
Create your first animation from a text prompt:
- Shell
- Python
- TypeScript
- C#
curl 'https://uthana.com/graphql' \
-u {{apiKey}}: \
-H "Content-Type: application/json" \
-d '{
"query": "mutation { create_text_to_motion(prompt: \"A person walking down the street\") { motion { id name } } }"
}'
query = '''
mutation {
create_text_to_motion(prompt: "A person walking down the street") {
motion {
id
name
}
}
}
'''
response = requests.post(
API_URL,
auth=(API_KEY, ''),
json={'query': query}
)
result = response.json()
motion = result["data"]["create_text_to_motion"]["motion"]
print(f"Created motion: {motion['id']} - {motion['name']}")
const response = await fetch(API_URL, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Basic ${authString}`,
},
body: JSON.stringify({
query: `
mutation {
create_text_to_motion(prompt: "A person walking down the street") {
motion {
id
name
}
}
}
`,
}),
});
const json = await response.json();
const motion = json.data.create_text_to_motion.motion;
console.log(`Created motion: ${motion.id} - ${motion.name}`);
var query = @"
mutation {
create_text_to_motion(prompt: ""A person walking down the street"") {
motion {
id
name
}
}
}";
var request = new { query = query };
var json = JsonSerializer.Serialize(request);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await _httpClient.PostAsync(ApiUrl, content);
response.EnsureSuccessStatusCode();
var responseJson = await response.Content.ReadAsStringAsync();
var result = JsonSerializer.Deserialize<GraphQLResponse<CreateTextToMotionData>>(responseJson);
var motion = result.Data.CreateTextToMotion.Motion;
Console.WriteLine($"Created motion: {motion.Id} - {motion.Name}");
Text-to-motion results are returned immediately:
{
"data": {
"create_text_to_motion": {
"motion": {
"id": "<new-motion-id>",
"name": "A person walking down the street"
}
}
}
}
If there are any errors, they will be returned in a GraphQL errors array.
Step 4: View and download your motion
View in Web UI
You can view and download your motion in the Uthana Web UI:
- Go to the Web UI and sign in with your Uthana account.
- From the motions list, you should see your new motion – click on it to view it.
- The URL includes the character ID and motion ID:
https://uthana.com/app/<character-id>/<motion-id>. - You can also download the motion as FBX (.fbx) or glTF (.glb/.gltf) format directly from the web UI.
Download via API
Download your motion programmatically using the API:
- Shell
- Python
- TypeScript
- C#
CHARACTER_ID="cXi2eAP19XwQ" # Default character, or use your own
MOTION_ID="<new-motion-id>"
# Download the motion as FBX (includes character mesh, filename is customizable)
curl -L "https://uthana.com/motion/file/motion_viewer/$CHARACTER_ID/$MOTION_ID/motion.fbx" \
-u {{apiKey}}: \
-o motion.fbx
# Download the motion as GLB (includes character mesh, filename is customizable)
curl -L "https://uthana.com/motion/file/motion_viewer/$CHARACTER_ID/$MOTION_ID/motion.glb" \
-u {{apiKey}}: \
-o motion.glb
# Download at 30 FPS
curl -L "https://uthana.com/motion/file/motion_viewer/$CHARACTER_ID/$MOTION_ID/motion.fbx?fps=30" \
-u {{apiKey}}: \
-o motion-30fps.fbx
# Download the motion-only GLB (animation data without character mesh, filename is customizable)
curl -L "https://uthana.com/motion/animation/motion_viewer/$CHARACTER_ID/$MOTION_ID/motion.glb" \
-u {{apiKey}}: \
-o motion-only.glb
CHARACTER_ID = "cXi2eAP19XwQ" # Default character, or use your own
MOTION_ID = motion["id"]
# Download the motion as FBX (includes character mesh, filename is customizable)
fbx_url = f'https://uthana.com/motion/file/motion_viewer/{CHARACTER_ID}/{MOTION_ID}/motion.fbx'
response = requests.get(fbx_url, auth=(API_KEY, ''))
with open('motion.fbx', 'wb') as f:
f.write(response.content)
# Download the motion as GLB (includes character mesh, filename is customizable)
glb_url = f'https://uthana.com/motion/file/motion_viewer/{CHARACTER_ID}/{MOTION_ID}/motion.glb'
response = requests.get(glb_url, auth=(API_KEY, ''))
with open('motion.glb', 'wb') as f:
f.write(response.content)
# Download at 30 FPS
fbx_url_30fps = f'https://uthana.com/motion/file/motion_viewer/{CHARACTER_ID}/{MOTION_ID}/motion.fbx?fps=30'
response = requests.get(fbx_url_30fps, auth=(API_KEY, ''))
with open('motion-30fps.fbx', 'wb') as f:
f.write(response.content)
# Download the motion-only GLB (animation data without character mesh, filename is customizable)
motion_only_url = f'https://uthana.com/motion/animation/motion_viewer/{CHARACTER_ID}/{MOTION_ID}/motion.glb'
response = requests.get(motion_only_url, auth=(API_KEY, ''))
with open('motion-only.glb', 'wb') as f:
f.write(response.content)
const CHARACTER_ID = "cXi2eAP19XwQ"; // Default character, or use your own
const MOTION_ID = motion.id;
async function downloadMotion(
characterId: string,
motionId: string,
format: "fbx" | "glb",
fps?: 24 | 30 | 60,
noMesh?: boolean,
) {
let url = `https://uthana.com/motion/file/motion_viewer/${characterId}/${motionId}/${format}/${characterId}-${motionId}.${format}`;
const params = new URLSearchParams();
if (fps) params.append("fps", fps.toString());
if (noMesh !== undefined) params.append("no_mesh", noMesh.toString());
if (params.toString()) url += `?${params.toString()}`;
const response = await fetch(url, {
headers: {
Authorization: `Basic ${authString}`,
},
});
if (!response.ok) {
throw new Error(`Failed to download motion: ${response.statusText}`);
}
const blob = await response.blob();
return blob;
}
async function downloadMotionOnlyGlb(characterId: string, motionId: string, filename: string = "motion") {
const url = `https://uthana.com/motion/animation/motion_viewer/${characterId}/${motionId}/${filename}.glb`;
const response = await fetch(url, {
headers: {
Authorization: `Basic ${authString}`,
},
});
const blob = await response.blob();
return blob;
}
// Usage
const fbxBlob = await downloadMotion(CHARACTER_ID, MOTION_ID, "fbx");
const glbBlob = await downloadMotion(CHARACTER_ID, MOTION_ID, "glb");
const motion30fps = await downloadMotion(CHARACTER_ID, MOTION_ID, "fbx", 30);
const motionOnlyBlob = await downloadMotionOnlyGlb(CHARACTER_ID, MOTION_ID);
private const string CHARACTER_ID = "cXi2eAP19XwQ"; // Default character, or use your own
public async Task<byte[]> DownloadMotionAsync(string motionId, string format = "fbx", int? fps = null, bool? noMesh = null)
{
var downloadUrl = $"https://uthana.com/motion/file/motion_viewer/{CHARACTER_ID}/{motionId}/{format}/{CHARACTER_ID}-{motionId}.{format}";
var queryParams = new List<string>();
if (fps.HasValue) queryParams.Add($"fps={fps.Value}");
if (noMesh.HasValue) queryParams.Add($"no_mesh={noMesh.Value.ToString().ToLower()}");
if (queryParams.Any()) downloadUrl += "?" + string.Join("&", queryParams);
var response = await _httpClient.GetAsync(downloadUrl);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsByteArrayAsync();
}
public async Task<byte[]> DownloadMotionOnlyGlbAsync(string motionId, string filename = "motion")
{
var downloadUrl = $"https://uthana.com/motion/animation/motion_viewer/{CHARACTER_ID}/{motionId}/{filename}.glb";
var response = await _httpClient.GetAsync(downloadUrl);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsByteArrayAsync();
}
// Usage
var fbxData = await DownloadMotionAsync(motion.Id, "fbx");
var glbData = await DownloadMotionAsync(motion.Id, "glb");
var motion30fps = await DownloadMotionAsync(motion.Id, "fbx", fps: 30);
var motionOnlyData = await DownloadMotionOnlyGlbAsync(motion.Id);
Next steps
- Explore Capabilities for detailed tutorials on each feature
- Review the API reference for complete schema documentation
- Get support for common questions and troubleshooting
- Join our Discord for community support