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.
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
requestspackage, and a Sume API key in theSUME_API_KEYenvironment 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:
| Call | Fields the script reads |
|---|---|
POST /v1/avatar-1.0/generate | status_url |
GET a status_url | terminal, sume_status, next_poll_after_seconds |
POST /v1/avatar-1.0/talking-video | status_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.
qualitydefaults toplus,aspect_ratioto9:16, and720pis 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
captionsandpackagewith400 invalid_request, even though the docs listcaptionsand the API reference listspackagethere. An avatar video preview stores both and applies them atgenerate-video; avatar video previews covers that flow.
Sources
Related posts
More in Developers
- Do AI-generated video URLs expire? How Sume stores outputs
Not for Format runs and Agent Completions: Sume returns their media on media.sume.com URLs that do not expire, and anyone holding a link can open it.
- Download a generated video from the Sume API: 401s and 302s
Sume unsigned_urls need your API key and answer with a 302 redirect. Download the MP4 with curl -L or code, and fix each 401, 404, or 409.
- Exposed API key? Revoke it, then check what it did
An exposed API key works for anyone until you revoke it. Rotate to a new key, revoke the old one, then check its last use, its spend, and your jobs.
- Free webhook tester: see what a webhook sends before coding
A free webhook tester gives you a public HTTPS URL and shows each request's headers and body. Point Sume's Send test at it to see a signed payload.
Written by Sume