Image & Video quickstart
Image & Video quickstart
The Image & Video API is asynchronous. You submit a generation, and once it finishes you download the result from a signed URL. Images and videos have separate endpoints, but the request and response shapes are the same for both.
There are two ways to collect the result. Webhook delivery is the recommended one, and what the examples below use: ElevenLabs calls your endpoint the moment a generation reaches a terminal status, so nothing is spent waiting. Polling is the fallback for when you have no endpoint to receive a callback, and each example shows how to drop back to it.
The Image & Video API requires a Pro plan or above. Calls from a workspace below that tier are
rejected with a 402 paid_plan_required error. Your API key must also carry the Image & Video or
Flows permission for the workspace.
Generate an image
Create an API key
Create an API key in the dashboard here, which you’ll use to securely access the API.
Store the key as a managed secret and pass it to the SDKs either as a environment variable via an .env file, or directly in your app’s configuration depending on your preference.
Submit the generation
Each model has its own request class, and the fields on it are the parameters that model accepts, so switching models can change which fields are available. Unknown fields are rejected rather than ignored.
webhook asks for the finished result to be delivered to your workspace’s webhooks, so the call
returns as soon as the generation is queued. It requires a webhook subscribed to generation
events; see Image & Video
webhooks to set one up, or omit the
field and poll instead.
The response contains the generation ID and nothing else. A newly created generation is always
pending:
Collect the result
Because the request opted into webhook, ElevenLabs posts a flows_generation event to your
endpoint once the generation reaches completed or failed. The event’s data is identical to
what the GET endpoint returns, and
Image & Video webhooks walks through
the handler that receives it.
Without an endpoint to receive callbacks, drop webhook from the request above and poll instead.
Fetch the generation until its status is completed or failed, leaving at least two seconds
between requests for an image — see Polling guidelines for the intervals
to use per modality.
Either way, a completed generation carries the same fields:
Generate a video
Video generations use flows.video and follow the same submit-and-collect pattern. A video can take
several minutes, so this example opts into webhook delivery with webhook rather than waiting on
the result.
The call returns as soon as the generation is queued, and the finished result is delivered to every
webhook in your workspace subscribed to generation events. Video output is MP4, so the completed payload reports a
content_mime_type of video/mp4. See
Image & Video webhooks for configuring a
webhook and writing the handler that receives this.
webhook requires at least one workspace webhook subscribed to generation events. Without one,
the create call is rejected rather than starting a generation whose result has nowhere to go. Drop
the field to fall back to polling with flows.video.get, and poll no more than once every 10
seconds.
Collecting results
Webhooks and polling return the same payload, so the choice is about how you wait for it rather than what you get.
Use webhooks wherever you can. Reach for polling when you have nowhere to receive a callback, and follow the intervals below when you do.
Choosing webhook targets
webhook accepts two forms. WebhookTarget_All reaches every webhook subscribed to generation
events, which is the right default because it survives webhooks being rotated or replaced.
WebhookTarget_Ids narrows delivery to specific webhooks, for when one workspace fans out to several
consumers and a given job should reach only one of them:
Every ID must already be subscribed to generation events; naming an unsubscribed webhook is rejected rather than silently ignored. The delivered payload is identical to what the GET endpoint returns, so a handler written against one works for the other. The webhooks guide covers configuring a webhook, verifying the signature, and handling the event.
Polling guidelines
A generation’s runtime depends on the model, the resolution, and, for video, the duration, so poll on an interval matched to what you asked for rather than on a fixed loop:
- Images: poll no more than once every 2 seconds. Most finish within a few seconds.
- Video: poll no more than once every 10 seconds. Expect minutes, not seconds, and scale the
interval with
duration_secsandresolution.
Two rules apply to both. Back off when a generation runs long — doubling the interval up to about a minute keeps a slow generation from turning into hundreds of requests. And give the loop a ceiling, so a stuck generation ends as a timeout in your own code rather than an unbounded loop.
Polling faster than this earns you nothing: a generation’s status does not change any sooner because you asked twice. Sustained aggressive polling can return 429 responses, which you should handle with exponential backoff.
Generation lifecycle
A generation moves through four statuses. The two terminal statuses carry different fields, so
branch on status before reading the rest of the response.
content_url is a signed URL that expires roughly an hour after the response is returned. Fetch
the generation again for a fresh URL rather than storing the signed URL itself.
Handling failures
A failed generation reports a failure_reason category alongside a human-readable error_message:
Failed generations are not charged. Parameter problems that can be detected up front — an unsupported field, a value outside a model’s allowed range, or an invalid combination of reference inputs — are rejected by the create request instead, before any generation starts.
Pricing
Generations are charged in credits. The cost depends on the model, the parameters you choose such as resolution and duration, and the inputs you provide. A generation costs the same through the API as it does in the ElevenLabs app, where the cost is shown before you submit. See Image & Video in the playground for how the cost of a given model and setting combination is presented.
List your generations
Each endpoint lists the generations created through it, newest first. Results are scoped to your workspace and to this API, so generations created in the ElevenLabs app do not appear.
page_size accepts 1 to 100 and defaults to 30. Pass status to return only generations in one
lifecycle state, and model_id to return only generations of a single model. Treat next_cursor as
opaque: pass the exact value back and stop when has_more is false.
Available models
The API exposes a subset of the models available in the ElevenLabs app. Each model accepts only the parameters listed for it — sending a field another model supports returns a validation error.
The ByteDance models, Seedance and Seedream, are disabled by default and require explicit approval
before you can generate with them. Until access is granted, a request naming one of them is
rejected with a model_access_denied error. Contact support to request access.
Image models
Video models
For model capabilities, availability, and pricing, see the Image & Video overview.