Search

Search HiAPI models, tools, and resources.

Back to blog
Guide

Kling 3.0 Text-to-Video: Short-Form Clips With Native Audio

hiapiKling 3.0Short-Form VideoText-to-VideoNative Audio

Most "text-to-video" demos stop at a silent clip. kling-3.0/text-to-video on hiapi doesn't — it renders native, lip-synced audio directly into the generation, and it's the only Kling 3.0 tier that goes up to 4K. For short-form content (Reels, TikTok, Shorts), that combination — vertical framing, real ambient sound, no separate TTS/dubbing pass — removes a whole post-production step.

This walks through the actual workflow: a single clip via the /v1/tasks API, then scaling it to a batch of clips with async code, plus the pricing tiers you need to budget against.

What kling-3.0/text-to-video adds over the turbo tier

hiapi runs two Kling 3.0 video families side by side: kling-3.0-turbo/text-to-video and the bare kling-3.0/text-to-video covered here. The turbo tier is faster and cheaper per second, capped at 1080p, with no audio. The bare tier trades some of that speed for:

  • Native synced audio — a sound boolean on the request. When true, hiapi.ai/renders speech and ambient tone that matches the on-screen action, not a bolted-on soundtrack.
  • A 4K ceiling — resolution accepts 720p, 1080p, or 4K, versus turbo's 1080p cap.
  • The same duration range — 3-15 seconds either way, which is exactly the window short-form platforms reward.

If you only need a silent background loop, turbo is the cheaper call. If the clip needs to sound like someone talking to camera — which is most short-form content — this is the tier that does it in one request.

Generating a single clip

The model lives on hiapi's async /v1/tasks endpoint: submit a task, poll for completion, then pull the output URL (it expires, so download immediately).

import os
import time
import requests

API_BASE = "https://api.hiapi.ai/v1/tasks"
TOKEN = os.environ["HIAPI_API_KEY"]
HEADERS = {"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"}


def create_clip(prompt: str, duration: int, resolution: str, aspect_ratio: str, sound: bool) -> str:
    body = {
        "model": "kling-3.0/text-to-video",
        "input": {
            "prompt": prompt,
            "duration": duration,
            "resolution": resolution,
            "aspect_ratio": aspect_ratio,
            "sound": sound,
        },
    }
    resp = requests.post(API_BASE, headers=HEADERS, json=body, timeout=30)
    resp.raise_for_status()
    return resp.json()["data"]["taskId"]


def wait_for_clip(task_id: str, poll_every: int = 5, timeout: int = 600) -> str:
    deadline = time.time() + timeout
    while time.time() < deadline:
        resp = requests.get(f"{API_BASE}/{task_id}", headers=HEADERS, timeout=30)
        resp.raise_for_status()
        data = resp.json()["data"]
        if data["status"] == "success":
            return data["output"][0]["url"]
        if data["status"] == "fail":
            raise RuntimeError(data.get("error"))
        time.sleep(poll_every)
    raise TimeoutError(f"task {task_id} did not finish in {timeout}s")


task_id = create_clip(
    prompt="Vertical 9:16 short-form video clip: a creator filming a quick "
           "product-review style take with a smartphone, energetic hand gestures, "
           "natural indoor lighting, casual talking-to-camera framing, realistic "
           "motion and lip movement, ambient room tone and speech audio synced to "
           "the action",
    duration=6,
    resolution="1080p",
    aspect_ratio="9:16",
    sound=True,
)
video_url = wait_for_clip(task_id)

That's the exact prompt and settings behind the clip below — vertical, 6 seconds, 1080p, audio on.

Prompt: "Vertical 9:16 short-form video clip: a creator filming a quick product-review style take with a smartphone, energetic hand gestures, natural indoor lighting, casual talking-to-camera framing, realistic motion and lip movement, ambient room tone and speech audio synced to the action"

Scaling to a batch: async code, not a sequential loop

A single clip is one request. A week of short-form content is ten or twenty. Polling each task one at a time in a for loop works, but it serializes wait time that doesn't need to be serial — the API happily accepts concurrent submissions. Fire them off together with asyncio and aiohttp instead:

import asyncio
import os
import aiohttp

API_BASE = "https://api.hiapi.ai/v1/tasks"
TOKEN = os.environ["HIAPI_API_KEY"]
HEADERS = {"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"}


