Skip to main content
HeyGen uses conventional HTTP response codes to indicate the success or failure of an API request. Codes in the 2xx range indicate success. Codes in the 4xx range indicate an error with the information provided (e.g., a missing parameter, insufficient credits, or a resource not found). Codes in the 5xx range indicate an error on HeyGen’s servers. Every error response includes a machine-readable code, a human-readable message, and a doc_url linking to the relevant section below. Some errors that relate to a specific request field also include a param attribute.

Error response format

HTTP status code summary


Error codes

unauthorized

HTTP status: 401 The API key provided is invalid, expired, or missing. Verify that you are sending your API key in the X-Api-Key header and that the key is active in your HeyGen account settings.

forbidden

HTTP status: 403 The request is authenticated but cannot use the current workspace or action. This can happen when the workspace session expires or the caller lacks access. Reauthenticate if the session expired; otherwise use a workspace or role with the required access.

insufficient_api_key_scope

HTTP status: 403 The API key is valid but its permission scopes don’t cover this endpoint. Restricted API keys are only authorized for the resources and access levels (read/write) explicitly granted when the key was created. Check the key’s configured scopes in your HeyGen account settings, or use a key with broader access. The message names the scopes the endpoint requires, so it can be compared against the key’s grants directly.

resource_access_denied

HTTP status: 403 The authenticated user does not have access to the specific resource referenced in the request. The resource may belong to a different user or organization. Verify that the resource ID is correct and belongs to your account.

encryption_setup_pending

HTTP status: 409 The workspace was created with a customer-managed encryption key (BYOK) and that key has not yet been registered, validated and activated. Until it is, the workspace refuses every media write — uploads, generation, renders — so that nothing it ever holds is outside the customer’s key. A workspace administrator completes the setup from Settings → Workspace Controls → Encryption, or HeyGen completes it on the customer’s behalf.

encryption_disabled

HTTP status: 403 The workspace’s customer-managed encryption cannot be used for this request: it has been stood down, or the workspace is marked for customer-managed encryption and its key record cannot be resolved. Either way the media write is refused rather than falling back to a HeyGen-managed key. Standing it down is deliberate and one-way, and it leaves the workspace unable to store media: its existing objects are under a key it would no longer use, and moving them out is a separate migration that does not exist yet. So this is not a way to return a workspace to ordinary shared storage — a customer who wants customer-managed encryption again is provisioned a new workspace, and support should treat a workspace in this state as one that needs a decision rather than a retry. This is also not the error for a key the customer has disabled in their own AWS account. That needs no change on this side: the operation fails because AWS refuses it, and uploads and playback resume on their own once the key is usable again.

encryption_cross_tree_transfer

HTTP status: 409 Content cannot be moved or copied between a workspace tree that uses customer-managed encryption and any other tree, in either direction. Moves re-point database rows at the same stored object rather than copying bytes, so a cross-tree move would leave media outside the customer’s key, or put another customer’s media under it. Moves within one tree are unaffected.

invalid_encryption_key

HTTP status: 400 The supplied KMS key reference was rejected. It must be a full key ARN (not an alias or bare key id, which would resolve inside HeyGen’s account rather than the customer’s), for a symmetric ENCRYPT_DECRYPT key in us-east-2.

encryption_setup_failed

HTTP status: 409 Provisioning the workspace’s encrypted bucket, or the validation round trip against it, did not complete. The key itself may be fine: this is the bucket refusing to be configured, an AWS error during setup, or a check in the validation report coming back red. The response carries the per-step or per-check report; act on the first failed entry. Nothing was activated. A provisioning failure during key registration means the key was not recorded; during activation the key stays recorded and the tree stays awaiting_key.

encryption_temporarily_unavailable

HTTP status: 503 The service could not tell whether this workspace’s media must be stored under a customer-managed key, so the media was not stored. Nothing is wrong with the workspace or its key: the record that answers the question could not be read. Retry with backoff.

ai_vendor_access_restricted

HTTP status: 403 The workspace has restricted which AI vendor companies may be used. The action or model you requested relies on a vendor that is not allowed under the workspace’s AI vendor access policy. Ask a workspace administrator to update the policy if this vendor should be permitted.

brand_kit_locked

