
grok-imagine-1.5/image-to-video@preview through hiapi's unified task API.POST /v1/tasks to create the job, poll GET /v1/tasks/<taskId> (or register a callback), then download the MP4 from data.output[0].url before it expires.image_urls entry, optional prompt, duration 1–15 seconds, resolution 480p/720p, aspect_ratio one of auto, 1:1, 16:9, 9:16, 3:2, 2:3. No other fields are accepted.Goal: send one source image plus a motion prompt, get back a short MP4 clip generated by Grok Imagine 1.5 (preview build).
Prerequisites:
sk-... and go in the Authorization: Bearer header.localhost links or files behind auth won't work.curl and Python's requests.Model id is the bare string grok-imagine-1.5/image-to-video@preview — the @preview suffix is part of the id, don't strip it.
Create the task:
curl -X POST "https://api.hiapi.ai/v1/tasks" \
-H "Authorization: Bearer sk-YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "grok-imagine-1.5/image-to-video@preview",
"input": {
"image_urls": ["https://example.com/product-shot.jpg"],
"prompt": "slow cinematic push-in, soft studio light drifting across the subject",
"duration": 6,
"resolution": "720p",
"aspect_ratio": "16:9"
}
}'
A successful submission returns 200 with a taskId:
{
"code": 200,
"data": { "taskId": "tk-hiapi-01KWVVSTX0TJ32AD45F6ZQ08W5", "status": "queued" },
"message": "success"
}
Poll until the task reaches a terminal state (status moves through queued → handling → success or fail):
curl "https://api.hiapi.ai/v1/tasks/tk-hiapi-01KWVVSTX0TJ32AD45F6ZQ08W5" \
-H "Authorization: Bearer sk-YOUR_KEY"
The finished task (real response, trimmed):
{
"code": 200,
"data": {
"taskId": "tk-hiapi-01KWVVSTX0TJ32AD45F6ZQ08W5",
"model": "grok-imagine-1.5/image-to-video@preview",
"status": "success",
"storage": "temp",
"output": [
{
"type": "video",
"url": "https://temp.hiapi.ai/.../01KWVVSTX0TJ32AD45F6ZQ08W5-0.mp4",
"expireAt": 1783951451
}
]
},
"message": "success"
}
⚠️ storage: "temp" and expireAt mean the download URL is temporary. Download the MP4 and store it on your own infrastructure as soon as the task succeeds — don't hotlink it.
Complete script — create, poll, download:
import time
import requests
API_KEY = "sk-YOUR_KEY"
BASE = "https://api.hiapi.ai/v1/tasks"
HEADERS = {"Authorization": f"Bearer {API_KEY}"}
# 1. Create the task
resp = requests.post(BASE, headers=HEADERS, json={
"model": "grok-imagine-1.5/image-to-video@preview",
"input": {
"image_urls": ["https://example.com/product-shot.jpg"],
"prompt": "slow cinematic push-in, soft studio light drifting across the subject",
"duration": 6,
"resolution": "720p",
"aspect_ratio": "16:9",
},
})
resp.raise_for_status()
task_id = resp.json()["data"]["taskId"]
print("task:", task_id)
# 2. Poll for the result (image-to-video usually lands well within a few minutes)
deadline = time.time() + 600
while time.time() < deadline:
task = requests.get(f"{BASE}/{task_id}", headers=HEADERS).json()["data"]
status = task["status"]
if status == "success":
break
if status == "fail":
raise RuntimeError(f"generation failed: {task.get('error')}")
time.sleep(5)
else:
raise TimeoutError(f"task {task_id} still not finished after 10 min")
# 3. Download immediately — the output URL expires
video_url = task["output"][0]["url"]
mp4 = requests.get(video_url, timeout=120)
mp4.raise_for_status()
with open("clip.mp4", "wb") as f:
f.write(mp4.content)
print("saved clip.mp4,", len(mp4.content), "bytes")
These constraints come straight from the live validator, not from guesswork — sending anything else returns 400 INVALID_REQUEST with a precise message:
| Field | Type | Rules |
|---|---|---|
image_urls | array of string | required, exactly 1 URL (maxItems: 1) |
prompt | string | optional — describes motion, camera work, mood |
duration | integer | 1–15 seconds |
resolution | string | 480p or 720p |
aspect_ratio | string | auto, 1:1, 16:9, 9:16, 3:2, 2:3 |
Notes:
seed, negative_prompt, fps, motion_strength, or camera_fixed field on this model. The schema rejects unknown properties (additional properties ... not allowed), so steer motion and camera behavior entirely through prompt.image_urls: maxItems: got 2, want 1.aspect_ratio: "auto" follows the source image's own proportions — usually what you want for product shots.Callbacks instead of polling. For anything beyond a script, register a webhook at task creation and skip the polling loop:
{
"model": "grok-imagine-1.5/image-to-video@preview",
"input": { "image_urls": ["https://example.com/shot.jpg"], "duration": 6 },
"callback": { "url": "https://yourapp.com/hooks/hiapi", "when": "final" }
}
callback.url must be a public http(s) endpoint and when only supports "final" — you get one POST when the task reaches success or fail. Polling is fine for CLIs and batch jobs; callbacks win when you're fanning out many clips and don't want a fleet of pollers. Keep a fallback poll (e.g. every few minutes for tasks with no callback received) so a dropped webhook doesn't strand a job.
Idempotency. Task creation is not deduplicated on the server, so a naive retry after a network timeout can double-submit and double-bill. Persist the taskId as soon as the create call returns, and on retry check your own store first; if you crashed between submit and persist, list recent tasks (GET /v1/tasks) and match on model + creation time before re-submitting.
Error handling. The failure modes you'll actually hit:
401 with {"error": {"code": "permission_denied", "type": "hiapi_error", ...}} — bad key, or the key lacks access to this model. The response includes a request_id for support.400 with error_code: "INVALID_REQUEST" — schema violation; the message names the exact field and rule (e.g. duration: maximum: got 99, want 15). Fix the payload, don't retry blindly.402 insufficient balance — video models pre-check your quota at submit time. Top up and resubmit.status: "fail" on the task itself — generation-side failure; inspect data.error and retry with backoff. Your source image being unreachable is a common cause.Store outputs immediately. Worth repeating: output[0].url lives on temp storage with an expireAt timestamp. The moment you see success, stream the MP4 to your own bucket. If your pipeline crashes after success but before download, re-fetch the task by id — the URL stays valid until expireAt, not forever.
How long can a grok-imagine-1.5 preview video be?
1 to 15 seconds, set by the integer duration field. Requesting more returns a 400 (duration: maximum: got X, want 15). If you need up to 30 s, use the older grok-imagine/image-to-video.
What resolutions and aspect ratios does it support?
resolution is 480p or 720p; aspect_ratio is auto, 1:1, 16:9, 9:16, 3:2, or 2:3. 9:16 at 720p is the go-to combo for Shorts/Reels-style vertical clips.
Can I pass multiple reference images?
No — image_urls accepts exactly one URL on this model. For multi-reference workflows, chain tasks or composite your references into a single input image first.
Is there a seed or negative prompt?
No. The input schema rejects seed, negative_prompt, fps, and similar fields outright. Motion style, camera movement, and mood are all controlled through the prompt string.
How much does it cost? It's usage-based, billed per second of generated video, with resolution affecting the multiplier. Rates change, so check the pricing page for current numbers rather than hardcoding them.
Why did my request return 401 permission_denied?
Either the API key is wrong/revoked, or it doesn't have access to this model. Verify the key in your dashboard and make sure you're sending Authorization: Bearer sk-... — a missing Bearer prefix produces the same error.
How do I know when the video is ready without polling?
Attach "callback": {"url": "https://your-endpoint", "when": "final"} when creating the task. hiapi POSTs to your endpoint once the task hits a terminal state. Poll GET /v1/tasks/<taskId> as a fallback.
Why does my video URL stop working after a few days?
Outputs land on temporary storage — every output entry carries an expireAt timestamp. Download the file right after success and serve it from your own storage.