curl --request POST \
--url https://api.apiyi.com/v1/videos \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"model": "grok-imagine-video-1.5-lite"
}
'import requests
url = "https://api.apiyi.com/v1/videos"
payload = { "model": "grok-imagine-video-1.5-lite" }
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({model: 'grok-imagine-video-1.5-lite'})
};
fetch('https://api.apiyi.com/v1/videos', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.apiyi.com/v1/videos",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'model' => 'grok-imagine-video-1.5-lite'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.apiyi.com/v1/videos"
payload := strings.NewReader("{\n \"model\": \"grok-imagine-video-1.5-lite\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.apiyi.com/v1/videos")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"model\": \"grok-imagine-video-1.5-lite\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.apiyi.com/v1/videos")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"model\": \"grok-imagine-video-1.5-lite\"\n}"
response = http.request(request)
puts response.read_body{
"id": "task_xxxxxxxxxxxxxxxx",
"object": "video",
"model": "grok-imagine-video-1.5-lite",
"status": "queued",
"progress": 0,
"seconds": "<string>",
"created_at": 123,
"completed_at": 123
}Grok Imagine Video Generation API Reference
Grok Imagine video generation API reference and live testing — text-to-video / image-to-video, async three-step flow, 1–15 seconds, 480p / 720p / 1080p, priced the same as xAI per second
curl --request POST \
--url https://api.apiyi.com/v1/videos \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"model": "grok-imagine-video-1.5-lite"
}
'import requests
url = "https://api.apiyi.com/v1/videos"
payload = { "model": "grok-imagine-video-1.5-lite" }
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({model: 'grok-imagine-video-1.5-lite'})
};
fetch('https://api.apiyi.com/v1/videos', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.apiyi.com/v1/videos",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'model' => 'grok-imagine-video-1.5-lite'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.apiyi.com/v1/videos"
payload := strings.NewReader("{\n \"model\": \"grok-imagine-video-1.5-lite\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.apiyi.com/v1/videos")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"model\": \"grok-imagine-video-1.5-lite\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.apiyi.com/v1/videos")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"model\": \"grok-imagine-video-1.5-lite\"\n}"
response = http.request(request)
puts response.read_body{
"id": "task_xxxxxxxxxxxxxxxx",
"object": "video",
"model": "grok-imagine-video-1.5-lite",
"status": "queued",
"progress": 0,
"seconds": "<string>",
"created_at": 123,
"completed_at": 123
}GrokOfficial group, granted on application: Grok Imagine Video is not in the Default group; access must be requested before you can call it (including the Playground on this page). Without access, or with a pay-per-request Token, every call returns 503. See Grok Imagine Video Overview - Group Setup.Bearer sk-xxx), pick the model, fill in prompt / seconds / size, and send; add input_reference for image-to-video. The Playground only performs step 1 (submit) — once you have the task id, poll and download as shown below.- Without
size/secondsthe default is 480p and 8 seconds, and that is what you are billed for. grok-imagine-videohas no 1080p:1920x1080returns 400; use-1.5or-1.5-litefor 1080p.- Moderation blocks are billed: a blocked task still reports
completed, but carrieserror.code: "moderation_blocked"and has no video.
- Step 1 (this page):
POST /v1/videos→ returns the taskid+status: "queued" - Step 2: poll
GET /v1/videos/{id}untilstatusis"completed"or"failed" - Step 3: download the MP4 from
GET /v1/videos/{id}/content
Code Examples
Python (text-to-video, all three steps)
import os, time, requests
BASE = "https://api.apiyi.com/v1"
H = {"Authorization": f"Bearer {os.environ['APIYI_API_KEY']}"}
# Step 1: submit
task = requests.post(f"{BASE}/videos", headers=H, json={
"model": "grok-imagine-video-1.5",
"prompt": "A lighthouse on the coast at dusk, slow push-in, waves lapping the rocks, seabirds calling, cinematic lighting",
"seconds": "5",
"size": "1280x720", # 720p landscape; the default without it is 480p
}, timeout=60).json()
vid = task["id"]
# Step 2: poll
for _ in range(75): # about 10 minutes at most
time.sleep(8)
job = requests.get(f"{BASE}/videos/{vid}", headers=H, timeout=60).json()
if job["status"] in ("completed", "failed"):
break
if job["status"] != "completed" or job.get("error"):
# moderation_blocked: blocked by moderation, no video, but billed
raise RuntimeError(job.get("error"))
# Step 3: download and store right away
mp4 = requests.get(f"{BASE}/videos/{vid}/content", headers=H, timeout=300)
open("out.mp4", "wb").write(mp4.content)
Python (image-to-video, base64 input image)
import base64, os, requests
H = {"Authorization": f"Bearer {os.environ['APIYI_API_KEY']}"}
img = base64.b64encode(open("first-frame.jpg", "rb").read()).decode()
task = requests.post("https://api.apiyi.com/v1/videos", headers=H, json={
"model": "grok-imagine-video",
"prompt": "the cat slowly turns its head and blinks",
"seconds": "3",
"input_reference": f"data:image/jpeg;base64,{img}", # a public image URL works too
}, timeout=60).json()
print(task["id"]) # then poll and download as above; the output frame follows the input image
cURL
# Step 1: submit text-to-video (1.5-lite, 2 seconds at 480p)
curl -X POST "https://api.apiyi.com/v1/videos" \
-H "Authorization: Bearer sk-your-api-key" \
-H "Content-Type: application/json" \
-d '{
"model": "grok-imagine-video-1.5-lite",
"prompt": "a red paper boat drifting on a calm lake, gentle ripples",
"seconds": "2",
"size": "854x480"
}'
# Image-to-video can also upload a local file with multipart
curl -X POST "https://api.apiyi.com/v1/videos" \
-H "Authorization: Bearer sk-your-api-key" \
-F "model=grok-imagine-video" \
-F "prompt=the cat slowly turns its head" \
-F "seconds=2" \
-F "[email protected]"
# Step 2: check status
curl "https://api.apiyi.com/v1/videos/task_xxxxxxxx" \
-H "Authorization: Bearer sk-your-api-key"
# Step 3: download the video
curl -o out.mp4 "https://api.apiyi.com/v1/videos/task_xxxxxxxx/content" \
-H "Authorization: Bearer sk-your-api-key"
Node.js (fetch)
import fs from 'node:fs';
const BASE = 'https://api.apiyi.com/v1';
const H = { 'Authorization': `Bearer ${process.env.APIYI_API_KEY}`, 'Content-Type': 'application/json' };
const task = await fetch(`${BASE}/videos`, {
method: 'POST',
headers: H,
body: JSON.stringify({
model: 'grok-imagine-video-1.5-lite',
prompt: 'A serene Japanese garden with cherry blossoms drifting, koi pond, golden hour',
seconds: '4',
size: '720x1280' // 720p portrait
})
}).then(r => r.json());
let job;
for (let i = 0; i < 75; i++) {
await new Promise(r => setTimeout(r, 8000));
job = await fetch(`${BASE}/videos/${task.id}`, { headers: H }).then(r => r.json());
if (['completed', 'failed'].includes(job.status)) break;
}
if (job.status !== 'completed' || job.error) throw new Error(JSON.stringify(job.error));
const mp4 = await fetch(`${BASE}/videos/${task.id}/content`, { headers: H });
fs.writeFileSync('out.mp4', Buffer.from(await mp4.arrayBuffer()));
Parameter Quick Reference
| Parameter | Type | Required | Default | Notes |
|---|---|---|---|---|
model | string | ✅ | — | grok-imagine-video / grok-imagine-video-1.5 / grok-imagine-video-1.5-lite |
prompt | string | Required for text-to-video | — | Scene, subject, action, camera, lighting; for image-to-video it describes the motion |
seconds | string / integer | ❌ | 8 | 1–15 seconds; out of range returns 400 |
size | string | ❌ | 480p | WIDTHxHEIGHT pixels; the shorter side sets resolution and the aspect ratio snaps to the nearest; 480p / 720p / 1080p also work |
resolution / aspect_ratio | string | ❌ | — | xAI’s native fields; they win when sent together with size |
input_reference | string / file | ❌ | — | Input image for image-to-video: public URL, data URI, or multipart file |
| Model | 480p | 720p | 1080p | Input image |
|---|---|---|---|---|
grok-imagine-video | $0.05 | $0.07 | Not supported | $0.002 / image |
grok-imagine-video-1.5 | $0.08 | $0.14 | $0.25 | $0.01 / image |
grok-imagine-video-1.5-lite | $0.02 | $0.03 | $0.14 | $0.01 / image |
Response Examples
Submit (step 1):{
"id": "task_xxxxxxxxxxxxxxxx",
"object": "video",
"model": "grok-imagine-video-1.5-lite",
"status": "queued",
"progress": 0,
"created_at": 1792320668
}
{
"id": "task_xxxxxxxxxxxxxxxx",
"object": "video",
"model": "grok-imagine-video-1.5-lite",
"status": "completed",
"progress": 100,
"seconds": "2",
"created_at": 1792320668,
"completed_at": 1792320715
}
status is still completed, but there is an extra error field and no video to download:
{
"id": "task_xxxxxxxxxxxxxxxx",
"status": "completed",
"error": { "code": "moderation_blocked", "message": "..." }
}
Related Docs
- Grok Imagine Video Overview - pricing, group application, parameters, FAQ
- Grok Imagine Official Images - image generation in the same group
Authorizations
API Key from the APIYI console (Token group must include GrokOfficial)
Body
Model ID (billed per second × resolution rate):
grok-imagine-video— 480p $0.05/s, 720p $0.07/s, no 1080pgrok-imagine-video-1.5— 480p $0.08, 720p $0.14, 1080p $0.25 per secondgrok-imagine-video-1.5-lite— 480p $0.02, 720p $0.03, 1080p $0.14 per second
grok-imagine-video, grok-imagine-video-1.5, grok-imagine-video-1.5-lite Prompt: scene + subject + action + camera + lighting. Required for text-to-video; for image-to-video it describes motion and camera
"a red paper boat drifting on a calm lake, gentle ripples, soft morning light"
Duration from 1 to 15 seconds, as a string or a number; default 8. Out-of-range values return 400. Cost scales with seconds — use 1–2 seconds while testing.
"2"
Output size in WIDTHxHEIGHT pixels: the shorter side sets the resolution (≥1080 is 1080p, ≥720 is 720p, otherwise 480p) and the aspect ratio snaps to the nearest supported one.
You can also pass 480p / 720p / 1080p directly. Without it, the default is 480p.
854x480— 480p landscape (measured 848×480)1280x720/720x1280— 720p landscape / portrait1920x1080/1080x1920— 1080p (1.5 / 1.5-lite only)
854x480, 480x854, 1280x720, 720x1280, 1920x1080, 1080x1920 Input image for image-to-video: a public image URL or a data:image/jpeg;base64,... data URI. Passing it makes the job image-to-video, and the output frame follows the input image.
Each input image costs an extra $0.002 (grok-imagine-video) or $0.01 (1.5 / 1.5-lite).
"https://example.com/first-frame.jpg"
Response
Task submitted; returns the task id and queued status
Task ID for polling and download
"task_xxxxxxxxxxxxxxxx"
Always video
"video"
"grok-imagine-video-1.5-lite"
Task status:
queued— submitted, waitingin_progress— generatingcompleted— done, download from/v1/videos/{id}/content; witherror.code: moderation_blockedit was blocked and has no videofailed— failed
queued, in_progress, completed, failed "queued"
Progress 0–100, coarse-grained; not suitable for a precise progress bar
0
Video duration, returned once done
Task creation Unix timestamp (seconds)
Task completion Unix timestamp (seconds), returned once done
Was this page helpful?