wan3.0-video runs through hiapi's shared async task endpoint, the same POST /v1/tasks / GET /v1/tasks/{taskId} pair used by every image and video model on the platform. This guide is a minimal, verified path from an API key to a downloadable .mp4: a working curl request, a poll loop, a Python version, the full input schema (with real error responses), and the production details you need before you wire this into a pipeline.
What you need
- A hiapi account and an API key from the dashboard.
- Nothing model-specific to install —
wan3.0-videouses the same unified task API as every other model. - Just a text prompt. hiapi's pricing table files
wan3.0-videounder "reference-to-video," but that's a billing category, not a requirement — a plainpromptwith no reference image or video works standalone. Reference media (reference_image_urls/reference_video_urls, covered below) is optional, for when you want to steer the output from an existing image or clip.
Minimal working example
1. Create the task
curl -s https://api.hiapi.ai/v1/tasks \
-H "Authorization: Bearer sk-<your-api-key>" \
-H "Content-Type: application/json" \
-d '{
"model": "wan3.0-video",
"input": {
"prompt": "A steaming cup of coffee on a wooden table, soft morning light, gentle steam rising",
"duration": 2,
"resolution": "480P"
}
}'
You get a taskId back immediately; generation keeps running in the background:
{
"code": 200,
"message": "success",
"data": {
"taskId": "tk-hiapi-01M1QP6MVQ4DKWEKH6Q65RG5SQ"
}
}
2. Poll for the result
curl -s https://api.hiapi.ai/v1/tasks/tk-hiapi-01M1QP6MVQ4DKWEKH6Q65RG5SQ \
-H "Authorization: Bearer sk-<your-api-key>"
Keep polling every few seconds until data.status is success or fail. Here's the actual terminal response from the request above — wan3.0-video moves through handling, briefly archiving, then success; this particular 2-second 480P clip took about 4.5 minutes end to end:
{
"code": 200,
"message": "success",
"data": {
"taskId": "tk-hiapi-01M1QP6MVQ4DKWEKH6Q65RG5SQ",
"status": "success",
"model": "wan3.0-video",
"storage": "temp",
"created": 1788575175,
"completed": 1788575455,
"output": [
{
"type": "video",
"url": "https://temp.hiapi.ai/7c6ttvrbpt/01M1QP6MVQ4DKWEKH6Q65RG5SQ-0.mp4",
"artifactId": "116211",
"expireAt": 1789180255
}
]
}
}
data.output[0].url is your video. With the default storage: "temp", that URL expires roughly 7 days after creation (expireAt is a Unix timestamp) — download it or set "storage": "persistent" on creation if you need it to stick around longer.
3. Python version
import time
import requests
API_KEY = "sk-<your-api-key>"
BASE = "https://api.hiapi.ai/v1"
headers = {"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"}
resp = requests.post(f"{BASE}/tasks", headers=headers, json={
"model": "wan3.0-video",
"input": {
"prompt": "A steaming cup of coffee on a wooden table, soft morning light, gentle steam rising",
"duration": 2,
"resolution": "480P",
},
})
task_id = resp.json()["data"]["taskId"]
while True:
detail = requests.get(f"{BASE}/tasks/{task_id}", headers=headers).json()["data"]
if detail["status"] in ("success", "fail"):
break
time.sleep(5)
if detail["status"] == "success":
print(detail["output"][0]["url"])
else:
print("generation failed:", detail)
Full input schema
| Field | Type | Required | Notes |
|---|---|---|---|
prompt | string | yes | Text description of the video. |
duration | integer | no | Seconds, 2–30. |
aspect_ratio | enum | no | adaptive, 16:9, 4:3, 1:1, 3:4, 9:16. |
resolution | enum | no | 480P, 720P, 1080P. |
audio | boolean | no | Generate synchronized audio alongside the video. |
reference_image_urls | array of URL strings | no | Switches to image-to-video / reference-image mode. |
reference_video_urls | array of URL strings | no | Switches to video-to-video / reference mode. |
A field named image_urls does not exist on this model — if you're porting code from another hiapi video model, double-check the field name; wan3.0-video rejects it as an unknown property (see below).
Three real 400 responses from probing this schema directly, so you can recognize them if you hit them yourself:
Missing prompt:
{"code":400,"data":null,"error_code":"INVALID_REQUEST","message":"invalid input: prompt: missing required field \"prompt\""}
Invalid resolution value:
{"code":400,"data":null,"error_code":"INVALID_REQUEST","message":"invalid input: resolution: value must be one of '480P', '720P', '1080P'"}
Unknown field (e.g. a negative_prompt this model doesn't accept):
{"code":400,"data":null,"error_code":"INVALID_REQUEST","message":"invalid input: <root>: additional properties 'negative_prompt' not allowed"}
Pricing is per second of output and scales with resolution (higher resolution costs more per second); check current rates on pricing before estimating a batch run.
Production notes
- Callbacks instead of polling. Add a top-level
callbackobject to skip the poll loop entirely:"callback": {"url": "https://your-app.com/hiapi/callback", "when": "final"}. hiapi POSTs to that URL once the task reachessuccessorfail—whencurrently only supportsfinal. Don't putcallbackinsideinput; it's rejected there. - Idempotency. Pass an
Idempotency-Keyheader (up to 255 bytes) on creation. A retried request with the same key under the same account returns the originaltaskIdinstead of creating a second (and separately billed) task — useful when a client-side timeout makes you unsure whether your first request actually landed. - Storage and expiry. Outputs default to
"storage": "temp"(~7 days). Set"storage": "persistent"on creation, or promote an existing output afterward, if you need the file to outlive that window — see the async task docs for both options. - Error handling. A bad or missing API key fails synchronously with
401before any task is created:
Malformed input fails synchronously with{"error":{"code":"permission_denied","message":"This API key is invalid. Check that it is correct or use another API key and try again. If the issue persists, contact support with request ID: ...","type":"hiapi_error","request_id":"..."}}400(examples above); insufficient balance fails with402. Once a task ishandling, failures show up asstatus: "fail"on the polled/callback response rather than as an HTTP error — always checkdata.status, not just the HTTP code.
Related resources
- wan3.0-video model page — live parameter reference and pricing.
- Unified async task API docs — full
create/poll/callback/storage reference shared by every model. - Prompt recipes for Wan 2.7 text-to-video — prompt-writing patterns that carry over to
wan3.0-video. - Image-to-video API workflow — for building out the
reference_image_urlspath. - Pricing — current per-second rates by resolution.
FAQ
Does wan3.0-video need a reference image or video?
No. It's priced under a "reference-to-video" category, but a text-only prompt generates a video on its own. Reference media is optional.
What's the correct field for image-to-video with this model?
reference_image_urls, an array of publicly reachable image URLs. image_urls is not a valid field on wan3.0-video and will 400 with an "additional properties" error.
How long does generation take? Expect roughly a few minutes for a short clip at 480P; longer durations and higher resolutions take longer. Poll every few seconds, or use a callback to avoid polling entirely.
Can I get 9:16 output for short-form video?
Yes — set aspect_ratio to 9:16. The enum also covers 16:9, 4:3, 1:1, 3:4, and adaptive.
How do I keep the output file longer than 7 days?
Set "storage": "persistent" when you create the task, or promote the temp output afterward — see the storage section in the async task docs.
What happens if my API key is wrong?
The request fails immediately with HTTP 401 and error.code: "permission_denied" — no task is created and nothing is billed.









