Search

Search HiAPI models, tools, and resources.

Back to blog
Guide6 min read

seedance-2-0 for Short-Form Video: A hiapi API Recipe

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.

HiAPI Teamseedance-2-0Video APIShort-FormGuide

TL;DR

  • What this is: a working, reproducible recipe for making short-form vertical video (TikTok / Reels / Shorts) with seedance-2.0 on hiapi. The clip embedded below was generated by the exact prompt printed right above it — no borrowed demos.
  • Two modes, one model: 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.
  • API facts (verified on the platform): 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.
  • Pricing (per second of output): 480p is $0.136/s, 720p is $0.293/s, 1080p is $0.729/s. A 4-second 480p clip costs $0.54. See pricing for live numbers.
  • How to run it: POST to the task API, poll until 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.

Why seedance-2.0 for short-form

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.

Mode 1 — text-to-video

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 is estimated at $0.54 at current 480p rates — 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)

Mode 2 — image-to-video

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.

Cost math for a short-form batch

Because billing is per second of output, your cost is trivially predictable — pick the resolution, multiply by the clip length:

ResolutionPrice / second4s clip8s clip
480p$0.136$0.54$1.09
720p$0.293$1.17$2.34
1080p$0.729$2.92$5.83

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.

The full script — submit, poll, download

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.

Takeaways

  • 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.
  • Billing is per second of output: 480p $0.136/s, 720p $0.293/s, 1080p $0.729/s — so draft cheap at 480p and only finish the winner high-res.
  • The output URL is temporary — download the bytes as soon as the task reports success.
  • Same call does text-to-video and image-to-video; pair image-to-video with a generated still for on-brand product motion.

Generate it with HiAPI

Choose a model, enter your prompt, and see the result.

HiAPI Blog

Related articles

HiAPI

Generate it with HiAPI

Start generating