bithuman-ai Video API

Render a finished talking-video mp4 from text or audio. Submit [`POST /v1/video/generate`](#operation/generateTalkingVideo) — async by default, or `wait: true` for a blocking render — and poll [`GET /v1/video/{job_id}`](#operation/getTalkingVideo).

Operations 2

POST /v1/video/generate Generate a talking video #
GET /v1/video/{job_id} Get talking-video status #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/bithuman-ai:bithuman-ai-video-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Discovery needs no key. Ratings and market analysis are Pro.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

bithuman-ai-video-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Bithuman Ai Video API
  version: '1.0'
  contact:
    name: bitHuman Support
    url: https://www.bithuman.ai/discord
  x-logo:
    url: https://docs.bithuman.ai/bithuman-mark.png
  description: 'Operations tagged Video across 2 of this provider''s published API definitions: openapi.yaml, bithuman-ai-openapi.yaml. Each path carries the servers of the definition it was published in.'
servers:
- url: https://api.bithuman.ai
  description: Production
security:
- apiSecret: []
tags:
- name: Video
  description: 'Render a finished talking-video mp4 from text or audio. Submit

    `POST /v1/video/generate` — async by

    default, or `wait: true` for a blocking render — and poll

    `GET /v1/video/{job_id}`.'
paths:
  /v1/video/generate:
    post:
      operationId: generateTalkingVideo
      x-codeSamples:
      - lang: shell
        label: cURL
        source: "curl -X POST https://api.bithuman.ai/v1/video/generate \\\n  -H \"api-secret: $BITHUMAN_API_SECRET\" \\\n  -H \"content-type: application/json\" \\\n  -d '{\n    \"model\": \"essence-2\",\n    \"agent_code\": \"A95SXN5716\",\n    \"input\": {\"type\": \"audio\", \"audio_url\": \"https://docs.bithuman.ai/samples/speech-16k-short.wav\"}\n  }'\n# → {\"success\": true, \"job_id\": \"vid_3f9a2c1b8e7d4a6f0b21\", \"status\": \"processing\"}\n# add \"wait\": true to block (≤ ~90 s) for the finished video_url\n"
      summary: Generate a talking video
      description: 'Render a talking-video (mp4) from **your audio**, driven by one of

        your agents.


        **Two modes:**

        - **Async (default)** — returns immediately with a `job_id`; poll

        `GET /v1/video/{job_id}` for the finished

        video. Use this for anything but the shortest clips — renders can take a

        while, so you don''t want to hold the connection.

        - **Sync** — set `wait: true` (or `?wait=true`). The call blocks until the

        render finishes (up to ~90s) and returns the `video_url` directly in this

        response. If the render is still running at that cap you get the async

        response (`status: processing`) instead — just poll the `job_id`.


        **Input:** your own audio. `audio_url` (a hosted WAV/MP3: a recording, or

        the output of any text-to-speech tool) drives the render directly.


        The output is a public CDN URL. `essence-2` renders at

        **1080p** by default (1080×1920 portrait or 1920×1080 landscape, matching

        the source orientation and capped at the source''s long side);

        `expression-2` renders at its native 416×720. Limits: max **120 seconds**

        of output.'
      tags:
      - Video
      parameters:
      - name: Idempotency-Key
        in: header
        required: false
        schema:
          type: string
        description: Optional. A unique client-generated key (e.g. a UUID). Retrying with the same key returns the original response instead of starting a second billed job. Keys are retained for 24 hours.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - model
              - input
              - agent_code
              properties:
                model:
                  type: string
                  enum:
                  - essence-1
                  - expression-1
                  - essence-2
                  - expression-2
                  description: 'Avatar engine. `essence-1` (2 credits/min) and

                    `expression-1` (4 credits/min) are the first-generation models,

                    still supported. `expression-2` (4 credits/min) and `essence-2`

                    (4 credits/min) are the second-generation models.

                    '
                  example: essence-2
                agent_code:
                  type: string
                  description: An agent you own, or a public sample from `GET /v1/models/showcase` (audio input, a model in its `supported_models`) — provides the avatar identity.
                  example: A95SXN5716
                wait:
                  type: boolean
                  default: false
                  description: 'Sync toggle. `false` (default) returns a `job_id` immediately (async — poll for the result). `true` blocks until the render finishes (up to ~90s) and returns the finished `video_url` in this response; if it exceeds the cap you get `status: processing` + a `job_id` to poll. `?wait=true` as a query parameter also works.'
                input:
                  type: object
                  required:
                  - type
                  properties:
                    type:
                      type: string
                      enum:
                      - audio
                    audio_url:
                      type: string
                      description: A public URL to a WAV or MP3 file, or a `file_id` or signed link from `POST /v1/files/upload` for a file your own account uploaded.
                      example: https://docs.bithuman.ai/samples/speech-16k-short.wav
      responses:
        '200':
          description: 'Async (or sync-timed-out): `status: processing` + a `job_id` to poll. Sync completed: `status: completed` with `video_url`, `duration_seconds`, and `credits_charged`.'
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  job_id:
                    type: string
                    example: vid_3f9a2c1b8e7d4a6f0b21
                  status:
                    type: string
                    enum:
                    - processing
                    - completed
                    example: processing
                  video_url:
                    type: string
                    description: Public mp4 URL — present only on a completed sync render (wait=true).
                  duration_seconds:
                    type: number
                    format: float
                    description: Output duration — present on a completed sync render.
                  credits_charged:
                    type: integer
                    description: Credits charged — present on a completed sync render.
        '400':
          description: Validation error — invalid model, missing input, or text/audio missing.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          description: Insufficient credits — INSUFFICIENT_BALANCE. The cost is `ceil(minutes) × rate` (essence-1 = 2, expression-1 = 4, expression-2 = 4, essence-2 = 4 credits/min).
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: '`MODEL_NOT_GENERATED` — checked **before any charge**: the requested `model` is a family this agent can''t be rendered as right now. For `expression-2` / `essence-2` that means the trained per-identity model hasn''t been generated yet (message: `agent <code>''s <family> model hasn''t been generated yet`, spelled with the public family name); `essence-1` needs the agent''s `.imx` model file (present on every completed essence-1 creation) and `expression-1` needs an expression-1 agent (or the free post-generation expression-1 model add) with an image.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                error:
                  code: MODEL_NOT_GENERATED
                  message: agent A56ZFX6217's essence-2 model hasn't been generated yet
                  httpStatus: 409
                status: error
                status_code: 409
        '429':
          $ref: '#/components/responses/RateLimited'
        '503':
          description: '`MODEL_NOT_YET_AVAILABLE` — the requested model can''t render right now (no routeable worker, or the model is temporarily paused). All four avatar models render talking video in normal operation, so this is a rare, transient condition. Returned **before any charge** — retry shortly, or render with another of the agent''s supported models.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                error:
                  code: MODEL_NOT_YET_AVAILABLE
                  message: essence-2 talking video isn't available right now — retry shortly
                  httpStatus: 503
                status: error
                status_code: 503
        '504':
          $ref: '#/components/responses/EdgeGatewayTimeout'
    servers:
    - url: https://api.bithuman.ai
      description: Production
  /v1/video/{job_id}:
    get:
      operationId: getTalkingVideo
      x-codeSamples:
      - lang: shell
        label: cURL
        source: "curl https://api.bithuman.ai/v1/video/vid_3f9a2c1b8e7d4a6f0b21 \\\n  -H \"api-secret: $BITHUMAN_API_SECRET\"\n# poll until status is \"completed\" — then video_url is the public mp4\n"
      summary: Get talking-video status
      description: 'Poll a talking-video render job. While rendering, `status` is `processing`.

        When done, `status` is `completed` and the response carries `video_url`,

        `duration_seconds`, and `credits_charged`. On failure, `status` is `failed`

        and the up-front charge is automatically refunded.'
      tags:
      - Video
      parameters:
      - name: job_id
        in: path
        required: true
        schema:
          type: string
        example: vid_3f9a2c1b8e7d4a6f0b21
      responses:
        '200':
          description: Job status
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  job_id:
                    type: string
                  status:
                    type: string
                    enum:
                    - processing
                    - completed
                    - failed
                  model:
                    type: string
                  video_url:
                    type: string
                    description: Public mp4 URL (present when status is `completed`).
                  duration_seconds:
                    type: number
                    format: float
                    description: Output duration in seconds (present when `completed`).
                  credits_charged:
                    type: integer
                    description: Credits charged for this render (present when `completed`).
                  error:
                    type: object
                    description: Failure detail (present when status is `failed`).
                    properties:
                      message:
                        type: string
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
    servers:
    - url: https://api.bithuman.ai
      description: Production
components:
  responses:
    NotFound:
      description: Resource not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: NOT_FOUND
              message: Agent not found
              httpStatus: 404
            status: error
            status_code: 404
    Unauthorized:
      description: 'Authentication failed. `UNAUTHORIZED` when the `api-secret` header is

        present but invalid; `MISSING_AUTH` when it is absent. The HTTP status

        equals `status_code` (401).

        '
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: UNAUTHORIZED
              message: Invalid API secret
              httpStatus: 401
            status: error
            status_code: 401
    RateLimited:
      description: 'Too many requests. The limiter is **per account** — every API secret on

        the account draws from one bucket — and **per cost tier**, so a burst of

        reads never uses up your generate budget. Your plan sets the number for

        each tier; the matrix, the tier each route belongs to and a retry

        recipe are on

        [Rate limits](https://docs.bithuman.ai/api/rate-limits).


        Wait `Retry-After` seconds and retry. The `X-RateLimit-*` headers let a

        client slow down *before* it gets here, but header coverage is uneven

        across routes — read them defensively and fall back to exponential

        backoff when they are absent.


        On a route that can also be called without an API secret, an anonymous

        call is metered per network address instead: same body, same

        `Retry-After`, no `X-RateLimit-*`.

        '
      headers:
        Retry-After:
          schema:
            type: integer
          description: Seconds to wait before retrying.
          example: 1
        X-RateLimit-Limit:
          schema:
            type: integer
          description: Requests per minute your plan allows on this request's cost tier.
          example: 30
        X-RateLimit-Remaining:
          schema:
            type: integer
          description: Whole requests left in the bucket right now.
          example: 0
        X-RateLimit-Reset:
          schema:
            type: integer
          description: Unix time at which the bucket is fully refilled.
          example: 1758380460
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: RATE_LIMITED
              message: Too many generate requests for this api-secret. Retry in ~1s.
              httpStatus: 429
            status: error
            status_code: 429
    EdgeGatewayTimeout:
      description: The request reached the content delivery network in front of the API but no response came back in time. **The body is an HTML page, not the JSON envelope every other status uses** — check `Content-Type` before parsing a 5xx. A `Retry-After` header is present, so an ordinary retry helper still works. Any route can answer this; the long-running routes are the likeliest.
      content:
        text/html:
          schema:
            type: string
      headers:
        Retry-After:
          schema:
            type: integer
          description: Seconds to wait before retrying.
  schemas:
    Error:
      type: object
      properties:
        error:
          type: object
          properties:
            code:
              type: string
              example: UNAUTHORIZED
            message:
              type: string
              example: Invalid API secret
            httpStatus:
              type: integer
              example: 401
            upgrade_url:
              type: string
              description: 'On `PLAN_REQUIRED`, `AGENT_LIMIT_REACHED` and `CONCURRENCY_LIMIT_REACHED` (403): where to choose a plan. Send the user there instead of retrying. See [Plan and credit refusals](https://docs.bithuman.ai/api/errors#plan-and-credit-refusals).'
              example: https://www.bithuman.ai/pricing
            topup_url:
              type: string
              description: 'On `INSUFFICIENT_BALANCE` (402): where to top up credits.'
              example: https://www.bithuman.ai/billing#credits
            details:
              type: object
              description: Code-specific fields. `CONCURRENCY_LIMIT_REACHED` sends `tier`, `cap`, `active` and `upgrade_url`.
              additionalProperties: true
        status:
          type: string
          example: error
        status_code:
          type: integer
          example: 401
  securitySchemes:
    apiSecret:
      type: apiKey
      name: api-secret
      in: header
      description: 'Your bitHuman API secret. Get one at

        [www.bithuman.ai/developer/api-keys](https://www.bithuman.ai/developer/api-keys).

        '
    bearerApiSecret:
      type: http
      scheme: bearer
      description: 'The same API secret, sent as `Authorization: Bearer <api-secret>`. Self-

        hosted runtimes send it this way; the classic `api-secret` header works

        on the same routes.

        '
x-refined-from:
- openapi.yaml
- bithuman-ai-openapi.yaml
x-tagGroups:
- name: Get started
  tags:
  - overview
  - quickstart
  - auth-guide
  - models
  - pricing
- name: Core concepts
  tags:
  - avatars-imx
  - audio-streaming
  - agent-lifecycle
- name: SDKs
  tags:
  - sdk-python
  - sdk-swift
  - cli
- name: Guides
  tags:
  - local-mode
  - building-avatars
- name: Deployment
  tags:
  - deploy-livekit
  - deploy-self-hosted
  - deploy-embed
- name: API reference
  tags:
  - Authentication
  - Agent Generation
  - Agent Management
  - Agent Context
  - Files
  - Dynamics
  - Embedding
  - Video
  - Billing
  - Account
  - Self-hosting
  - API secrets
  - Providers
  - Runtime sessions
  - Knowledge
  - Realtime
  - Status
  - Webhooks
- name: Resources
  tags:
  - community
  - changelog
  - support