HTTP status: 403 The workspace’s brand policy is locked (an enterprise governance setting): only the workspace default brand kit may be applied, and the request named a different brand_kit_id. Pass the default brand kit id (included in the error message) or omit brand_kit_id entirely — the workspace default is applied automatically. Ask a workspace administrator to pin a different default or unlock the brand policy if another kit is needed.

voice_not_usable

HTTP status: 403 The voice referenced in the request cannot currently be used to generate this video. The voice is in a state that blocks generation and will not resolve on retry. Select a different voice.

phone_verification_required

HTTP status: 403 The account associated with this request must complete phone (SMS) verification before this quota-consuming request can proceed. This is an anti-abuse safeguard that can apply to any generation feature (video, voice, avatar, and related operations). Complete phone verification for the account in the HeyGen app or dashboard, then retry the request.

rate_limit_exceeded

HTTP status: 429 You are sending requests too frequently. Back off and retry with exponential backoff. Check the Retry-After response header for the number of seconds to wait before retrying. See Usage Limits for concurrency and per-endpoint limits; Enterprise workspaces can raise effective concurrency with burst.

quota_exceeded

HTTP status: 429 You have exceeded a usage quota (e.g., the free-tier limit for video agent or AI clip requests). Upgrade your plan or wait for your quota to reset. Check your current usage in the HeyGen dashboard.

insufficient_credit

HTTP status: 402 Your account does not have enough credits to complete this request. The error message includes how many credits you have and how many are required. Purchase additional credits or reduce the scope of your request (e.g., shorter video duration, fewer scenes).

trial_limit_exceeded

HTTP status: 402 You have reached the video generation limit for trial accounts. Upgrade to a paid plan to continue creating videos.

subscription_required

HTTP status: 402 You have used all the credits included in your pay-as-you-go trial, and further top-ups are not available on your account. Choose a plan to continue making API requests — see pricing. This is distinct from insufficient_credit: buying more credits will not resolve it, because purchases are no longer available. Subscribing to a plan is the only action that restores API access.

plan_upgrade_required

HTTP status: 402 The requested feature or resource requires a higher subscription tier than your current plan. This can occur when:
  • Using a premium avatar that is not available on your plan.
  • Accessing an integration that requires a higher tier.
  • Requesting a resolution or feature gated by plan level.
Upgrade your plan in the HeyGen dashboard to access this feature.

video_not_found

HTTP status: 404 No video, draft, or video translation was found matching the provided ID. Verify that:
  • The video_id is correct and was not mistyped.
  • The video has not been deleted.
  • The video belongs to your account.

avatar_not_found

HTTP status: 404 No avatar was found matching the provided ID. This applies to all avatar types — standard avatars, photo avatars (photars), instant avatars, and avatar kits. Verify that:
  • The avatar_id is correct.
  • The avatar has finished training (if recently created).
  • The avatar belongs to your account or is a public avatar.

voice_not_found

HTTP status: 404 No voice was found matching the provided ID. Verify that the voice_id is correct and that the voice is available in your account. If using a cloned voice, ensure it has finished processing.

template_not_found

HTTP status: 404 No template was found matching the provided ID. Verify that the template_id is correct and that the template is shared with your account or is publicly available.

asset_not_found

HTTP status: 404 No asset was found matching the provided ID. Assets may have been deleted or may not have finished uploading. Verify that the asset_id was returned from a successful POST /v3/assets call and that the asset has not been removed.

webhook_not_found

HTTP status: 404 No webhook endpoint was found matching the provided ID. Verify that the endpoint_id is correct and that the webhook has not been deleted. List your existing webhooks with GET /v3/webhooks/endpoints to find valid endpoint IDs.

batch_not_found

HTTP status: 404 No batch was found matching the provided ID. Verify that the batch_id is correct and that the batch belongs to your account.

resource_not_found

HTTP status: 404 The requested resource was not found. This is a generic not-found error for resources that do not have a more specific error code (e.g., streaming sessions, audio records). Verify that the resource ID is correct and belongs to your account.

voice_not_cloneable

HTTP status: 400 The selected voice is provider-owned and cannot be cloned, duplicated, improved, or changed to a different provider. Use the voice with one of its listed allowed_engines, or choose a cloneable voice for mutation operations.

invalid_parameter

