
Short-form video lives or dies on iteration speed: you need many candidate clips, in vertical format, with sound, fast. This guide walks through exactly that with wan2.7-video/text-to-video@pro on hiapi — Alibaba's Tongyi Wanxiang 2.7 text-to-video model, which outputs up to 1080P with native audio and clips up to 15 seconds. Every request shape, parameter bound, and error message below was run against the live API while writing this post, and both demo clips embedded here came out of the exact code shown.
wan2.7-video/text-to-video@pro is served through hiapi's unified async task endpoint (/v1/tasks). Key properties, verified against the live schema:
16:9, 9:16, 1:1, 4:3, 3:4 — so true vertical 9:16 for Shorts/Reels/TikTok-style formats without cropping.There is no synchronous mode for video: you create a task, poll until it finishes, then download the result.
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": "wan2.7-video/text-to-video@pro",
"input": {
"prompt": "Macro studio shot: a single drop of royal-blue ink falls into clear water and blooms in slow motion, tendrils unfurling into cloud shapes against a bright white seamless background, crisp high-key lighting, gentle ambient hum",
"ratio": "16:9",
"resolution": "1080P",
"duration": 4
}
}'
Response:
{"code": 200, "data": {"taskId": "tk-hiapi-01KWGGS29FDZ8NRJW49YRQZ65R"}, "message": "success"}
Poll for the result:
curl "https://api.hiapi.ai/v1/tasks/tk-hiapi-01KWGGS29FDZ8NRJW49YRQZ65R" \
-H "Authorization: Bearer sk-YOUR_KEY"
While rendering, data.status is handling; when done it flips to success and data.output[0].url holds the MP4. That exact request produced this clip:
⚠️ The output URL is a temporary link with an expireAt timestamp. Download the file to your own storage as soon as the task succeeds — do not hot-link it or store it in a database.
The API validates input strictly and rejects unknown fields, so here is the complete accepted field list — probed directly against the endpoint rather than copied from anywhere:
| Field | Type | Constraint | Required |
|---|---|---|---|
prompt | string | scene description; also drives the generated audio | ✅ |
ratio | string | one of 16:9, 9:16, 1:1, 4:3, 3:4 | optional |
resolution | string | 720P or 1080P | optional |
duration | integer | 2–15 (seconds) | optional |
negative_prompt | string | things to suppress (e.g. watermarks, text overlays) | optional |
seed | integer | reproducibility across retries | optional |
watermark | boolean | toggle provider watermark | optional |
Two gotchas that will save you a 400:
ratio, not aspect_ratio — image models on hiapi use aspect_ratio, this video model does not. Sending aspect_ratio returns additional properties 'aspect_ratio' not allowed.duration is validated server-side: duration: minimum: got 1, want 2 and maximum: got 999, want 15 are the exact errors you get outside the 2–15 range.This is the complete workflow — create, poll, download — with no SDK, just requests:
import time
import requests
API = "https://api.hiapi.ai/v1/tasks"
HEADERS = {"Authorization": "Bearer sk-YOUR_KEY", "Content-Type": "application/json"}
def make_short(prompt: str, out_path: str, duration: int = 5,
ratio: str = "9:16", seed: int | None = None) -> str:
payload = {
"model": "wan2.7-video/text-to-video@pro",
"input": {
"prompt": prompt,
"ratio": ratio, # 9:16 = vertical short-form
"resolution": "1080P",
"duration": duration, # 2..15 seconds
"negative_prompt": "text overlay, watermark, distorted hands",
},
}
if seed is not None:
payload["input"]["seed"] = seed
task_id = requests.post(API, headers=HEADERS, json=payload).json()["data"]["taskId"]
while True:
task = requests.get(f"{API}/{task_id}", headers=HEADERS).json()["data"]
if task["status"] == "success":
break
if task["status"] == "fail":
raise RuntimeError(f"task failed: {task.get('error')}")
time.sleep(10)
video_url = task["output"][0]["url"] # temporary link — download immediately
with open(out_path, "wb") as f:
f.write(requests.get(video_url, timeout=120).content)
return out_path
make_short(
"Handheld vertical night-market shot at golden hour: a street food vendor "
"tosses sizzling noodles in a flaming wok, steam and orange sparks swirl toward "
"the camera, neon signs glow soft pink and teal in the bokeh background, shallow "
"depth of field, energetic handheld documentary feel, sizzling and crackling sounds",
"night-market.mp4",
duration=5,
seed=20260702,
)
Here is the actual clip that call produced — vertical 1080×1920, 5 seconds, with the sizzling audio generated from the prompt text:
In my runs, a 4-second 16:9 clip finished in roughly 3 minutes and the 5-second 9:16 clip took slightly longer — budget a few minutes per clip and poll every ~10 seconds rather than hammering the endpoint.
The schema has no camera-control or motion parameters — everything about movement, pacing, and sound is steered through prompt language. What worked in my tests:
negative_prompt defensively: "text overlay, watermark, distorted hands" cleans up the most common artifacts without touching the main prompt.seed when iterating: change one prompt phrase at a time with a fixed seed to see what that phrase actually does.If your workflow starts from a designed keyframe — a product shot, a brand-styled first frame — text-to-video won't take an image input. The companion model wan2.7-video/image-to-video@pro does: it accepts a media array of {"url": "...", "type": "image"} objects alongside the same prompt / resolution / duration fields, animating your supplied frame instead of inventing one. Same task-based flow, same per-second billing. A practical pipeline: generate a still with an image model, review and approve it, then animate the approved frame — you spend video-generation dollars only on frames you've already signed off.
invalid input: prompt: missing required field "prompt" — prompt is the only required field.invalid input: ratio: value must be one of '16:9', '9:16', '1:1', '4:3', '3:4' — you passed a ratio the model doesn't support (or misspelled one).invalid input: <root>: additional properties '...' not allowed — unknown field; the schema is strict, check the table above.status: "fail" with TASK_FAILED — the task itself failed after creation; create a new task (reuse your seed to stay reproducible).expireAt and download immediately after success.One POST, one polling loop, one download — that's the whole distance from a text prompt to a publish-ready vertical MP4 with sound. If you're building a short-form pipeline, grab an API key from your dashboard, start with the Python function above, and browse the rest of the model catalog — the same /v1/tasks pattern (as in our seedance-2.0-fast guide) works across every video model on the platform, so swapping models later is a one-line change.