Docs index: /llms.txt · every page as Markdown: add .md

‹ API reference

Talking video API

Render a talking-video MP4 from your own hosted audio.

Overview

Renders an MP4 of one of your agents speaking your audio: a WAV or MP3 at a public URL, from a recording or any text-to-speech tool. Submit a job and poll for the URL, or pass wait: true to get the MP4 in the response.

essence-2 renders at up to 1080p, 1080×1920 or 1920×1080 to match the source; expression-2 renders at 416×720.

Renders bill per minute of output, rounded up: 4 credits/min for essence-2, expression-2 and expression-1, 2 for essence-1. A job charges the 120-second maximum up front and refunds the difference when it finishes, so your balance must cover that maximum at submit time. A failed render is refunded in full.

Limits: up to 120 seconds of output.

Generate a talking video

POST /v1/video/generate generateTalkingVideo

Before you start: you need an agent code. Any API secret can render a public sample, such as A23WJF0199 (wise-pup, expression-2), from audio; GET /v1/models/showcase lists them, and each one’s supported_models says which model to pass. The video bills you, never the sample’s owner. Or list your own agents with curl https://api.bithuman.ai/v1/agents -H "api-secret: $BITHUMAN_API_SECRET". Then export BITHUMAN_AGENT_CODE=A…. Creating an agent needs the Creator plan or higher (Pricing).

POST /v1/video/generate returns a job_id with status: "processing"; poll GET /v1/video/{job_id} until it completes, or register a webhook for video.completed / video.failed.

ParameterTypeRequiredDescription
modelstringyesModel: essence-1, expression-1, expression-2, or essence-2. All four render talking video today. A model outside your plan returns 403 PLAN_REQUIRED.
agent_codestringyesAn agent you own, or a public sample — supplies the avatar identity.
inputobjectyesThe render source — see below.
input.typestringyesaudio.
input.audio_urlstringyesPublic URL to a WAV or MP3 file.
waitbooleannotrue returns the finished video in this response (up to ~90 s, else a job_id); false (default) returns a job_id to poll. See Blocking mode.

Audio input

Bring your own audio: a recording, or a WAV or MP3 from any text-to-speech tool, at a public URL. To try it, use the 5-second sample, https://docs.bithuman.ai/samples/speech-16k-short.wav.

Blocking mode (wait: true)

Add "wait": true (a JSON/multipart field, or ?wait=true as a query parameter) to hold the connection until the render finishes and get the mp4 back in the same response — no polling. If the render exceeds the ~90-second cap you get the async { job_id } to poll instead.

Errors are returned at submit time, before any charge: 402 INSUFFICIENT_BALANCE if your balance cannot cover the up-front maximum; 400 for an invalid model or input; 409 MODEL_NOT_GENERATED if the agent does not have that model yet. Check the agent’s supported_models (poll status) or add the model.

curl

curl -X POST https://api.bithuman.ai/v1/video/generate \
  -H "api-secret: $BITHUMAN_API_SECRET" -H "Content-Type: application/json" \
  -d '{"model": "expression-2", "agent_code": "'"$BITHUMAN_AGENT_CODE"'", "input": {"type": "audio", "audio_url": "https://docs.bithuman.ai/samples/speech-16k-short.wav"}}'

Python

import os, requests

resp = requests.post(
    "https://api.bithuman.ai/v1/video/generate",
    headers={"api-secret": os.environ["BITHUMAN_API_SECRET"]},
    json={
        "model": "expression-2",
        "agent_code": os.environ["BITHUMAN_AGENT_CODE"],
        "input": {
            "type": "audio",
            "audio_url": "https://docs.bithuman.ai/samples/speech-16k-short.wav",
        },
    },
)
print(resp.json())

Node

const resp = await fetch("https://api.bithuman.ai/v1/video/generate", {
  method: "POST",
  headers: {
    "api-secret": process.env.BITHUMAN_API_SECRET,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    model: "expression-2",
    agent_code: process.env.BITHUMAN_AGENT_CODE,
    input: {
      type: "audio",
      audio_url: "https://docs.bithuman.ai/samples/speech-16k-short.wav",
    },
  }),
});
console.log(await resp.json());
{
  "success": true,
  "job_id": "vid_3f9a2c1b8e7d4a6f0b21",
  "status": "processing"
}