HTTP status: 400 One or more request parameters are invalid, missing, or in the wrong format. The message field describes which parameter failed validation and why. The param field, when present, identifies the specific field. Common causes:
  • A required field is missing from the request body.
  • A field value is the wrong type (e.g., string instead of number).
  • A field value is outside the allowed range or not in the set of accepted values.
  • The request body is not valid JSON or is not a JSON object.

graph_invalid

HTTP status: 422 The workflow product graph is structurally well-formed JSON but semantically invalid, so it cannot be saved as submitted or compiled into a version. Distinct from invalid_parameter (400), which means the request body itself is malformed; a graph_invalid response means the graph parsed but violates the workflow contract. The errors[] array lists every problem found in one pass — validation never stops at the first issue. Each entry carries a stable code (for example unknown_node_type, node_type_unavailable, invalid_config, dangling_dependency, cycle_detected), a human-readable message, and where applicable the offending node_id, port, and path (a field path such as config.prompt or outputs.result). Fix every listed issue and resubmit; use GET /v3/workflows/node-types to discover the node types and config schemas available for authoring.

conflict

HTTP status: 409 The request conflicts with existing state. For example, attempting to create a webhook endpoint with a URL that is already registered for your account, or creating a brand glossary whose name another glossary in your workspace already uses (names are compared without regard to case). On brand glossary writes this also covers a term that appears more than once within any one of the terms, do_not_translate_terms, or forced_translations lists in the request body (terms are compared without regard to case). Use a different value or delete the existing resource first. On the workflow authoring endpoints this also covers Idempotency-Key reuse: sending a key that was already used with a different request body or target returns this code instead of replaying the original response. Send a new key for a new request.

brand_kit_not_ready

HTTP status: 409 The brand kit is still being assembled, or its import failed, so its role assignments cannot be edited yet. A brand kit imported from a website gathers its colors, logos and fonts in the background; until status is completed the assets a role would name may not exist. Poll GET /v3/brand-kits/{brand_kit_id} until status is completed, then retry. Renaming a kit is allowed at any time and never returns this.

voice_not_ready

HTTP status: 409 The professional voice clone (PVC) is still training and cannot serve inference yet. Training normally completes within minutes; retry the stream request after a short delay.

voice_training_failed

HTTP status: 409 Training for this professional voice clone (PVC) ended in a terminal failure, so the voice cannot serve inference. Retrying the stream request will not succeed; contact support to retrain or replace the voice.

stale_workflow_draft

HTTP status: 409 Returned by PATCH /v3/workflows/{workflow_id} when the workflow draft moved before your write landed: the expected_revision you sent no longer matches. Nothing was written. Re-read the workflow to pick up the current draft_revision and reapply your change; retrying the same request unchanged will keep failing. Publishing (POST .../versions) no longer surfaces this code — its graph rides the request body, so a concurrent draft edit cannot invalidate it.

workflow_archived

HTTP status: 409 The workflow definition is archived, so it cannot be published or modified. This is terminal for that definition — retrying will not help. Create a new workflow instead.

template_update_in_progress

HTTP status: 409 Another write to this template is still being applied. Writes to one template, from the API or from the HeyGen editor, are serialized so two of them cannot interleave. The template row is only written by the writer that holds the lock; the editor keeps its lock alive while it stores element metadata, and a save that lost the lock is refused before it writes the row. Wait a moment and retry; if you use expected_edit_version, re-read the template first.

workflow_run_admission_refused

HTTP status: 409 The workflow run was created, but its coordinator workflow was authoritatively refused by workflow scheduling — its inputs failed scheduling validation. This is terminal for the run: retrying the same request will not help. Check the run’s status for details, or contact HeyGen support with the run id.

resource_not_ready

HTTP status: 409 The requested resource exists but is not yet in a ready state. This can occur when a video translation is still processing, an instant avatar has not finished training, or an existing reference image does not yet have its stored object. Poll the resource or asset status and retry once it reaches a ready state. Also returned when the uploaded object is not yet present in storage (e.g. POST /v3/assets/{asset_id}/complete called before the upload PUT landed); safe to retry. Missing or deleted reference images return asset_not_found instead.

image_processing_failed

