Make vertical TikTok/Reels/Shorts clips with the seedance-2-0 task API on hiapi — text-to-video and image-to-video, verified pricing, and a copy-paste submit/poll/download script.

seedance-2-0 on hiapi. The clip embedded below was generated by the exact prompt printed right above it — no borrowed demos.seedance-2-0 does text-to-video and image-to-video from the same endpoint — start from a prompt, or animate a still you already have.seedance-2-0 runs on the async task endpoint (/v1/tasks). The input object needs prompt, aspect_ratio (required — use 9:16 for short-form), resolution (480p / 720p / 1080p), and duration in seconds. The result comes back as an MP4 at output[0].url.480p is $0.15/s, 720p is $0.33/s, 1080p is $0.823/s. A 4-second 480p clip costs $0.60. See pricing for live numbers.success, then download the MP4 from output[0].url — that URL is temporary and expires, so save the bytes immediately. Full copy-paste script (both modes + a batch loop) at the end.Short-form platforms have a fixed grammar: vertical 9:16, a few seconds long, one clear beat of motion. That is exactly the shape seedance-2-0 is built to hit. It renders cinematic motion with a single aspect-ratio flag, so you are not fighting the model to get a portrait frame — you just ask for 9:16 and it composes for that frame.
The other reason is workflow: it is a single async task per clip. You submit a job, poll a task id, and pull back a finished MP4. There is no streaming socket to babysit and no 100-second timeout to dodge — the same task protocol every other hiapi generation model uses, so if you already call the image models, the control flow here is identical.
This is the fastest path to a clip: describe the shot, pick a frame and a length, submit.
The prompt below is the one I actually ran. Write like a director — subject, lighting, camera move — not like a keyword list.
Prompt: A barista pulls a shot of espresso in a sunlit café, close-up on the crema swirling into the cup, warm morning light, gentle steam rising, shallow depth of field, smooth handheld camera push-in.
Settings:
aspect_ratio: 9:16,resolution: 480p,duration: 4
<video controls muted loop playsinline width="270" poster="https://static.hiapi.ai/blog/seedance-2-0-short-form-video-hiapi/cover.jpg" src="https://static.hiapi.ai/blog/seedance-2-0-short-form-video-hiapi/demo-espresso-9x16-480p.mp4"></video>
That clip is a native 9:16 frame, ready to drop straight into a Reels/Shorts timeline. It took roughly two minutes to render and cost $0.60 at 480p — cheap enough to generate a handful of variants and keep the best.
The request body is small. The one field people miss is aspect_ratio: it is required, and the API will reject the task with missing required field "aspect_ratio" if you leave it out.
import os, json, requests
TOKEN = os.environ["HIAPI_TOKEN"]
BASE = "https://api.hiapi.ai/v1/tasks"
HEADERS = {"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"}
payload = {
"model": "seedance-2-0",
"input": {
"prompt": (
"A barista pulls a shot of espresso in a sunlit cafe, close-up on the "
"crema swirling into the cup, warm morning light, gentle steam rising, "
"shallow depth of field, smooth handheld camera push-in."
),
"aspect_ratio": "9:16", # required — 9:16 for short-form
"resolution": "480p", # 480p | 720p | 1080p
"duration": 4, # seconds
},
}
r = requests.post(BASE, headers=HEADERS, data=json.dumps(payload), timeout=60)
task_id = r.json()["data"]["taskId"]
print("submitted:", task_id)
The second mode animates a still you supply instead of inventing the scene from scratch. That is the one you want for brand work: shoot (or generate) a clean product still, then let seedance-2-0 add the motion beat, so the framing and product stay on-model.
The shape is the same task call — you keep aspect_ratio, resolution, and duration, and your prompt now describes the motion you want applied to the frame ("slow push-in", "the model turns toward camera", "steam begins to rise") rather than the whole scene. Pair it with a still from one of the image models and you have a two-step pipeline: generate the hero frame, then animate it.
Tip: keep image-to-video clips short (4s) and let the motion be subtle. Short-form rewards one clean beat, not a busy camera.
Because billing is per second of output, your cost is trivially predictable — pick the resolution, multiply by the clip length:
| Resolution | Price / second | 4s clip | 8s clip |
|---|---|---|---|
| 480p | $0.15 | $0.60 | $1.20 |
| 720p | $0.33 | $1.32 | $2.64 |
| 1080p | $0.823 | $3.29 | $6.58 |
For iterating on a hook, draft at 480p — it is a quarter the price of 720p and plenty to judge composition and motion. Re-render only the winner at 720p or 1080p for the final post. See pricing for the current per-model rates.
This is the end-to-end version: it submits a text-to-video job, polls the task until it finishes, and saves the MP4. The download step matters — output[0].url is a temporary link with an expiry, so you write the bytes to disk the moment the task succeeds.
import os, json, time, requests
TOKEN = os.environ["HIAPI_TOKEN"]
BASE = "https://api.hiapi.ai/v1/tasks"
HEADERS = {"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"}
def submit(prompt, aspect_ratio="9:16", resolution="480p", duration=4):
payload = {
"model": "seedance-2-0",
"input": {
"prompt": prompt,
"aspect_ratio": aspect_ratio, # required
"resolution": resolution, # 480p | 720p | 1080p
"duration": duration, # seconds
},
}
r = requests.post(BASE, headers=HEADERS, data=json.dumps(payload), timeout=60)
r.raise_for_status()
return r.json()["data"]["taskId"]
def wait(task_id, timeout_s=600, poll=8):
deadline = time.time() + timeout_s
while time.time() < deadline:
r = requests.get(f"{BASE}/{task_id}", headers=HEADERS, timeout=30)
data = r.json().get("data", {})
status = data.get("status")
if status == "success":
return data
if status == "fail":
raise RuntimeError(f"task failed: {data.get('error')}")
time.sleep(poll)
raise TimeoutError(f"task {task_id} did not finish in {timeout_s}s")
def download(task, path):
out = task["output"][0]
assert out["type"] == "video", f"unexpected output type: {out['type']}"
# output[0].url is temporary (see expireAt) — save immediately.
resp = requests.get(out["url"], timeout=120)
resp.raise_for_status()
with open(path, "wb") as f:
f.write(resp.content)
return path
if __name__ == "__main__":
prompt = (
"A barista pulls a shot of espresso in a sunlit cafe, close-up on the crema "
"swirling into the cup, warm morning light, gentle steam rising, shallow "
"depth of field, smooth handheld camera push-in."
)
tid = submit(prompt, aspect_ratio="9:16", resolution="480p", duration=4)
print("submitted:", tid)
task = wait(tid)
print("saved:", download(task, "espresso_9x16.mp4"))
To turn one hook into a whole batch, wrap submit / wait / download in a loop over your prompts:
HOOKS = {
"espresso": "A barista pulls a shot of espresso in a sunlit cafe, close-up on the crema, gentle steam, handheld push-in.",
"matcha": "A whisk froths bright green matcha in a ceramic bowl, top-down light, slow spiral motion, calm morning mood.",
"pour-over": "Hot water spirals over fresh coffee grounds in a dripper, close macro, steam rising, warm backlight.",
}
for name, prompt in HOOKS.items():
tid = submit(prompt, aspect_ratio="9:16", resolution="480p", duration=4)
task = wait(tid)
download(task, f"{name}_9x16.mp4")
print("done:", name)
Draft the batch at 480p, pick the winner, re-render that one at 1080p, and post. That is the whole short-form loop on one model and one endpoint.
seedance-2-0 is a single async task on /v1/tasks that returns an MP4 — same control flow as the hiapi image models.aspect_ratio is required; use 9:16 for TikTok / Reels / Shorts.success.