> ## Documentation Index
> Fetch the complete documentation index at: https://docs.boson.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Higgs Avatar

> Talking-head avatar video from a single still image and a driving voice, with lip sync, head motion, and expression aligned to audio.

## Features

* **Instant avatar cloning** — zero-shot from a single still image.
* **Voice- or text-driven** — drive the avatar with an existing audio clip, or generate the driving voice from text in the same request with the [TTS API](/models/higgs-tts/overview) (including voice cloning).
* **Streaming delivery** — a dedicated streaming endpoint returns the video as a fragmented-MP4 byte stream, so playback can start before the clip is complete.

## Try it in the playground

The fastest way to preview the model is the [playground](https://boson.ai/workspace). Pick an avatar, paste text, and press play.

## Generate avatars with the API

<Note>
  Higgs Avatar is in public preview. API usage is currently free and rate-limited while we improve reliability, latency, and model quality.
</Note>

Set the [API key](/authentication) in your shell for the current session:

```bash theme={null}
export BOSON_API_KEY=bai-xxxx
```

Avatar video generation API is **asynchronous**:

1. `POST /v1/videos` creates a job and returns a **Video** object with `video_id`.
2. `GET /v1/videos/{video_id}` polls the Video object until `status` is `"completed"`.
3. `GET /v1/videos/{video_id}/content` downloads the rendered MP4.

Every request needs a `ref_image` (the still photo the avatar is generated from) plus exactly
one driving input: an `input` audio clip (**audio-to-video**) or an `input_tts` text request
(**text-to-video**).

## Drive with an audio clip

Pass the driving voice in `input` (an http(s) URL, data URI, or base64). The avatar lip-syncs and
moves to that audio. The driving audio sets the video length and is capped at 60 s.

<CodeGroup>
  ```bash cURL theme={null}
  # 1. Create the video — returns {"id": "video_…", "object": "video", "status": "queued", …}
  VIDEO=$(curl -s https://api.boson.ai/v1/videos \
    -H "Authorization: Bearer $BOSON_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "model": "higgs-avatar",
      "ref_image": "https://docs.boson.ai/public/avatar/sample.jpg",
      "input": "https://docs.boson.ai/public/audio/sample.mp3",
      "size": "640x640"
    }' | jq -r .id)

  # 2. Poll until completed.
  until [ "$(curl -s "https://api.boson.ai/v1/videos/$VIDEO" \
    -H "Authorization: Bearer $BOSON_API_KEY" | jq -r .status)" = "completed" ]; do sleep 2; done

  # 3. Download the rendered MP4.
  curl -s "https://api.boson.ai/v1/videos/$VIDEO/content" \
    -H "Authorization: Bearer $BOSON_API_KEY" --output out.mp4
  ```

  ```python Python theme={null}
  import os
  import time
  import requests

  BASE = "https://api.boson.ai/v1/videos"
  HEADERS = {"Authorization": f"Bearer {os.environ['BOSON_API_KEY']}"}

  # 1. Create
  video = requests.post(
      BASE,
      headers=HEADERS,
      json={
          "model": "higgs-avatar",
          "ref_image": "https://docs.boson.ai/public/avatar/sample.jpg",
          "input": "https://docs.boson.ai/public/audio/sample.mp3",
          "size": "640x640",
      },
  ).json()

  # 2. Poll the Video object until it is completed.
  while True:
      v = requests.get(f"{BASE}/{video['id']}", headers=HEADERS).json()
      if v["status"] == "completed":
          break
      if v["status"] == "failed":
          raise RuntimeError(v.get("error"))
      time.sleep(2)

  # 3. Download the rendered MP4.
  content = requests.get(f"{BASE}/{video['id']}/content", headers=HEADERS)
  with open("out.mp4", "wb") as f:
      f.write(content.content)
  ```

  ```typescript TypeScript theme={null}
  import { writeFile } from "node:fs/promises";

  const base = "https://api.boson.ai/v1/videos";
  const headers = {
    Authorization: `Bearer ${process.env.BOSON_API_KEY}`,
    "Content-Type": "application/json",
  };

  // 1. Create the video.
  const createRes = await fetch(base, {
    method: "POST",
    headers,
    body: JSON.stringify({
      model: "higgs-avatar",
      ref_image: "https://docs.boson.ai/public/avatar/sample.jpg",
      input: "https://docs.boson.ai/public/audio/sample.mp3",
      size: "640x640",
    }),
  });
  const video = await createRes.json();

  // 2. Poll the Video object until it is completed.
  while (true) {
    const pollRes = await fetch(`${base}/${video.id}`, {
      headers: { Authorization: `Bearer ${process.env.BOSON_API_KEY}` },
    });
    const status = await pollRes.json();
    if (status.status === "completed") break;
    if (status.status === "failed") throw new Error(JSON.stringify(status.error));
    await new Promise((resolve) => setTimeout(resolve, 2000));
  }

  // 3. Download the rendered MP4.
  const contentRes = await fetch(`${base}/${video.id}/content`, {
    headers: { Authorization: `Bearer ${process.env.BOSON_API_KEY}` },
  });
  await writeFile("out.mp4", Buffer.from(await contentRes.arrayBuffer()));
  ```
</CodeGroup>

## Drive with text

Skip the audio clip and synthesize the driving voice from text in the same request: put a
[speech request](/models/higgs-tts/overview) under `input_tts` (the same body as
`POST /v1/audio/speech`). Pick a speaker with `voice`, or clone one with `ref_audio`. Provide
exactly one of `input` or `input_tts`.

<CodeGroup>
  ```bash cURL theme={null}
  curl -s https://api.boson.ai/v1/videos \
    -H "Authorization: Bearer $BOSON_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "model": "higgs-avatar",
      "ref_image": "https://docs.boson.ai/public/avatar/sample.jpg",
      "input_tts": {
        "model": "higgs-tts-3",
        "input": "Hello, world.",
        "voice": "default"
      },
      "size": "640x640"
    }'
  ```

  ```python Python theme={null}
  import os
  import requests

  resp = requests.post(
      "https://api.boson.ai/v1/videos",
      headers={"Authorization": f"Bearer {os.environ['BOSON_API_KEY']}"},
      json={
          "model": "higgs-avatar",
          "ref_image": "https://docs.boson.ai/public/avatar/sample.jpg",
          "input_tts": {
              "model": "higgs-tts-3",
              "input": "Hello, world.",
              "voice": "default",
          },
          "size": "640x640",
      },
  )
  resp.raise_for_status()
  print(resp.json())
  ```

  ```typescript TypeScript theme={null}
  const res = await fetch("https://api.boson.ai/v1/videos", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.BOSON_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      model: "higgs-avatar",
      ref_image: "https://docs.boson.ai/public/avatar/sample.jpg",
      input_tts: {
        model: "higgs-tts-3",
        input: "Hello, world.",
        voice: "default",
      },
      size: "640x640",
    }),
  });

  console.log(await res.json());
  ```
</CodeGroup>

## Upload local files

Send the body as `multipart/form-data` to upload a local `ref_image` (and `input` audio) directly,
with no base64 encoding. `input_tts` is passed as a JSON-string field.

<CodeGroup>
  ```bash cURL theme={null}
  curl -s https://api.boson.ai/v1/videos \
    -H "Authorization: Bearer $BOSON_API_KEY" \
    -F model=higgs-avatar \
    -F size=480x640 \
    -F ref_image=@face.png \
    -F input=@voice.wav
  ```

  ```python Python theme={null}
  import os
  import requests

  with open("face.png", "rb") as ref_image, open("voice.wav", "rb") as input_audio:
      resp = requests.post(
          "https://api.boson.ai/v1/videos",
          headers={"Authorization": f"Bearer {os.environ['BOSON_API_KEY']}"},
          data={
              "model": "higgs-avatar",
              "size": "480x640",
          },
          files={
              "ref_image": ref_image,
              "input": input_audio,
          },
      )

  resp.raise_for_status()
  print(resp.json())
  ```

  ```typescript TypeScript theme={null}
  import { readFile } from "node:fs/promises";

  const form = new FormData();
  form.set("model", "higgs-avatar");
  form.set("size", "480x640");
  form.set("ref_image", new Blob([await readFile("face.png")]), "face.png");
  form.set("input", new Blob([await readFile("voice.wav")]), "voice.wav");

  const res = await fetch("https://api.boson.ai/v1/videos", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.BOSON_API_KEY}`,
    },
    body: form,
  });

  console.log(await res.json());
  ```
</CodeGroup>

## Retrieve and download

* **`GET /v1/videos/{video_id}`** returns the Video object — `status`
  (`queued` → `in_progress` → `completed` | `failed`), `progress`, `size`, `error`. Always JSON.
* **`GET /v1/videos/{video_id}/content`** downloads the rendered MP4 bytes once the video is
  `completed` (a `404` until then).

## Sizes

`size` is one of three presets — pick the aspect ratio that matches your reference image so
the subject is not cropped:

| `size`    | Aspect | Use for          |
| --------- | ------ | ---------------- |
| `640x640` | 1:1    | Square (default) |
| `640x480` | 4:3    | Landscape        |
| `480x640` | 3:4    | Portrait         |

## Limits

| Field              | Limit                                                                       |
| ------------------ | --------------------------------------------------------------------------- |
| `ref_image`        | PNG / JPEG / WEBP; inline (base64 / data URI) ≤ 10 MB (URLs are not capped) |
| `input` (audio)    | AAC / WAV / MP3 / FLAC / OPUS; ≤ 60 s (it sets the video length)            |
| `input_tts` (text) | ≤ 5000 characters                                                           |

A request that violates a limit returns a `4xx` with an error `code` — e.g.
`invalid_image_format`, `payload_too_large`, `audio_too_long`, `input_too_long`,
`invalid_size`, or `model_not_found`.

## Stream the video

For live playback, POST the same body to `/v1/videos/stream` (a non-standard extension). The
response body **is** the
[fragmented MP4](https://developer.mozilla.org/en-US/docs/Web/API/Media_Source_Extensions_API) byte
stream — frames arrive as they are generated, so playback can start before the full clip is ready
(append the chunks to an MSE `SourceBuffer` in the browser, or write them to a `.mp4` file). The
video id comes back in the `X-Video-Id` header, and the full MP4 is stored too — so a later
`GET /v1/videos/{video_id}/content` still works.

<CodeGroup>
  ```python Python theme={null}
  import os
  import requests

  payload = {
      "model": "higgs-avatar",
      "ref_image": "https://docs.boson.ai/public/avatar/sample.jpg",
      "input": "https://docs.boson.ai/public/audio/sample.mp3",
      "size": "640x640",
  }

  with open("out.mp4", "wb") as f, requests.post(
      "https://api.boson.ai/v1/videos/stream",
      headers={"Authorization": f"Bearer {os.environ['BOSON_API_KEY']}"},
      json=payload,
      stream=True,
      timeout=300,
  ) as r:
      r.raise_for_status()
      video_id = r.headers["X-Video-Id"]
      for chunk in r.iter_content(chunk_size=8192):
          if chunk:                       # first non-empty chunk ≈ time-to-first-frame
              f.write(chunk)
  ```

  ```typescript TypeScript theme={null}
  import { writeFile } from "node:fs/promises";

  const payload = {
    model: "higgs-avatar",
    ref_image: "https://docs.boson.ai/public/avatar/sample.jpg",
    input: "https://docs.boson.ai/public/audio/sample.mp3",
    size: "640x640",
  };

  const res = await fetch("https://api.boson.ai/v1/videos/stream", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.BOSON_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify(payload),
  });

  if (!res.ok || !res.body) throw new Error(`HTTP ${res.status}`);

  const chunks: Uint8Array[] = [];
  const reader = res.body.getReader();
  while (true) {
    const { value, done } = await reader.read();
    if (done) break;
    if (value) chunks.push(value);
  }

  await writeFile("out.mp4", Buffer.concat(chunks));
  console.log(`video id: ${res.headers.get("X-Video-Id")}`);
  ```
</CodeGroup>

## API reference

Full request body (JSON):

```jsonc theme={null}
{
  "model": "higgs-avatar",            // avatar model ID

  "ref_image": "URL | data URI | base64",  // the face to animate (PNG/JPEG/WEBP, inline ≤ 10 MB)

  // Driving signal — provide exactly one of `input` or `input_tts`.
  "input": "URL | data URI | base64",    // audio-to-video: the driving voice (AAC/WAV/MP3/FLAC/OPUS, ≤ 60 s)
  "input_tts": {                         // …or text-to-video: synthesize the voice from text
    "model": "higgs-tts-3",
    "input": "Text to speak.",
    "voice": "default"                   // preset speaker, or clone with `ref_audio`
  },

  "size": "640x640"                      // "640x640" | "640x480" | "480x640"
}
```

See the [API reference](/api-reference/videos/create-a-video) for field details and additional options.