HTTP status: 422 The referenced image exists but its processing has permanently ended without producing a usable result (for example, image processing failed, or it completed without generating a thumbnail), so the requested operation cannot be performed. Unlike resource_not_ready, this is terminal — retrying or polling will not change the outcome. Upload a new image and retry with it.

artifact_unavailable

HTTP status: 409 The requested artifact exists but permanently failed to resolve to a durable storage location, so no download URL can be issued. Unlike resource_not_ready, this is terminal — retrying or polling will not change the outcome. Inspect the artifact’s failure field for the reason.

stale_edit_version

HTTP status: 409 The Video Agent draft changed after the caller read its scenes, so the supplied edit_version no longer identifies the current scene graph. No edit was submitted. Fetch GET /v3/videos/{video_id}/scenes again, choose scene IDs from that response, and retry with its new edit_version. On PUT /v3/templates/{template_id}/variables, the expected_edit_version you sent does not match the template’s current edit_version, so something changed the template after you last read it. Every write changes it, whether from another API client or from a save in the HeyGen editor, and the write itself is refused if the template moved between your read and your write. Re-read the template with GET /v3/templates/{template_id}, rebuild your variable set from the current state, and send it again with the new edit_version.

request_in_progress

HTTP status: 409 A prior request with this Idempotency-Key is still in progress. Wait for the original request to complete and retry. Once the original request finishes, subsequent retries with the same key within 24 hours replay the original response.

content_policy_violation

HTTP status: 400 The request was rejected for violating HeyGen’s content policy. This can occur when an instant avatar does not pass the moderation review, when submitted content contains inappropriate content, or when an uploaded image/video asset that was flagged by moderation is used in a request. Create a new resource (or upload a new asset) that complies with our usage policy.

avatar_not_usable

HTTP status: 400 The requested avatar cannot be used because it did not pass content moderation or its creation failed. Select a different avatar; the error message identifies which condition occurred.

unlimited_mode_disabled

HTTP status: 400 The avatar does not support unlimited mode. Use a different avatar, or use Avatar IV or Avatar V.

scenes_unavailable

HTTP status: 400 This video’s scenes cannot be returned. Not every video has a scene representation, and there is no fix on the caller’s side — re-create the video through the API if you need its scenes.

template_source_unsupported

HTTP status: 400 The video passed to POST /v3/templates cannot become a template. Either it has no editable draft in the current editor format (videos rendered before the current editor, or whose draft was never saved), or it was produced by a flow that does not keep an editable draft, such as URL to Video. Create the template from a video generated with POST /v3/videos or saved in the HeyGen editor. For an older editor video, open it in the editor and save it again, then retry.

variable_match_not_found

HTTP status: 400 A text variable in PUT /v3/templates/{template_id}/variables was declared with match, but that exact text does not occur in the template’s script or on-screen text (or not in the element_ids you limited it to). Check the current text in GET /v3/templates/{template_id} under elements[].current.text; matching is literal and case-sensitive. If the template already contains {{name}} placeholders for this variable, declare it without match.

variable_match_spans_styles

HTTP status: 400 The match text for a text variable occurs in a rich-text element only across differently styled runs (for example, half bold and half regular), so a single placeholder cannot replace it without losing styling. Shorten match to text inside one styled run, or make the styling uniform in the HeyGen editor and retry. HTTP status: 400 The avatar group used in the request requires consent before it can be used to generate a video (its instant-avatar consent was skipped, rejected, or never completed). Complete the consent flow for the avatar group (e.g. POST /v3/avatars/{group_id}/consent), then retry the request.

avatar_expired

HTTP status: 400 The digital twin used in the request has expired because the workspace exceeded its avatar slot limit — typically after an avatar-slot add-on lapsed or the plan was downgraded. Existing videos are unaffected, but the expired digital twin cannot be used to generate new videos. Renew or purchase avatar slots, or delete other digital twins to bring the workspace back within its limit; the digital twin becomes usable again immediately.

voice_expired

HTTP status: 400 The professional voice clone used in the request has expired because the workspace exceeded its professional voice clone slot limit — typically after a voice clone add-on lapsed or its quantity was reduced. The voice and its data are untouched, but it cannot be used to generate new speech. Purchase more professional voice clone slots, or delete other professional voice clones to bring the workspace back within its limit; the voice becomes usable again immediately.

resource_limit_reached