curl

curl -X POST https://api.bithuman.ai/v1/video/generate \
  -H "api-secret: $BITHUMAN_API_SECRET" -H "Content-Type: application/json" \
  -d '{"model": "essence-2", "agent_code": "'"$BITHUMAN_AGENT_CODE"'", "input": {"type": "audio", "audio_url": "https://docs.bithuman.ai/samples/speech-16k-short.wav"}, "wait": true}'

Python

import os, requests

resp = requests.post(
    "https://api.bithuman.ai/v1/video/generate",
    headers={"api-secret": os.environ["BITHUMAN_API_SECRET"]},
    json={
        "model": "essence-2",
        "agent_code": os.environ["BITHUMAN_AGENT_CODE"],
        "input": {
            "type": "audio",
            "audio_url": "https://docs.bithuman.ai/samples/speech-16k-short.wav",
        },
        "wait": True,
    },
)
print(resp.json())

Node

const resp = await fetch("https://api.bithuman.ai/v1/video/generate", {
  method: "POST",
  headers: {
    "api-secret": process.env.BITHUMAN_API_SECRET,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    model: "essence-2",
    agent_code: process.env.BITHUMAN_AGENT_CODE,
    input: {
      type: "audio",
      audio_url: "https://docs.bithuman.ai/samples/speech-16k-short.wav",
    },
    wait: true,
  }),
});
console.log(await resp.json());
{
  "success": true,
  "job_id": "vid_3f9a2c1b8e7d4a6f0b21",
  "status": "completed",
  "video_url": "https://assets.bithuman.ai/.../vid_3f9a2c1b8e7d4a6f0b21.mp4",
  "duration_seconds": 6.5,
  "credits_charged": 4
}

Get talking-video status

GET /v1/video/{job_id} getTalkingVideo

Poll a render job.

While rendering:

When complete:

FieldTypeDescription
statusstringprocessing, completed, or failed.
modelstringThe engine used.
video_urlstringPublic mp4 URL (present when completed).
duration_secondsnumberOutput duration (present when completed).
credits_chargedintegerCredits charged for this render (present when completed).
errorobjectFailure detail (present when failed); the charge is refunded.

Note Read video_url from the response; never construct it. The storage host can change, so allowlist what the API returns.

curl

curl https://api.bithuman.ai/v1/video/vid_3f9a2c1b8e7d4a6f0b21 -H "api-secret: $BITHUMAN_API_SECRET"

Python

import os, requests

resp = requests.get(
    "https://api.bithuman.ai/v1/video/vid_3f9a2c1b8e7d4a6f0b21",
    headers={"api-secret": os.environ["BITHUMAN_API_SECRET"]},
)
print(resp.json())

Node

const resp = await fetch("https://api.bithuman.ai/v1/video/vid_3f9a2c1b8e7d4a6f0b21", {
  headers: { "api-secret": process.env.BITHUMAN_API_SECRET },
});
console.log(await resp.json());
{ "success": true, "job_id": "vid_3f9a2c1b8e7d4a6f0b21", "status": "processing", "model": "essence-2" }
{
  "success": true,
  "job_id": "vid_3f9a2c1b8e7d4a6f0b21",
  "status": "completed",
  "model": "essence-2",
  "video_url": "https://assets.bithuman.ai/.../vid_3f9a2c1b8e7d4a6f0b21.mp4",
  "duration_seconds": 6.5,
  "credits_charged": 4
}

Polling pattern

import time, requests

def wait_for_video(job_id, api_secret, timeout=600):
    while timeout > 0:
        r = requests.get(
            f"https://api.bithuman.ai/v1/video/{job_id}",
            headers={"api-secret": api_secret},
        ).json()
        if r["status"] == "completed":
            return r["video_url"]
        if r["status"] == "failed":
            raise RuntimeError(r.get("error"))
        time.sleep(3)
        timeout -= 3
    raise TimeoutError("render did not finish in time")

See pricing for how credits are consumed.

This section moved: docs.bithuman.ai/api/video#audio-input