Video Generation
Send an image URL and get back a short MP4 video generated from it. Add a text prompt to direct what happens in the video, or leave it out and let the image speak for itself.
Generation runs in the background: you create a job, check on it every few seconds, and receive a video URL when it’s done. Most videos are ready in one to four minutes.
Quickstart
You need an API key and an image on an Imgix Source with Image to Video enabled. The Source must belong to the same account as the key.
1. Create a job. The only required field is url. The auto model picks the best generation engine for your image.
curl -X POST https://api.imgix.com/api/v1/generations/video \
-H "Authorization: Bearer $IMGIX_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-domain.imgix.net/image.jpg",
"model": "auto"
}'2. Check the job with the id from step 1. Repeat every 5 to 10 seconds until status is ready or errored.
curl https://api.imgix.com/api/v1/generations/video/66523ecdc9e5a19840c20941 \
-H "Authorization: Bearer $IMGIX_API_KEY"3. Use the video. result.url serves the MP4 directly. Use it anywhere you would use a video file:
<video src="https://your-domain.imgix.net/image.jpg?itv=ef22b73a53&variant=542db280" controls autoplay muted loop playsinline></video>Examples
Each video below was generated from a single still image by the request shown with it.
Choosing a Model
Start with auto. Use a Veo model when you want to describe the video precisely and get it back faster.
| Model | Prompt | Duration (seconds) | Shape when aspect_ratio is omitted | Typical time to ready |
|---|---|---|---|---|
auto | Optional | 1 to 30 | Same as the source image | 3 to 4 minutes |
klingai | Optional | 1 to 30 | Same as the source image | 2½ to 4 minutes |
veo3.1 | Required | 4, 6, or 8 | 16:9 | 1 to 2 minutes |
veo3.1-fast | Required | 4, 6, or 8 | 16:9 | 1 to 1½ minutes |
Typical times are the median and 90th percentile of production jobs over 30 days. A small number of auto and klingai jobs take up to 20 minutes.
Unsupported durations are not rejected
If a model cannot render the duration you ask for, the job still succeeds with a different length: the Veo models render 8 seconds, and auto and klingai render 5. For example, "model": "veo3.1", "duration": 10 produces an 8-second video. Check the table above before choosing a duration.
Request Parameters
POST /api/v1/generations/video takes a JSON body.
| Parameter | Type | Required | Description |
|---|---|---|---|
url | String | Yes | The image to generate from. It must be on an Imgix Source with Image to Video enabled. JPEG and PNG are used as-is; other formats are converted first. |
model | String | No | auto, klingai, veo3.1, or veo3.1-fast. See Choosing a Model. |
prompt | String | For Veo models | What you want to see happen, in plain language: who or what moves, how, and where the camera goes. For example, "the band starts playing, the drummer counts in and the guitarist steps forward". |
duration | Integer | No | Length in seconds. Defaults to 8. See Choosing a Model for the values each model renders. |
aspect_ratio | String | No | 16:9 (landscape) or 9:16 (portrait). When omitted, Veo models render 16:9 and the other models match the source image. |
Every job generates a new video, even when the request is identical to an earlier one.
Job Lifecycle
| Status | Meaning | What to do |
|---|---|---|
started | The job was accepted. This is the status in the response to POST. | Keep checking. |
preparing | The video is being generated. | Keep checking. |
ready | The video is available at result.url. | Stop checking and use the video. |
errored | Generation failed. | Stop checking. Create a new job to try again. |
Check every 5 to 10 seconds. Stop waiting after 20 minutes and treat the job as failed. Job records are kept for 24 hours after creation, after which GET returns 404.
Response Reference
Both endpoints return the same shape.
| Attribute | Type | Description |
|---|---|---|
data.id | String | The job ID. Use it with GET /api/v1/generations/video/:job_id. |
status | String | started, preparing, ready, or errored. |
created_at | Integer | Unix timestamp when the job was created. |
updated_at | Integer | Unix timestamp when the job finished. Present when status is ready or errored. |
result.url | String | Present when status is ready. The video URL: your source url with itv and variant added. |
error.code | Integer | Present when status is errored. 500. |
error.message | String | Present when status is errored. A short description, such as failed to generate. |
Errors
Request errors return an HTTP status and a JSON body with a detail field. No job is created and nothing is generated.
| HTTP | Cause | Example detail |
|---|---|---|
400 | model is not a recognized value. | unknown model 'not-a-model' |
400 | prompt is missing or empty for a Veo model. | prompt is required for model 'veo3.1' |
400 | aspect_ratio is not 16:9 or 9:16 for a Veo model. | aspect_ratio '1:1' is not supported for model 'veo3.1'; supported: 16:9, 9:16 |
400 | url is not on a valid, active Imgix Source. | Bad Request |
401 | The API key is missing or invalid. | invalid token |
401 | The Source in url belongs to a different account than the API key. | Unauthorized |
403 | The Source is disabled, or Image to Video is not enabled on it. | Forbidden |
404 | GET only. The job ID does not exist or its record has expired. | Job not found |
422 | The body is malformed: url is missing, a field has the wrong type, or aspect_ratio is not a recognized value. detail is a list of field errors. | [{"type": "missing", "loc": ["body", "url"], "msg": "Field required", ...}] |
500 | The job could not be created. Retry the request. | Internal Server Error |
A job that fails after it was created does not return an HTTP error. GET returns 200 with status: "errored":
{
"data": {
"type": "job",
"id": "3f98a9b2c4d5e6f7a8b9c0d1",
"attributes": {
"status": "errored",
"created_at": 1792783178,
"updated_at": 1792783410,
"error": {
"code": 500,
"message": "failed to generate"
}
}
}
}Complete Examples
Each example creates a job, waits for it, and prints the video URL. Set IMGIX_API_KEY in your environment and replace the image URL with one of your own.
// Node.js 18 or later. Run: IMGIX_API_KEY=... node generate.mjs
const API = "https://api.imgix.com/api/v1/generations/video";
const headers = {
Authorization: `Bearer ${process.env.IMGIX_API_KEY}`,
"Content-Type": "application/json",
};
async function generateVideo(request) {
const created = await fetch(API, {
method: "POST",
headers,
body: JSON.stringify(request),
});
if (!created.ok) {
throw new Error(`${created.status}: ${await created.text()}`);
}
const { id } = (await created.json()).data;
const deadline = Date.now() + 20 * 60 * 1000;
while (Date.now() < deadline) {
await new Promise((resolve) => setTimeout(resolve, 5000));
const job = await fetch(`${API}/${id}`, { headers });
const { status, result, error } = (await job.json()).data.attributes;
if (status === "ready") return result.url;
if (status === "errored") throw new Error(error.message);
}
throw new Error(`job ${id} did not finish within 20 minutes`);
}
const videoUrl = await generateVideo({
url: "https://your-domain.imgix.net/image.jpg",
model: "auto",
});
console.log(videoUrl);