HTTP status: 400 You have reached the maximum number of a resource allowed for your account (e.g., voice clone slots, instant avatar redo attempts, verified avatar group slots). Delete unused resources to free up capacity, wait for limits to reset, or contact HeyGen support to request a higher limit. Also returned when an account reaches the number of voice clones its plan includes. Every plan has a number; higher plans include more. The web and API clone pools are shared, so a clone created on either surface counts toward the same allowance, and voices created as part of avatar creation do not count against a paid plan’s allowance. Deleting a clone frees a slot; upgrading raises the limit where a higher plan exists.

template_limit_reached

HTTP status: 400 The workspace already holds the maximum number of templates (2000), so POST /v3/templates cannot create another one. Delete templates you no longer need with the HeyGen editor’s template library and retry. The limit is per workspace and shared with templates created in the web app.

role_not_allowed

HTTP status: 400 The requested space member role is no longer available and cannot be assigned. The viewer role has been retired; assign a different role (for example creator) instead. Distinct from invalid_parameter, which means the request was malformed — here the request is well-formed but names a role the workspace no longer offers.

voice_unavailable

HTTP status: 400 The requested voice exists but is not in a usable state. This occurs when a cloned voice failed processing, expired, or was canceled. Delete the voice and create a new voice clone, or use a different voice.

voice_settings_not_supported

HTTP status: 400 The voice is valid, but the requested voice-and-engine combination cannot be honored. Unlike voice_unavailable (the voice itself is in a broken state) and invalid_parameter (the request was malformed), the request is well-formed but this voice cannot run under the selected engine or configuration. Common causes:
  • The selected engine needs a source clip for the voice and none could be prepared.
  • A third-party (bring-your-own-key) voice was requested on an engine that only runs on HeyGen’s house account.
  • The engine configuration mapping for the voice is missing and cannot be recreated.
Some cases are transient rather than configuration problems: the voice’s source clip may still be being prepared, or a newly created voice may still be propagating at the provider. In those cases, a later attempt with the same voice and engine may succeed. Allow a few seconds between retries for source preparation, or up to a minute between retries for provider propagation. The message field does not currently distinguish a transient source-clip failure from a permanent one. If the error persists after a few spaced retries, try a different engine or voice, or contact support.

script_too_short

HTTP status: 400 The script provided is too short to generate a video. HeyGen requires the text-to-speech audio to be at least 1.0 second long. Very short scripts (a single word, a period, or a few characters) will not produce enough audio. Add more content to your script and retry the request.

tts_text_invalid

HTTP status: 400 The text provided for text-to-speech conversion is invalid or cannot produce speech. Check that the script is not empty and contains speakable words or valid pauses, then retry.

video_too_long

HTTP status: 400 The input video exceeds the maximum supported duration for this endpoint (for example, filler word removal supports sources up to 2 hours). The error message includes the probed duration and the limit. Trim or split the video and retry.

no_audio_track

HTTP status: 400 The input video has no audio track. Speech-based processing (such as filler word removal) needs speech audio to transcribe. Provide a video that contains an audio stream.

download_failed

HTTP status: 400 A URL provided in your request could not be downloaded. This applies to video URLs, image URLs, audio URLs, and any other user-supplied resource link. Common causes:
  • The URL is not publicly accessible (authentication required, private video, restricted sharing settings).
  • The URL is malformed or points to a page rather than a direct file.
  • The remote server refused the connection or returned an error.
  • For Google Drive links, the file must be shared with “Anyone with the link” access.
  • For YouTube/Vimeo, the video must be public (unlisted or private videos are not supported).
When the failing URL is one you supplied and could be safely re-rendered, the message field names it, without any query parameters. Otherwise — the failure involves one of HeyGen’s own internal resources, or your URL could not be parsed — message is the fixed string Failed to download a required asset. If you supplied the URL, check it first; if it recurs on a URL you’ve confirmed is valid and reachable, contact support and quote that message.

youtube_video_download_forbidden

HTTP status: 400 A YouTube URL you provided points to a video that cannot be downloaded because YouTube itself forbids it. Unlike download_failed, this is not a connectivity or parsing problem — the video is reachable but not permitted for download. Common causes:
  • The video is members-only, age-restricted, or otherwise gated behind a YouTube sign-in.
  • The URL points to a live stream, a premiere, or an upcoming event rather than a finished video.
  • The video is region-locked or blocked in the region HeyGen downloads from.
