Skip to main content

uthana-python-client

PyPI version License

A Python client for Uthana: generate lifelike human motion from text or 2D video, create and auto-rig characters, and manage your motions.

📖 Full API documentation · 🤖 Context7 page

Install

pip install uthana

API key

You need an Uthana account and API key. Sign up for free, then get your API key from account settings once logged in. For full setup, verification, and capabilities, see the Uthana API docs.

Quick start

Context7

Context7 helps LLMs and AI code editors pull up-to-date documentation instead of relying on stale training data. Use it in prompts, e.g.: "How do I create a text-to-motion animation with the Uthana API? use context7 library /websites/uthana_api". Add Uthana as a source: context7.com/websites/uthana_api, then install Context7 in your IDE.

Async by default

All methods are async and return coroutines. Use await inside an async function and asyncio.run() to execute. Async calls are non-blocking: the event loop can run other tasks while waiting on I/O, which is ideal for concurrent requests or UI applications.

Sync variants exist for every method, suffixed with _sync. These block until the request completes and are simpler for scripts or when you don't need concurrency.

import asyncio
from uthana import Uthana

uthana_client = Uthana("your-api-key")

# Async (non-blocking)
async def async_example():
    output = await uthana_client.ttm.create("a person walking")
    data = await uthana_client.motions.download(output.character_id, output.motion_id, output_format="glb")
    return data

data = asyncio.run(async_example())

# Sync (blocking)
def sync_example():
    output = uthana_client.ttm.create_sync("a person walking")
    data = uthana_client.motions.download_sync(output.character_id, output.motion_id, output_format="glb")
    return data

data = sync_example()

Text to motion (ttm)

Docs: Text to motion

Generate 3D character animations from natural language prompts.

import asyncio
from uthana import Uthana, UthanaCharacters

uthana_client = Uthana("your-api-key")


async def text_to_motion():
    # Basic usage (default model: text-to-motion-1.0)
    output = await uthana_client.ttm.create("a person walking forward")
    print(output.character_id, output.motion_id)

    # Use a specific character (default is Tar)
    output = await uthana_client.ttm.create(
        "a person dancing",
        character_id=UthanaCharacters.ava,
    )

    # Explicit model and advanced options
    output = await uthana_client.ttm.create(
        "a person waving hello",
        model="text-to-motion-2.0",
        character_id=UthanaCharacters.manny,
        length=5.0,
        cfg_scale=2.5,
        seed=42,
    )

    # Download the motion
    data = await uthana_client.motions.download(
        output.character_id,
        output.motion_id,
        output_format="glb",
        fps=30,
    )


asyncio.run(text_to_motion())

Available models: text-to-motion-1.0 (default), text-to-motion-2.0.

Text to motion 3.0 async job (ttm.create_job)

text-to-motion-3.0 runs as an async job and returns a job dict to poll — the same pattern as video-to-motion. Available to any account on the pay-as-you-go plan.

import asyncio
from uthana import Uthana, UthanaCharacters

uthana_client = Uthana("your-api-key")


async def ttm_async_job():
    # Submit the job
    job = await uthana_client.ttm.create_job(
        "a person doing jumping jacks",
        model="text-to-motion-3.0",
        length=8,            # optional, 4–10 seconds
        rewrite_prompt=True, # optional, default True
        character_id=UthanaCharacters.tar,  # optional
    )

    # Poll until finished
    while job["status"] not in ("FINISHED", "FAILED"):
        await asyncio.sleep(5)
        job = await uthana_client.jobs.get(job["id"])
    if job["status"] == "FINISHED":
        motion_id = job["result"]["result"]["id"]
        print(motion_id)


asyncio.run(ttm_async_job())

Sync variant: uthana_client.ttm.create_job_sync(...).

Locomotion

Docs: Locomotion

Generate controllable, loopable travel motion for a character (stride count, speed, style, direction).

import asyncio
from uthana import Uthana, UthanaCharacters

uthana_client = Uthana("your-api-key")


async def locomotion_example():
    styles = await uthana_client.motions.list_locomotion_styles()
    print("Available style_id values:", styles)

    output = await uthana_client.motions.create_locomotion(
        UthanaCharacters.tar,
        strides=2,
        move_speed=1.3,
        style_id="neutral_male_a",
        travel_angle=0,
    )
    print(output.character_id, output.motion_id)


asyncio.run(locomotion_example())

Video to motion (vtm)

Docs: Video to motion

Extract motion capture from video files. Returns a job to poll until complete.

import asyncio
from uthana import Uthana, UthanaCharacters