async def create_task(session, prompt, duration=6, resolution="1080p",
                       aspect_ratio="9:16", sound=True):
    body = {
        "model": "kling-3.0/text-to-video",
        "input": {"prompt": prompt, "duration": duration, "resolution": resolution,
                   "aspect_ratio": aspect_ratio, "sound": sound},
    }
    async with session.post(API_BASE, json=body) as resp:
        resp.raise_for_status()
        data = await resp.json()
        return data["data"]["taskId"]


async def poll_task(session, task_id, poll_every=5, timeout=600):
    loop = asyncio.get_event_loop()
    deadline = loop.time() + timeout
    while loop.time() < deadline:
        async with session.get(f"{API_BASE}/{task_id}") as resp:
            resp.raise_for_status()
            data = (await resp.json())["data"]
        if data["status"] == "success":
            return data["output"][0]["url"]
        if data["status"] == "fail":
            raise RuntimeError(data.get("error"))
        await asyncio.sleep(poll_every)
    raise TimeoutError(f"task {task_id} timed out")


async def produce_batch(prompts: list[str]) -> list[str]:
    async with aiohttp.ClientSession(headers=HEADERS) as session:
        task_ids = await asyncio.gather(*(create_task(session, p) for p in prompts))
        return await asyncio.gather(*(poll_task(session, tid) for tid in task_ids))


prompts = [
    "Vertical 9:16 clip: creator unboxing a package on camera, excited reaction, "
    "soft window light, speech synced to lip movement",
    "Vertical 9:16 clip: creator walking through a city street talking to camera, "
    "b-roll style framing, ambient street audio",
    "Vertical 9:16 clip: creator demoing a skincare routine close-up, calm "
    "narration audio, natural bathroom lighting",
]
urls = asyncio.run(produce_batch(prompts))

Each task runs independently server-side, so the wall-clock time for three clips is close to the time for one, not three times as long. For bigger batches, cap concurrency with an asyncio.Semaphore so you don't flood your own rate limit.

What it costs

Pricing on kling-3.0/text-to-video is tiered by resolution and the sound toggle, not a single flat rate — don't carry over the turbo tier's flat per-second price, the two routes are priced separately:

  • 720p, no audio: $0.10/s
  • 720p + sound:true: $0.143/s
  • 1080p, no audio: $0.129/s
  • 1080p + sound:true: $0.193/s
  • 4K: $0.479/s

For a 6-second 1080p clip with audio, that's roughly $1.16. A 10-clip batch at the same settings lands around $11.60 — worth checking against your own pricing dashboard before you queue a large run, since every field (resolution, duration, audio) moves the total independently. See the full breakdown on the Kling 3.0 pricing page if you're comparing against other Kling tiers.

Short-form production notes

A few things that actually move retention, beyond just "turn audio on":

  • Keep duration at 6-10s. The model supports up to 15s, but most short-form hooks land or lose the viewer in the first few seconds — a tighter clip forces a tighter prompt.
  • Write the audio into the prompt, don't assume it. "Speech audio synced to the action" or "ambient street noise" in the prompt text steers what sound:true actually renders — a prompt with no audio cues can still come back oddly quiet.
  • aspect_ratio: "9:16" end to end. Don't generate 16:9 and crop; the model composes the shot differently for vertical framing (hand position, headroom, background depth) when you ask for it directly.
  • Batch variations of the same hook, not unrelated clips. Async generation makes it cheap to try 3-4 phrasing variants of one hook concept and pick the best take, instead of committing to a single prompt per idea.

FAQ

Does kling-3.0/text-to-video support 9:16 natively, or do I need to crop a 16:9 render? It's native — aspect_ratio accepts 9:16 directly on the request, and the model frames the shot for vertical from the start.

Is the audio real dialogue/ambience, or a generic background track? It's generated to match the scene: speech syncs to lip movement when the prompt describes someone talking, and ambient tone (room, street, etc.) follows the setting described in the prompt. It's not a stock music bed.

How is this different from kling-3.0-turbo for short-form use? Turbo is cheaper and faster but caps at 1080p with no audio field at all. Use turbo for silent b-roll loops; use this tier when the clip needs to sound like something, especially talking-to-camera content.

Can I generate a week's worth of clips without waiting on each one sequentially? Yes — submit all the create-task requests concurrently (see the async batch sample above), then poll them concurrently too. The API doesn't require one-at-a-time submission.

What happens if I don't download the output URL right away? The output URL is time-limited (expireAt). Download the bytes as soon as the task reports success and store them yourself — don't link the hot URL anywhere persistent.

Full field reference and live pricing are on the kling-3.0/text-to-video model page.

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