Guide
Kling 3.0 Text-to-Video: Short-Form Clips With Native Audio
- Max resolution
- 4K
- Clip duration
- 3-15s
- Native audio
- Synced speech + ambience
- 1080p + audio price
- $0.193/s
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, capped at 1080p, and always adds its own sound, with no switch to control it. The bare tier trades some of that speed for:
- Native synced audio — a
soundboolean on the request. Whentrue, hiapi.ai/renders speech and ambient tone that matches the on-screen action, not a bolted-on soundtrack. - A 4K ceiling —
resolutionaccepts720p,1080p, or4K, versus turbo's 1080p cap. - The same duration range —
3-15seconds either way, which is exactly the window short-form platforms reward.
If you need a silent background loop, use this tier with sound off: $0.10/s at 720p, below Turbo's $0.13/s. 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="720p",
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, 720p, audio on ($0.86).
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/s720p+sound:true: $0.143/s1080p, no audio: $0.129/s1080p+sound:true: $0.193/s4K: $0.479/s
For a 6-second 1080p clip with audio, that's roughly $1.16; the 720p clip above with audio came to $0.86. 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
durationat 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:trueactually 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 faster and caps at 1080p. It has no sound field, but its clips always come with sound. Use turbo for quick b-roll where its built-in sound is fine; use this tier when you need silent output, or when the clip needs to sound like something specific, 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.
Related articles
View all articles →-
Guide
Getting Started with seedream-5.0-pro/image-to-image: Capabilities, Pricing, and Integration Essentials
Capability boundaries, billing model, and engineering integration practices for ByteDance's image editing model
-
Guide
seedream-5.0-pro Text-to-Image Getting Started Guide: Capability Boundaries, API Integration, and Cost Control
A hands-on developer perspective: from key parameters to a cost estimate of $0.05 per image
-
Guide
Hailuo 2.3 Fast Image-to-Video Getting Started Guide: Capability Boundaries, Pricing, and Integration Practices
MiniMax image-to-video model selection and integration key points for developers