kling-3.0-omni/transformation is Kling 3.0 Omni's video-to-video model: you hand it one source clip plus a text prompt, and it rewrites the look of that clip — scene, style, subject — while keeping the original motion and timing. On hiapi you call it through the same unified async endpoint as every other generation model: POST /v1/tasks. This guide gives you the live-verified parameter table, a copy-paste curl request, a complete Python client that polls and downloads the result, and the production patterns (callbacks, idempotency, error handling) you need before wiring it into a real app.
Every field name, enum value, and response shape below was checked against the live API.
What you'll build + prerequisites
A tiny program that submits a restyle task, waits for it to finish, and downloads the restyled .mp4. You need two things:
- An hiapi API key. Create one in your hiapi dashboard — it looks like
sk-.... Every request usesAuthorization: Bearer sk-<your-key>. - One publicly reachable source video URL. The model downloads your clip server-side, so it must be a public
http(s)URL (your own CDN, R2/S3 with a public object, etc.), not a local file path. Exactly one URL is allowed.
The model and its parameters
The request envelope is {"model": ..., "input": {...}}. The input schema is strict — only these three fields are accepted, and anything extra is rejected with a 400.
| Field | Type | Required | Notes |
|---|---|---|---|
prompt | string | yes | Describes the target style/scene/subject. This is also where you dial restyle strength — there is no numeric strength/fidelity param. |
video_urls | array | yes | Exactly one public source video URL (minItems 1, maxItems 1). |
resolution | string enum | yes | One of 720p, 1080p, 4k. |
A few things that trip people up:
- There is no
durationparameter. Output length and pacing follow your source clip's motion — the model restyles what's there, it doesn't re-time it. - The model id carries
-omniin the id itself:kling-3.0-omni/transformation(some other Omni endpoints usekling-3.0/...). Use the bare id as themodelvalue. - Restyle strength is prompt-driven. Say "subtle repaint, keep facial features and lighting" for a light touch, or "fully re-imagine as a 2D cel-shaded cartoon" for a heavy one.
Minimal working example (curl)
# 1) Create the restyle task
curl -s https://api.hiapi.ai/v1/tasks \
-H "Authorization: Bearer sk-<your-key>" \
-H "Content-Type: application/json" \
-d '{
"model": "kling-3.0-omni/transformation",
"input": {
"prompt": "Restyle the scene as a hand-drawn watercolor animation with soft paper texture and warm dusk light; keep the original camera motion and subject.",
"video_urls": ["https://your-cdn.example.com/source-clip.mp4"],
"resolution": "720p"
}
}'
# -> {"code":200,"data":{"taskId":"tk-hiapi-01..."},"message":"success"}
# 2) Poll the task until it reaches a terminal state
curl -s https://api.hiapi.ai/v1/tasks/tk-hiapi-01... \
-H "Authorization: Bearer sk-<your-key>"
# data.status progresses: queued -> handling -> archiving -> success
# on success, data.output[0].url is the restyled .mp4
The task id you poll is data.taskId from the create response — not a top-level id. The status strings are queued / handling / archiving / success (terminal) or fail; don't assume succeeded/completed.
Complete Python client (create → poll → download)
import os
import time
import requests
API_KEY = os.environ["HIAPI_KEY"] # your sk-... key
BASE = "https://api.hiapi.ai"
HEADERS = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
}
def create_restyle(source_video_url: str, prompt: str, resolution: str = "720p") -> str:
body = {
"model": "kling-3.0-omni/transformation",
"input": {
"prompt": prompt,
"video_urls": [source_video_url], # exactly one PUBLIC url
"resolution": resolution, # "720p" | "1080p" | "4k"
},
}
r = requests.post(f"{BASE}/v1/tasks", json=body, headers=HEADERS, timeout=30)
r.raise_for_status()
data = r.json()
if data.get("code") != 200:
raise RuntimeError(f"submit rejected: {data}")
return data["data"]["taskId"]
def wait_for_video(task_id: str, poll_s: int = 10, timeout_s: int = 900) -> str:
deadline = time.time() + timeout_s
while time.time() < deadline:
r = requests.get(f"{BASE}/v1/tasks/{task_id}", headers=HEADERS, timeout=30)
r.raise_for_status()
data = r.json()["data"]
status = data["status"]
if status == "success":
return data["output"][0]["url"]
if status == "fail":
raise RuntimeError(f"task failed: {data.get('error')}")
time.sleep(poll_s) # queued / handling / archiving
raise TimeoutError(f"{task_id} did not finish within {timeout_s}s")
if __name__ == "__main__":
task_id = create_restyle(
source_video_url="https://your-cdn.example.com/source-clip.mp4",
prompt=(
"Restyle the scene as a hand-drawn watercolor animation, soft paper "
"texture, warm dusk light; keep the original camera motion and subject."
),
resolution="720p",
)
print("task:", task_id)
url = wait_for_video(task_id)
print("video url:", url)
# Output lives on temp.hiapi.ai and expires (~7 days). Download it now.
clip = requests.get(url, timeout=120).content
with open("restyled.mp4", "wb") as f:
f.write(clip)
print("saved restyled.mp4", len(clip), "bytes")
Run it with your key in the environment:
export HIAPI_KEY=sk-<your-key>
python restyle.py
Production patterns
Use a callback instead of tight polling
For anything beyond a script, register a webhook so you don't hold a polling loop open. callback is a top-level field — a sibling of model and input, never inside input:
{
"model": "kling-3.0-omni/transformation",
"input": {
"prompt": "Restyle as 2D cel-shaded cartoon, bold outlines; keep original motion.",
"video_urls": ["https://your-cdn.example.com/source-clip.mp4"],
"resolution": "1080p"
},
"callback": { "url": "https://your-app.example.com/hooks/hiapi", "when": "final" }
}
callback.whenonly acceptsfinal(the default). It fires once on the terminal state, and both success and fail trigger it.- The delivered POST body is identical to the
dataobject you'd get fromGET /v1/tasks/<id>— so your handler can readstatus,output[].url, anderrorwithout a re-fetch. - Putting
callbackinsideinputfails at submit withadditional properties 'callback' not allowed(the per-model input schema is strict).
Idempotency and delivery semantics
Callbacks are at least once, not exactly once — the same taskId can arrive more than once. Key your handler on taskId and make the "mark done + store the video" step idempotent. hiapi retries a non-2xx/timeout callback on a schedule of immediately → +1 min → +5 min → +20 min, up to 4 attempts; your endpoint must return HTTP 2xx to stop the retries.
Download outputs immediately
data.output[0].url points at temp.hiapi.ai with an expireAt (unix timestamp, roughly a 7-day TTL). After expiry the object is purged and the URL returns a 404 — re-polling the task returns the same URL but does not regenerate the file. Copy the bytes to your own storage as soon as the task succeeds.
Error handling
Three failure shapes you should branch on:
- Bad request (HTTP 400) — flat envelope, no task created:
{"code":400,"data":null,"error_code":"INVALID_REQUEST","message":"invalid input: resolution: value must be one of '720p', '1080p', '4k'"} - Auth failure (HTTP 401) — a nested envelope:
{"error":{"code":"permission_denied","type":"hiapi_error", ...}} - Failed task — the submit returns
200+ ataskId, but a later poll showsdata.status == "fail"with adata.error. Failed tasks are not charged (the reserved amount is refunded on terminal fail). An unreachable or non-downloadable source URL shows up here.
Cost
kling-3.0-omni/transformation is billed per second of output, and the rate scales with the resolution you request (4k costs more than 720p). See the live hiapi pricing page for the current per-second rate before you batch-run clips — video tasks add up far faster than image ones.
Related reading
- Model page and spec: kling-3.0-omni/transformation on hiapi
- Endpoint reference: Create Task — unified async API
- A sibling recipe on the same endpoint: Kling AI Image-to-Video API in Python
FAQ
How do I control how strongly it restyles the clip?
Through the prompt, not a parameter. There is no strength, fidelity, or creativity field. Phrase a light edit as "subtle repaint, preserve faces and lighting" and a heavy edit as "fully re-imagine as <style>". Passing a numeric strength field returns a 400 additional properties not allowed.
What resolutions are supported?
720p, 1080p, or 4k. Any other value (including 2k or 1440p) is rejected with value must be one of '720p', '1080p', '4k'.
Can I send more than one source video?
No. video_urls takes exactly one URL. For multi-clip or reference-image workflows you'd use a different Kling endpoint (e.g. reference-to-video), not transformation.
How long will the output be?
The model keeps the source clip's motion and timing, so the output tracks the length of the clip you upload — there's no duration control on this endpoint.
Does it keep the original camera movement and action? Yes — that's the point of video-to-video: motion is preserved, only the visual style/scene is rewritten. Reinforce it in the prompt ("keep the original camera motion and subject") for the most faithful motion.
Should I poll or use a callback?
Poll for quick scripts and one-offs; use a callback with when: "final" for production so you're not holding a loop open. Polling is always available as the ground-truth fallback even when you also register a callback.
I can't find this model in /v1/models — is it real?
Yes. kling-3.0-omni/transformation is callable on /v1/tasks even though it may not appear in the /v1/models listing yet; a submit returns normal schema validation rather than MODEL_UNAVAILABLE. Use the model id exactly as written.
Why am I getting additional properties ... not allowed?
The input schema accepts only prompt, video_urls, and resolution. Remove any extra fields (duration, strength, etc.), and make sure callback is at the top level of the body, not inside input.


