Video Generation
Learn how to make API calls to generate video from text or generate video from an image
Overview
This guide covers generating videos on AI.ML with video generation models, including request/response formats, polling, and downloading the final file.
Base URL: <https://api.ai.ml>
Auth: Bearer token via Authorization: Bearer <AIML_API_KEY>
Model: video-gen.pro-1.0
Core workflow (all tasks):
-
Create task →
POST /v1/generation/tasks/videoReturns a task UUID. -
Poll task →
GET /v1/generation/tasks/video/{task-uuid}
Whenstatus === "succeeded", response includes a backend media URL invideoUrl(e.g.,/v1/media/videos/orgs%2F64%2Fvideos%2F...). -
Resolve media URL → Authorized
GETto the backend media URL returns{ "url": "<presigned-download-url>" }. -
Download video →
GETthe presigned URL (no auth) to savevideo.mp4.
Typical inline directives (inside your text prompt):
-
--resolution 720p|1080p|... -
--duration <seconds> -
--camerafixed true|false
Video API
Endpoints
POST /v1/generation/tasks/video— Create a text-to-video or image-to-video task.GET /v1/generation/tasks/video/{task-uuid}— Poll task status & get backend media URL when ready.GET /v1/media/videos/{path-encoded}— Resolve backend media URL to presigned download URL.
Request (JSON) — POST /v1/generation/tasks/video
| Field | Type | Required | Example | Notes |
|---|---|---|---|---|
| model | string | ✓ | "video-gen.pro-1.0" | Model identifier |
| content | array | ✓ | See below | Ordered list of inputs |
| content[].type | string | ✓ | "text" or "image_url" | Input item type |
| content[].text | string | if type = text | "A serene forest ... --resolution 720p --duration 5" | Text prompt + inline directives |
| content[].image_url.url | string | if type = image_url | https://example.com/still.jpg | Publicly accessible image URL |
Response (create task)
{
"data": {
"uuid": "9b9f1e2c-1a23-45b6-8c9d-1234567890ab",
"status": "queued"
}
}Polling — GET /v1/generation/tasks/video/{task-uuid}
Successful (ready) example
{
"data": {
"uuid": "9b9f1e2c-1a23-45b6-8c9d-1234567890ab",
"status": "succeeded",
"videoUrl": "/v1/media/videos/orgs%2F64%2Fvideos%2F2025%2F10%2Fabc123.mp4"
}
}Statuses you may see
-
queued/running— keep polling -
succeeded—videoUrlpresent -
failed— checkerrorobject if present -
canceled— task canceled server-side
Error example
{
"error": {
"code": "invalid_request",
"message": "Text content is required."
}
}Resolve & Download
- Resolve backend media URL (authorized):
GET /v1/media/videos/orgs%2F64%2Fvideos%2F2025%2F10%2Fabc123.mp4
Authorization: Bearer <AIML_API_KEY>Response:
{ "url": "https://presigned.cdn.ai.ml/..." }
2. Download (no auth):
GET <https://presigned.cdn.ai.ml/...> → bytes (video/mp4)
Text-to-Video Generation
import fs from 'node:fs';
import fetch from 'node-fetch';
const API = 'https://api.ai.ml';
const API_KEY = process.env.AIML_API_KEY || 'AIML_API_KEY';
(async () => {
// 1) Create task
const createRes = await fetch(`${API}/v1/generation/tasks/video`, {
method: 'POST',
headers: {
Authorization: `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: 'video-gen.pro-1.0',
content: [
{
type: 'text',
text: 'A serene forest scene with a flowing stream and dappled sunlight --resolution 720p --duration 5'
}
]
})
});
if (!createRes.ok) throw new Error(`HTTP ${createRes.status}: ${await createRes.text()}`);
const { data: { uuid } } = await createRes.json();
// 2) Poll for status
let backendUrl;
while (!backendUrl) {
const sRes = await fetch(`${API}/v1/generation/tasks/video/${uuid}`, {
headers: { Authorization: `Bearer ${API_KEY}` }
});
const s = await sRes.json();
if (s?.data?.status === 'failed') throw new Error('Task failed');
backendUrl = s?.data?.videoUrl;
if (!backendUrl) await new Promise(r => setTimeout(r, 3000));
}
// 3) Resolve presigned URL
const resRes = await fetch(backendUrl.startsWith('http') ? backendUrl : `${API}${backendUrl}`, {
headers: { Authorization: `Bearer ${API_KEY}` }
});
const { url: presignedUrl } = await resRes.json();
// 4) Download video
const fileRes = await fetch(presignedUrl);
const buf = Buffer.from(await fileRes.arrayBuffer());
fs.writeFileSync('video.mp4', buf);
console.log('Saved video.mp4');
})().catch(console.error);Image-to-Video Generation
Attach an image plus a text instruction by adding an image_url item after your text item in content.
import fs from 'node:fs';
import fetch from 'node-fetch';
const API = 'https://api.ai.ml';
const API_KEY = process.env.AIML_API_KEY || 'AIML_API_KEY';
(async () => {
const createRes = await fetch(`${API}/v1/generation/tasks/video`, {
method: 'POST',
headers: {
Authorization: `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: 'video-gen.pro-1.0',
content: [
{
type: 'text',
text: 'In the sky, soft cotton-like clouds drift... --resolution 720p --duration 5 --camerafixed false'
},
{
type: 'image_url',
image_url: { url: 'https://google.com/sample' }
}
]
})
});
const { data: { uuid } } = await createRes.json();
let backendUrl;
while (!backendUrl) {
const sRes = await fetch(`${API}/v1/generation/tasks/video/${uuid}`, {
headers: { Authorization: `Bearer ${API_KEY}` }
});
const s = await sRes.json();
if (s?.data?.status === 'failed') throw new Error('Task failed');
backendUrl = s?.data?.videoUrl;
if (!backendUrl) await new Promise(r => setTimeout(r, 3000));
}
const resRes = await fetch(backendUrl.startsWith('http') ? backendUrl : `${API}${backendUrl}`, {
headers: { Authorization: `Bearer ${API_KEY}` }
});
const { url: presignedUrl } = await resRes.json();
const fileRes = await fetch(presignedUrl);
fs.writeFileSync('video.mp4', Buffer.from(await fileRes.arrayBuffer()));
console.log('Saved video.mp4');
})().catch(console.error);Poll & resolve exactly as in the Text-to-Video flow.
Implementation Notes & Best Practices
- Prompt design: Keep cinematic directions concise; use inline flags for resolution/duration/camera.
- Polling cadence: 2–5 seconds is a good balance; include a timeout or retry limit.
- Error handling: Check HTTP codes and
data.status. - Security: Never expose API keys in client apps.
- Presigned URLs: They expire quickly—resolve right before downloading.
- File sizes: Prefer streaming and chunked writes for large outputs.