Skip to main content
The Assets API lets you upload files to HeyGen and receive an asset_id you can reference in other endpoints — including Video Agent, Avatar creation, Video Translation, and Lipsync. There are two ways to upload:
  • POST /v3/assets — a single multipart/form-data request. Simplest option, capped at 32 MB.
  • Direct upload — a presigned-URL flow for files larger than 32 MB, up to 200 MB per video, image, or PDF file and 1.5 GB per audio file. The file bytes go straight to storage and never pass through the API.

Upload via POST /v3/assets

Full schema: POST /v3/assets. Upload a file using multipart/form-data — the MIME type is auto-detected from file bytes.

Constraints

Example request

Response

Upload large files (direct upload)

For files larger than 32 MB, use the direct upload flow. Direct upload accepts files up to 200 MB (209,715,200 bytes) for video, image, and PDF, and up to 1.5 GB for audio. The limit for your file type is also returned as max_bytes. It is a three-step process — all three steps are required; the asset_id is not usable until you call the complete endpoint:
1

Initialize the upload

Call POST /v3/assets/direct-uploads with the file’s name, MIME type, and exact byte size. The response contains an asset_id, a presigned upload_url, and upload_headers.
2

PUT the file bytes

Send the raw file bytes to upload_url with an HTTP PUT, including every header from upload_headers verbatim. The URL expires after expires_in_seconds, and the byte size is signed into it — the upload fails if the file doesn’t match the declared size_bytes.
3

Complete the upload

Call POST /v3/assets/{asset_id}/complete to finalize the asset. This step is idempotent — repeated calls return the same finalized asset. The asset_id is now usable anywhere the API accepts assets. The complete response returns status: "processing". You do not need to wait for it to change; pass the asset_id to the next request (for example POST /v3/avatars) right away.

Example

Initialize response fields

Calling complete before the PUT has finished returns a 409 conflict (“Uploaded object not found yet”). Retry after the PUT returns 200. You can optionally pass checksum_sha256 (hex) at both the initialize and complete steps to have the stored bytes verified end to end.

Use assets in Video Agent

Once uploaded, reference the asset_id in the files array when creating a video:

Three ways to provide files

Video Agent and other endpoints accept files in three formats. Use whichever is most convenient for your workflow:

Asset ID

Upload once, reference by ID. Best for files you reuse across multiple videos.

HTTPS URL

Point to a publicly accessible HTTPS URL. No upload step needed, HeyGen fetches the file directly. Limits: 16 MB for images, 32 MB for video, audio, and PDF.

Base64

Inline the file content as a base64-encoded string. Useful for small files or when you want a self-contained request.

Format comparison

Size limits by input

URL inputs are capped at 16 MB for images and 32 MB for video, audio, and PDF. Pointing at a larger self-hosted file fails with Maximum size for URL inputs of type 'video/mp4' is 32 MB. For larger files, use direct upload and pass the resulting asset_id. Base64 encoding additionally inflates payload size by ~33%, so prefer the other two formats for anything beyond a few MB.
Signed URLs (for example S3 or Google Cloud Storage presigned URLs) work like any other url input and have the same size limits. Signing a URL does not raise the limit. The URL must use HTTPS, be downloadable without extra headers, return the file directly (redirects are not followed) with a Content-Type header, and still be valid when HeyGen fetches it. For files over the URL limit, or when you cannot guarantee the link stays valid, use direct upload and pass the asset_id.

Where assets can be used

The asset_id format is accepted anywhere the API takes file inputs:

Asset lifetime and deletion

An asset_id does not expire. It stays usable in any request until you delete it, so upload a file once and reuse the same asset_id on every retry, for example when you retry POST /v3/avatars or resubmit a consent video with POST /v3/avatars/{group_id}/consent after a rejected review. You do not need to upload the file again. Only the presigned upload_url from direct upload is time-limited (expires_in_seconds, 24 hours). The asset_id it returns becomes usable after the complete step and then follows the same rule. Delete an asset with DELETE /v3/assets/{asset_id}:
curl
A deleted asset_id returns asset_not_found in later requests. Assets are separate from the avatars, videos, and translations you create from them. Deleting an avatar group with DELETE /v3/avatars/{group_id} removes the avatar and all its looks, but not the files you uploaded to create it, and deleting an asset does not affect avatars or videos already made from it. To remove an uploaded training footage or consent video file, call DELETE /v3/assets/{asset_id} for each asset_id. Files you pass to other endpoints as a url or base64 are also saved as assets in your workspace, but those calls do not return the new asset_id. If you will need to delete a source file later, upload it here first and pass its asset_id.

Example: Upload then generate

A complete workflow — upload a PDF, then use it to generate a video: