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.
What the model gives you
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:
- Output: H.264 MP4, up to 1080P, with a native AAC audio track — the model generates ambient sound and effects matching your prompt, no separate audio step.
- Duration: 2–15 seconds per clip (integer seconds).
- Aspect ratios:
16:9,9:16,1:1,4:3,3:4— so true vertical 9:16 for Shorts/Reels/TikTok-style formats without cropping. - Pricing: billed per second of output video, from $0.167/second at the time of writing — a 5-second 1080P clip is about $0.84. Always confirm on the pricing page before batching.
There is no synchronous mode for video: you create a task, poll until it finishes, then download the result.
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": "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 full input schema (verified against the live API)
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:
- The field is
ratio, notaspect_ratio— image models on hiapi useaspect_ratio, this video model does not. Sendingaspect_ratioreturnsadditional properties 'aspect_ratio' not allowed. durationis validated server-side:duration: minimum: got 1, want 2andmaximum: got 999, want 15are the exact errors you get outside the 2–15 range.
End-to-end Python: prompt in, publish-ready MP4 out
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.
Prompting for short-form: motion and audio live in the prompt
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:
- Name the camera behavior explicitly: "handheld", "slow push-in", "macro", "shallow depth of field". The night-market clip's documentary wobble came entirely from the word "handheld".
- Describe the audio you want: the model generates sound from the prompt. "sizzling and crackling sounds" and "gentle ambient hum" both landed in the output audio tracks.
- Front-load the hook: for short-form, the first second is everything. Put the most kinetic element (the flame toss, the ink drop hitting water) at the start of the prompt description.
- Use
negative_promptdefensively: "text overlay, watermark, distorted hands" cleans up the most common artifacts without touching the main prompt. - Pin
seedwhen iterating: change one prompt phrase at a time with a fixed seed to see what that phrase actually does.
Need first-frame control? Use the image-to-video variant
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.
Common errors and what they actually mean
invalid input: prompt: missing required field "prompt"—promptis 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"withTASK_FAILED— the task itself failed after creation; create a new task (reuse yourseedto stay reproducible).- Downloaded file is tiny or won't play — you likely fetched an expired output URL. Re-check
expireAtand download immediately aftersuccess.
Wrap-up
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.



