TL;DR
- What this is: a copy-paste guide to animating a still image into a short video with
grok-imagine-1.5/image-to-video@previewthrough hiapi's unified task API. - Flow:
POST /v1/tasksto create the job, pollGET /v1/tasks/<taskId>(or register a callback), then download the MP4 fromdata.output[0].urlbefore it expires. - Input schema (verified against the live API): exactly one
image_urlsentry, optionalprompt,duration1–15 seconds,resolution480p/720p,aspect_ratioone ofauto,1:1,16:9,9:16,3:2,2:3. No other fields are accepted.
What you'll build and what you need
Goal: send one source image plus a motion prompt, get back a short MP4 clip generated by Grok Imagine 1.5 (preview build).
Prerequisites:
- A hiapi API key — create one in the dashboard. Keys look like
sk-...and go in theAuthorization: Bearerheader. - A publicly reachable source image URL (JPEG/PNG). The API fetches the image server-side, so
localhostlinks or files behind auth won't work. - Any HTTP client. Examples below use
curland Python'srequests.
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.
Minimal working example: curl
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.
Minimal working example: Python
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")
Input reference (what the schema actually accepts)
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:
- There is no
seed,negative_prompt,fps,motion_strength, orcamera_fixedfield on this model. The schema rejects unknown properties (additional properties ... not allowed), so steer motion and camera behavior entirely throughprompt. - Only one reference image per task. Passing two returns
image_urls: maxItems: got 2, want 1. aspect_ratio: "auto"follows the source image's own proportions — usually what you want for product shots.- The older grok-imagine/image-to-video accepts up to 30 s per clip; the 1.5 preview caps at 15 s. Pricing for both is per second of output — see pricing.
Production notes
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:
401with{"error": {"code": "permission_denied", "type": "hiapi_error", ...}}— bad key, or the key lacks access to this model. The response includes arequest_idfor support.400witherror_code: "INVALID_REQUEST"— schema violation; themessagenames the exact field and rule (e.g.duration: maximum: got 99, want 15). Fix the payload, don't retry blindly.402insufficient balance — video models pre-check your quota at submit time. Top up and resubmit.status: "fail"on the task itself — generation-side failure; inspectdata.errorand 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.
Related reading
- grok-imagine-1.5/image-to-video@preview model page — pricing, parameters, and an in-browser playground
- How to use grok-imagine/image-to-video — the non-preview predecessor, same task flow, 30 s cap
- Grok Imagine text-to-video guide — start from a prompt instead of an image
- hiapi API reference — full task API contract
FAQ
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.