uthana_client = Uthana("your-api-key")


async def video_to_motion():
    # Default model: video-to-motion-2.0 (single base motion)
    job = await uthana_client.vtm.create("path/to/dance.mp4", motion_name="my_dance")

    # Use video-to-motion-2.1 for DCM refinement motion IDs
    # job = await uthana_client.vtm.create(
    #     "path/to/dance.mp4", motion_name="my_dance", model="video-to-motion-2.1"
    # )

    while job["status"] not in ("FINISHED", "FAILED"):
        await asyncio.sleep(5)  # Non-blocking; other tasks can run while waiting
        job = await uthana_client.jobs.get(job["id"])
    if job["status"] == "FINISHED":
        motion_id = job["result"]["result"]["id"]
        data = await uthana_client.motions.download(
            UthanaCharacters.tar, motion_id, output_format="glb", fps=30
        )
        with open("dance.glb", "wb") as f:
            f.write(data)


asyncio.run(video_to_motion())

Available models: video-to-motion-2.0 (default), video-to-motion-2.1.

Characters

Docs: Auto-rig / add a character · Download a character

Upload, list, and download characters. Supports auto-rigging for humanoid meshes, and generation from text prompts or image files.

import asyncio
from uthana import Uthana

uthana_client = Uthana("your-api-key")


async def manage_characters():
    # Upload and auto-rig a character from a file
    output = await uthana_client.characters.create_from_file("path/to/character.glb")
    print(output.character_id)
    print(output.auto_rig_confidence)  # 0–1.0, higher is better

    # Download the rigged character
    data = await uthana_client.characters.download(output.character_id, output_format="glb")
    with open("character_rigged.glb", "wb") as f:
        f.write(data)

    # List all characters
    for c in await uthana_client.characters.list():
        print(c.get("id"), c.get("name"))

    # Text-to-character: one-shot with callback
    result = await uthana_client.characters.create_from_prompt(
        prompt="a knight in shining armor",
        name="Knight",
        on_previews_ready=lambda previews: previews[0]["key"],
    )
    print(result.character.get("id"))

    # Text-to-character: async callback (e.g. show a UI and return the chosen key)
    result = await uthana_client.characters.create_from_prompt(
        prompt="a futuristic soldier",
        on_previews_ready=lambda previews: show_picker_ui(previews),
    )

    # Text-to-character: two-step (inspect previews before confirming)
    pending = await uthana_client.characters.create_from_prompt(prompt="a futuristic soldier")
    # pending.previews is a list of {"key": ..., "url": ...} — show them to the user
    result = await uthana_client.characters.generate_from_image(pending, pending.previews[0]["key"])

    # Image-to-character: upload an image file (always one-shot)
    result = await uthana_client.characters.create_from_image("path/to/reference.png")

    # Rename or delete
    await uthana_client.characters.rename(result.character["id"], "New name")
    await uthana_client.characters.delete(result.character["id"])


asyncio.run(manage_characters())

Motions

Docs: Asset management · Retargeting

List, download, preview, delete, rename, favorite, and bake motions.

import asyncio
from uthana import Uthana, UthanaCharacters

uthana_client = Uthana("your-api-key")


async def manage_motions():
    # List all motions
    for m in await uthana_client.motions.list():
        print(m.get("id"), m.get("name"))

    # Download a motion
    data = await uthana_client.motions.download(
        UthanaCharacters.tar,
        "motion-id",
        output_format="glb",
        fps=30,
        no_mesh=False,
    )

    # Download motion preview WebM (does not charge download seconds)
    preview_bytes = await uthana_client.motions.preview(character_id, motion_id)
    with open("preview.webm", "wb") as f:
        f.write(preview_bytes)

    # Rename a motion
    await uthana_client.motions.rename("motion-id", "New name")

    # Delete a motion (soft delete)
    await uthana_client.motions.delete("motion-id")

    # Favorite / unfavorite
    await uthana_client.motions.favorite("motion-id", True)

    # Bake custom GLTF animation data as a new motion for an existing character
    result = await uthana_client.motions.bake_with_changes(
        gltf_content, "My motion", character_id=character_id
    )
    print(result.motion_id, result.character_id)


asyncio.run(manage_motions())

Organization and user (org)

Docs: Account and organization

Get user and organization info, including quota.

import asyncio
from uthana import Uthana

uthana_client = Uthana("your-api-key")


async def get_org_info():
    user = await uthana_client.org.get_user()
    print(user.get("id"), user.get("name"), user.get("email"))

    org = await uthana_client.org.get_org()
    print(org.get("name"))
    print(org.get("motion_download_secs_per_month_remaining"), "seconds remaining")


