> ## Documentation Index
> Fetch the complete documentation index at: https://heygen-1fa696a7.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Assets

> Upload up to 100 files in one batch: request presigned S3 slots in a single call, PUT the bytes in parallel, then finalize and poll one batch id.

## Overview

Asset batches parallelize the [direct upload flow](/assets#direct-upload-for-large-files): instead of presigning, uploading, and completing files one by one, you request up to 100 presigned upload slots in a single call, PUT all the files in parallel, and finalize the whole batch with one request.

The flow has three steps:

1. [`POST /v3/assets/direct-uploads/batches`](/reference/create-asset-upload-batch) — get an `asset_id` + presigned `upload_url` per file.
2. `PUT` each file's raw bytes to its `upload_url`, sending `upload_headers` verbatim.
3. [`POST /v3/assets/complete/batches`](/reference/complete-asset-upload-batch) — finalize, then poll [`GET /v3/assets/batches/{batch_id}`](/reference/get-asset-batch).

<Note>
  A batch holds up to **100** files. No file bytes flow through the HeyGen API — uploads go straight to S3. On completion, each file is validated and ingested independently, so one bad file does not fail the rest.
</Note>

## Step 1 — Create a Batch of Upload Slots

* Endpoint: `POST /v3/assets/direct-uploads/batches`
* Purpose: Presign up to 100 direct-to-S3 upload slots and return them synchronously with a `batch_id`.

### Quick Example

```bash theme={null}
curl -X POST "https://api.heygen.com/v3/assets/direct-uploads/batches" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Webinar recordings — October",
    "files": [
      { "filename": "session-1.mp4", "content_type": "video/mp4", "size_bytes": 734003200 },
      { "filename": "session-2.mp4", "content_type": "video/mp4", "size_bytes": 812646400 }
    ]
  }'
```

```json Response theme={null}
{
  "data": {
    "batch_id": "batch_as_abc123",
    "items": [
      {
        "asset_id": "asset_9f2c...",
        "upload_url": "https://heygen-uploads.s3.amazonaws.com/...",
        "upload_headers": { "Content-Type": "video/mp4" },
        "expires_in_seconds": 3600,
        "max_bytes": 734003200,
        "status": "pending_upload"
      },
      {
        "asset_id": "asset_81aa...",
        "upload_url": "https://heygen-uploads.s3.amazonaws.com/...",
        "upload_headers": { "Content-Type": "video/mp4" },
        "expires_in_seconds": 3600,
        "max_bytes": 812646400,
        "status": "pending_upload"
      }
    ]
  }
}
```

Slots are returned **in the submitted order**, one per file.

### Request Body

| Parameter      | Type   | Required | Default | Description                                                                                                                           |
| -------------- | ------ | -------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `files`        | array  | Yes      | —       | Files to presign, each the same shape as [`POST /v3/assets/direct-uploads`](/reference/create-asset-upload). Between 1 and 100 items. |
| `title`        | string | No       | `null`  | Display name for the batch, shown in the HeyGen app.                                                                                  |
| `callback_url` | string | No       | `null`  | Reserved for parity with the other batch APIs — track completion by [polling the batch](#step-3--complete-and-poll).                  |

Each entry in `files`:

| Field             | Type    | Required | Description                                                                                                                                         |
| ----------------- | ------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| `filename`        | string  | Yes      | Original filename for reference/metadata. The stored object's extension is derived from `content_type`.                                             |
| `content_type`    | string  | Yes      | Declared MIME type (e.g. `video/mp4`, `image/png`, `audio/mpeg`, `application/pdf`). Verified against the stored bytes when the batch is completed. |
| `size_bytes`      | integer | Yes      | Exact byte size of the file. Signed into the upload URL so it cannot be exceeded.                                                                   |
| `checksum_sha256` | string  | No       | SHA-256 of the file as hex. When provided, S3 enforces it on upload.                                                                                |

### Idempotency

Pass an `Idempotency-Key` header to make retries safe — replaying the same key returns the same batch and the same slots.

## Step 2 — Upload the Files

`PUT` each file's raw bytes to its `upload_url`, sending the returned `upload_headers` verbatim. Uploads are plain S3 PUTs, so you can run them in parallel from any HTTP client:

```bash theme={null}
curl -X PUT "https://heygen-uploads.s3.amazonaws.com/..." \
  -H "Content-Type: video/mp4" \
  --data-binary @session-1.mp4
```

Each `upload_url` expires after `expires_in_seconds` — request a fresh batch if a slot lapses before you upload.

## Step 3 — Complete and Poll

* Endpoint: `POST /v3/assets/complete/batches`
* Purpose: Finalize every uploaded file in the batch. Call once, after all PUTs return `200`.

```bash theme={null}
curl -X POST "https://api.heygen.com/v3/assets/complete/batches" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "batch_id": "batch_as_abc123" }'
```

```json Response theme={null}
{
  "data": {
    "batch_id": "batch_as_abc123"
  }
}
```

The call returns `202 Accepted` — validation and ingestion run asynchronously per file. The call is idempotent: repeating it re-drives the same batch.

Poll [`GET /v3/assets/batches/{batch_id}`](/reference/get-asset-batch) for per-item progress:

```bash theme={null}
curl -X GET "https://api.heygen.com/v3/assets/batches/batch_as_abc123?limit=100" \
  -H "x-api-key: YOUR_API_KEY"
```

```json Response theme={null}
{
  "data": {
    "batch_id": "batch_as_abc123",
    "title": "Webinar recordings — October",
    "status": "processing",
    "total_items": 2,
    "counts_by_status": {
      "completed": 1,
      "processing": 1
    },
    "created_at": 1783891200,
    "items": [
      {
        "item_index": 0,
        "status": "completed",
        "video_id": "asset_9f2c...",
        "error": null
      },
      {
        "item_index": 1,
        "status": "processing",
        "video_id": null,
        "error": null
      }
    ],
    "has_more": false,
    "next_token": null
  }
}
```

<Info>
  The batch read model is shared across the batch APIs, so the per-item id field is named `video_id` — for asset batches it holds the **asset id**. Once an item is `completed`, that asset id is usable anywhere assets are accepted, e.g. as `audio_asset_id` in [video creation](/generate-avatar-video) or as an `asset_id` source in [translation](/batch-video-translations) and [lipsync](/batch-lipsyncs) batches.
</Info>

### Query Parameters

| Parameter | Type    | Required | Default | Description                                                       |
| --------- | ------- | -------- | ------- | ----------------------------------------------------------------- |
| `limit`   | integer | No       | `100`   | Items per page, `1`–`100`.                                        |
| `token`   | string  | No       | —       | Opaque pagination cursor from a previous response's `next_token`. |

The response shape matches the [video batch](/batch-videos#response-fields): aggregate `status` (`processing`, `completed`, or `failed`), `total_items`, `counts_by_status`, and a paged `items` array where each item carries `item_index`, `status` (`queued`, `processing`, `completed`, or `failed`), its id, and `error` details when failed.

## Bulk Statuses

* Endpoint: `GET /v3/assets/statuses`
* Purpose: Check up to 100 assets in one request — across batches, or for assets uploaded individually.

```bash theme={null}
curl -X GET "https://api.heygen.com/v3/assets/statuses?batch_ids=batch_as_abc123" \
  -H "x-api-key: YOUR_API_KEY"
```

| Parameter   | Type   | Required | Description                                                   |
| ----------- | ------ | -------- | ------------------------------------------------------------- |
| `asset_ids` | string | No       | Comma-separated asset ids to look up.                         |
| `batch_ids` | string | No       | Comma-separated batch ids; each expands to its member assets. |

Statuses are `queued`, `processing`, `completed`, or `failed`, plus `not_found` for unknown or unowned ids.
