Calling Kling from the command line: create, poll, fetch
Updated 2026-10-02
Before writing any client code, it is worth driving the Kling API by hand once. Three curl commands and a bit of jq show you the whole contract: how a job is created, how it moves through its states, where the finished URL lives, and what an error looks like. Everything below uses VideoRouter's single POST /videos endpoint with Kling v3.0 model ids. If you prefer Python, the Kling v3 Python tutorial covers the same flow.
Step 0: set up your key
Create an account, generate a key, and export it so it never appears in your shell history as a literal:
export LLMR_API_KEY="llmr_sk_live_..."
export BASE="https://videorouter.sh/api/v1"
Keys can carry a monthly spend cap and a model allow-list. For experiments, create a key with a small cap. Sign up if you do not have one yet.
Step 1: create the job
curl -s "$BASE/videos" \
-H "Authorization: Bearer $LLMR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "kling-v3.0-std",
"prompt": "a red kite rising over a windy beach, slow upward camera tilt",
"duration_secs": 5,
"aspect_ratio": "16:9"
}'
The response is a job, not a video: an id and a status of queued. The charge is taken here, at creation, based on the duration you requested, so ask for the length you intend to use. Which duration values the model accepts is a per-model fact; check the model page in the models catalog.
Capture the id in a variable so you can reuse it:
ID=$(curl -s "$BASE/videos" \
-H "Authorization: Bearer $LLMR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"kling-v3.0-std","prompt":"a red kite rising over a windy beach","duration_secs":5}' \
| jq -r .id)
echo "$ID"
Step 2: poll until it finishes
curl -s "$BASE/videos/$ID" -H "Authorization: Bearer $LLMR_API_KEY" | jq .status
The status moves queued, in_progress, then completed or failed. Polling is free, so a loop is fine:
while true; do
RESP=$(curl -s "$BASE/videos/$ID" -H "Authorization: Bearer $LLMR_API_KEY")
STATUS=$(echo "$RESP" | jq -r .status)
echo "status: $STATUS"
[ "$STATUS" = "completed" ] || [ "$STATUS" = "failed" ] && break
sleep 5
done
In a real script, add a deadline so a stuck job cannot hold the terminal forever. A counter that exits after a few hundred iterations is enough for manual testing.
Step 3: extract the video URL
echo "$RESP" | jq -r 'if .status == "completed" then .data[0].url else .error end'
On success you get a URL; on failure the error field. Download it right away rather than keeping the link:
URL=$(echo "$RESP" | jq -r '.data[0].url')
curl -L -o kite.mp4 "$URL"
URLs from upstream hosts are delivery links, not permanent storage, so copy the file into your own bucket if you need it later.
Step 4: switch tier or pin a host
Kling v3.0 tiers are separate model ids, not a quality flag. Use kling-v3.0-std, kling-v3.0-pro or kling-v3.0-4k in the same request. The Std vs Pro vs 4K guide explains how to choose.
By default the request goes to the cheapest healthy host and falls back to others if one rejects it. To prefer a host, suffix it to the model id:
-d '{"model":"kling-v3.0-std/novita","prompt":"...","duration_secs":5}'
That is a preference, not a guarantee: if the named host errors, the platform may still try other hosts serving the same checkpoint. For a strict pin, add a provider object:
-d '{
"model": "kling-v3.0-std",
"prompt": "...",
"duration_secs": 5,
"provider": { "only": ["novita"], "allow_fallbacks": false }
}'
With allow_fallbacks set to false, a rejection comes straight back as an error with no retry. Check the model page for the hosts that currently serve each tier, since hosts change.
Step 5: read the errors
Failures at creation use an OpenAI-style envelope. Print the body and the status together while testing:
curl -s -w "\nHTTP %{http_code}\n" "$BASE/videos" \
-H "Authorization: Bearer $LLMR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"not-a-real-model","prompt":"test"}'
| Status | Meaning | What to do |
|---|---|---|
| 400 | Unknown model or fields the model rejects | Fix the request. Do not retry. |
| 401 | Missing, revoked or expired key | Check $LLMR_API_KEY. |
| 402 | Spend cap reached or balance empty; the code field tells you which | Raise the cap or top up. |
| 403 | Model not on this key's allow-list | Adjust the key policy. |
| 429 | Rate limit | Wait for the Retry-After header, then retry. |
| 5xx | Every candidate host failed | Retry with backoff; documented as not billed. |
A job can also be accepted and later end with status failed. In that case the polling response carries the error field. A job that fails because every upstream host failed is not billed.
Two curl gotchas
- Quoting. Single-quote the JSON body. If you need variables inside it, build the body with
jq -n --arg p "$PROMPT" '{model:"kling-v3.0-std", prompt:$p, duration_secs:5}'instead of splicing strings, so quotes in a prompt do not break the JSON. - Silent ignores. VideoRouter's documentation says an unsupported
resolutionandaspect_ratiocombination is ignored rather than rejected, so a 200 does not prove you got the size you asked for. Check the downloaded file's dimensions withffprobe.
Where to go next
Once the loop works by hand, move to code with the Python tutorial, add a start frame with the image-to-video guide, or look at the quickstart for the shared basics. See pricing for live per-second rates before you batch anything.
Frequently asked questions
How do I call the Kling API with curl?
POST a JSON body with model (for example kling-v3.0-std), prompt and duration_secs to https://videorouter.sh/api/v1/videos with a Bearer key, then GET /videos/{id} until the status is completed and read data[0].url.
How do I get the video URL from the response?
When status is completed, the URL is in data[0].url. With jq: jq -r '.data[0].url'. Download it promptly because it is a delivery link.
How do I pin Kling to a specific host?
Suffix the host to the model id for a soft preference, or use provider.only with allow_fallbacks set to false for a hard pin with no retries.
Does polling cost anything?
No. Polling is free; you are billed once at creation based on the requested duration.
Keep reading
- Kling v3.0 Std vs Pro vs 4K — Which Tier Should You Use?
- Kling Image-to-Video API: Animate a Start Image (Code)
- Kling Motion Control API: Reference Video + Character Image
- Kling O3 (Omni) vs Kling v3: Which Model Should You Call?
VideoRouter puts it next to dozens of other video and image models behind one API key, so you can compare providers, prices and fail over automatically. Compare providers on VideoRouter →