asyncio.run(get_org_info())

Jobs

Docs: Video to motion (job polling)

Poll async jobs (e.g. video to motion).

import asyncio
from uthana import Uthana

uthana_client = Uthana("your-api-key")


async def poll_job():
    job = await uthana_client.jobs.get("job-id")
    print(job["status"])   # RESERVED, READY, FINISHED, FAILED
    print(job["result"])   # Result payload when FINISHED


asyncio.run(poll_job())

Uthana characters

Docs: Auto-rig / add a character

Pre-built characters you can use without uploading your own:

Attribute Character ID
UthanaCharacters.tar cXi2eAP19XwQ
UthanaCharacters.ava cmEE2fT4aSaC
UthanaCharacters.manny c43tbGks3crJ
UthanaCharacters.quinn czCjWEMtWxt8
UthanaCharacters.y_bot cJM4ngRqXg83

Testing

Integration tests (tests/test_client.py) require UTHANA_API_KEY. Use .env.local (gitignored) or env vars:

# .env.local
UTHANA_API_KEY=your_key
UTHANA_DOMAIN=custom.uthana.com  # optional, for non-production

Releasing and PyPI

The uthana package is published automatically. The workflow is:

  1. In a PR, run make set-version VERSION=1.2.3 to bump pyproject.toml, then commit and merge.
  2. On merge to main, .github/workflows/release.yml detects that the new version is not yet on PyPI, runs tests, publishes via OIDC, and creates the v1.2.3 git tag and GitHub Release.

Re-merging the same version is a no-op — the workflow checks PyPI before doing anything.

Bump version in a branch

make set-version VERSION=1.2.3

Writes 1.2.3 to pyproject.toml. No git operations — just commit and open a PR.

Manual publish (fallback if CI fails)

make release-publish-dry-run   # build + simulate, no upload
make release-publish           # build + publish for real

Both require a PyPI API token. Set UV_PUBLISH_TOKEN from your PyPI account then run the command. For TestPyPI, run directly:

uv run python scripts/release.py publish --dry-run --index testpypi
uv run python scripts/release.py publish --index testpypi

After a successful manual publish, create the git tag manually if CI did not:

git tag -a v1.2.3 -m "Release v1.2.3"
git push origin v1.2.3

Auth: PyPI Trusted Publishing (OIDC)

The release workflow uses PyPI Trusted Publishers (OIDC) — no PYPI_API_TOKEN secret required. The Trusted Publisher must be configured on pypi.org for this repository and workflow file (release.yml).

Type hints

The package ships an empty py.typed marker (PEP 561) so type checkers treat uthana as providing inline types. Keep src/uthana/py.typed in the repo and in package data (pyproject.toml).

Custom domain

Use a different API host by passing domain=:

uthana_client = Uthana("your-api-key", domain="custom.example.com")

Support

License

Apache 2.0

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

uthana-0.4.1.tar.gz (29.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

uthana-0.4.1-py3-none-any.whl (24.4 kB view details)

Uploaded Python 3

File details

Details for the file uthana-0.4.1.tar.gz.

File metadata

  • Download URL: uthana-0.4.1.tar.gz
  • Upload date:
  • Size: 29.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for uthana-0.4.1.tar.gz
Algorithm Hash digest
SHA256 f0535bf529bba93223cc037c09b48f69c10a3b8d005668e2108376a7294c9d03
MD5 82c5d6c835281c711b64234147c8db54
BLAKE2b-256 f9a153000bb4b0db495c39e61018b8d514536fa737b6e6f3edffc0c51c1e6a3c

See more details on using hashes here.

File details

Details for the file uthana-0.4.1-py3-none-any.whl.

File metadata

  • Download URL: uthana-0.4.1-py3-none-any.whl
  • Upload date:
  • Size: 24.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for uthana-0.4.1-py3-none-any.whl
Algorithm Hash digest
SHA256 5bcf963fa09e546cc9322637ef7170daf32d3ffe2a8d5b8ed19ea78ceff0f047
MD5 384843f474aa5dc5ba881f5b56f35a44
BLAKE2b-256 fa133eac8858b2d1436ab0c624bcee3b7a799cbab4e9d6556f92c3b2feec0916

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.4.1 This release

2 files

0.4.0

2 files

0.3.1

2 files

0.3.0

2 files

0.2.0

2 files

0.1.6

2 files

0.1.5

2 files

0.1.4

2 files

0.1.3

2 files

0.1.2

2 files

0.1.1

1 file

0.1.0

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page