Supply a different, publicly downloadable YouTube video. Check the message field for the specific reason.

batch_too_large

HTTP status: 400 The batch request contains more items than the maximum allowed. Split the request into smaller batches and retry.

batch_item_invalid

HTTP status: 400 One or more items in the batch request failed validation. Check the message field for details about which item failed and why, correct the payload, and retry.

too_many_ids

HTTP status: 400 The request specified more IDs than the maximum allowed in a single call. Reduce the number of IDs and split the request across multiple calls.

video_delete_failed

HTTP status: 500 The video could not be deleted due to an internal error. Retry the request. If the error persists, contact HeyGen support with the video_id.

internal_error

HTTP status: 500 An unexpected error occurred on HeyGen’s servers. This is not caused by your request. If the error persists, contact HeyGen support and include the full error response for faster debugging.

voice_provider_error

HTTP status: 502 An upstream voice provider that HeyGen relies on (for text-to-speech, voice cloning, or voice design) failed or closed the connection unexpectedly. This is not caused by your request. Retry after a short delay. If the error persists, contact HeyGen support and include the full error response for faster debugging.

not_implemented

HTTP status: 501 The requested endpoint exists, but its server-side implementation has not shipped yet. Retrying the same request will not succeed until the feature is implemented.

gateway_timeout

HTTP status: 504 An upstream dependency did not respond within the time limit. For a resource URL in your request (for example, background audio or an image), verify that the URL is publicly accessible, responds quickly, and is not blocked by firewall or geo-restrictions. For voice generation, retry after a short delay. When the timeout came from a resource URL you supplied, message names that URL when it can be rendered safely; otherwise asset-download timeouts use the fixed string Failed to download a required asset. Voice-generation timeouts instead explain that the voice service is temporarily unavailable. Clients should branch on code, not message.

service_unavailable

HTTP status: 503 A downstream service needed to fulfill your request (for example, the text-to-speech synthesis backend or video quality comparison judge) is temporarily overloaded or unavailable. This is transient and not caused by your request. Back off and retry after a short delay. Also returned when publishing a workflow version loses the version number to a concurrent publish on the same workflow. Same guidance: back off and retry. Also returned by PUT /v3/templates/{template_id}/variables when the element metadata store cannot be read while binding a character variable. The variable set is left unchanged rather than stored with a guessed avatar type; retry the same request. Also returned when starting a workflow run could not confirm its coordinator workflow was accepted (a timeout, transport error, or an unrecognized response from workflow scheduling). This is safe and expected to retry: repeat the request with the same Idempotency-Key to resume the same run. Also returned when canceling a workflow run could not be confirmed as settled (workflow scheduling was unreachable or returned an error). The run is never moved backward by an unconfirmed cancel; retry the cancel request, or fetch the run to see its current status. Also returned when a workflow run’s url or base64 asset input could not be resolved yet. The run stays preparing; repeat the run-create request with the same Idempotency-Key to resume resolution at the first unresolved input. Also returned when professional voice source-audio ingestion is temporarily saturated or a finalized source cannot be read consistently. Back off and retry the create request with the same Idempotency-Key.

hyperframes_project_invalid

HTTP status: 400 The HyperFrames project zip you supplied isn’t a valid composition. Make sure the zip contains an index.html (or the composition entry file you specified) at the root or in a single top-level directory, and that it opens correctly with the hyperframes CLI before submitting.

hyperframes_project_too_large

HTTP status: 413 The HyperFrames project zip exceeds the maximum allowed size for the ingestion method you used. Use asset_id (pre-upload via POST /v3/assets) for projects larger than the url / base64 caps.

hyperframes_render_not_found

HTTP status: 404 No HyperFrames render with that render_id exists for your space, or the render has been soft-deleted. Check that the render_id is correct and was created under the same API key / space.

hyperframes_render_failed

HTTP status: 500 The HyperFrames render failed server-side while producing the video. The project was accepted and the render started, so this is not a problem with your request payload. Retry the render; if it fails repeatedly for the same project, verify the composition renders locally with the hyperframes CLI before contacting support with the render_id. When the cause is in the composition, the render’s failure_message names it instead of the generic text: