Create a talking avatar using Python with the Sume API

Create a talking avatar in Python with Requests: generate the avatar, poll its job, send it a script to speak, then read the finished video's URL.

5 min readSume
All posts

To create a talking avatar using Python, run two jobs with the Requests library: one creates the avatar from a text prompt, a profile, or a photo, and one sends it a script to speak. On Sume those are POST /v1/avatar-1.0/generate and POST /v1/avatar-1.0/talking-video. You poll each job until it finishes, then read the video's URL.

Sume facts come from Create new avatar, Generate avatar video, Jobs and results, and the Sume API reference; Requests behavior comes from its Quickstart. All were read on 2026-09-27. Sume's docs cover a TypeScript SDK, @sume-com/sdk; this script makes plain HTTPS calls with Requests instead. For a plain text-to-video job in Python, see Text-to-video API in Python.

What do I need before running the script?

  • Python with the requests package, and a Sume API key in the SUME_API_KEY environment variable. Keep the key on a server or your own machine, never in frontend JavaScript or a mobile app.
  • A handle for the avatar: 2 to 30 letters, digits, underscores, or periods, with no hyphens, and no period or underscore first, last, or twice in a row. Sume stores it in lowercase, and the sume_ prefix is reserved for Sume's own avatars.
  • An input: a text prompt, a profile, or a public HTTPS photo URL. The three are compared in How to create a reusable AI avatar; this script uses a prompt.

How do I create the avatar in Python?

post() sends JSON with json=, which Requests encodes and labels as application/json, plus an Idempotency-Key: if a call times out, resend it with the same key and the same body, and Sume returns the original job instead of starting a second paid one. wait() polls the job's status_url, sleeping for the next_poll_after_seconds Sume suggests, until terminal is true, then returns sume_status: completed, failed, or canceled. Every call sets a timeout, because Requests does not time out without one.

import os, time, requests

API = "https://api.sume.com/v1"
AUTH = {"Authorization": f"Bearer {os.environ['SUME_API_KEY']}"}

def post(path, body, key):
    return requests.post(API + path, json=body, timeout=30,
                         headers={**AUTH, "Idempotency-Key": key})

def wait(url):  # poll a job's status_url until it is terminal
    while True:
        r = requests.get(url, headers=AUTH, timeout=30)
        r.raise_for_status()
        s = r.json()["data"]
        if s["terminal"]:
            return s["sume_status"]
        time.sleep(s["next_poll_after_seconds"] or 10)

prompt = {"type": "prompt", "prompt": "A friendly presenter in a bright studio"}
r = post("/avatar-1.0/generate", {"avatar_handle": "demo_host", "input": prompt}, "demo-host-v1")
r.raise_for_status()
assert wait(r.json()["data"]["status_url"]) == "completed"

How do I make the avatar talk?

Send the handle and a script to the talking-video route, wait for that job, then read video_url from the avatar-video resource the submit response names in avatar_video_id. In current code the avatar speaks English only, so keep the script in English and within an estimated 4 to 60 seconds; how many words fit in 60 seconds explains the count.

body = {"avatar_handle": "demo_host", "script": "Hi! A Python script made this whole video."}
r = post("/avatar-1.0/talking-video", body, "demo-video-v1")
r.raise_for_status()  # 409 avatar_not_ready: the avatar or its voice isn't ready; resend later
job = r.json()["data"]
assert wait(job["status_url"]) == "completed"
res = requests.get(f"{API}/avatar-videos/{job['avatar_video_id']}", headers=AUTH, timeout=30)
res.raise_for_status()
print(res.json()["data"]["avatar_video"]["video_url"])

What does each call return?

Responses wrap their fields in data. These are the fields the script reads:

From Create new avatar, Generate avatar video, Jobs and results, and the Sume API reference, read 2026-09-27.
CallFields the script reads
POST /v1/avatar-1.0/generatestatus_url
GET a status_urlterminal, sume_status, next_poll_after_seconds
POST /v1/avatar-1.0/talking-videostatus_url, avatar_video_id
GET /v1/avatar-videos/{id}avatar_video.video_url

Why not wait with sync mode instead of polling?

Both creates default to async. sync holds the request for at most 30 seconds, and the docs say avatar-video jobs routinely outlast that, so you would poll anyway. A client-side timeout does not cancel a job: it keeps running and still bills, so store the status_url and resume polling rather than resubmitting.

In current code, a talking video requested while the avatar is not ready is refused with 409 avatar_not_ready, and the message "Avatar voice is not ready for video generation." means the avatar exists but its voice is not ready. The avatar resource, GET /v1/avatar-1.0/avatars/{id}, reports voice.status as processing, ready, or failed.

What does it cost, and what are the limits?

API pricing lists avatar creation at $0.95 per avatar and talking video per second by quality tier: $0.184/s standard, $0.245/s plus, $0.55/s max (no product image). Each is plus a 5.5% agent fee by default.

  • quality defaults to plus, aspect_ratio to 9:16, and 720p is the documented resolution.
  • One video covers an estimated 4 to 60 seconds; split longer scripts into several jobs.
  • In current code, the talking-video route refuses captions and package with 400 invalid_request, even though the docs list captions and the API reference lists package there. An avatar video preview stores both and applies them at generate-video; avatar video previews covers that flow.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume