Kling motion control API in Python: submit, poll, fetch

Call Kling 3.0 motion control from Python with requests: submit with an Idempotency-Key, poll the status URL until terminal, then fetch the result when ready.

4 min readSume
All posts

To call Kling motion control from Python, POST to /v1/kling/3.0/motion-control with an image, a driving video and duration_seconds, poll the returned status_url until terminal is true, then GET result_url once result_ready is true. The script below does exactly that with requests.

The submit-poll-fetch flow is described in Jobs and results, read 2026-09-29. The request fields come from the Sume OpenAPI reference.

What does the Python script look like?

Set SUME_API_KEY in your environment first. The Idempotency-Key header makes a retried submit return the original job instead of billing a second one.

import os, time, requests

BASE = "https://api.sume.com"
HEAD = {"Authorization": f"Bearer {os.environ['SUME_API_KEY']}"}

body = {
    "image_url": "https://example.com/character.png",
    "motion_video_url": "https://example.com/move-8s.mp4",
    "duration_seconds": 8,
}
r = requests.post(
    f"{BASE}/v1/kling/3.0/motion-control",
    headers={**HEAD, "Idempotency-Key": "mc-demo-001"},
    json=body,
)
r.raise_for_status()
job = r.json()["data"]

while True:
    s = requests.get(job["status_url"], headers=HEAD)
    s.raise_for_status()
    st = s.json()["data"]
    if st["terminal"]:
        break
    time.sleep(st.get("next_poll_after_seconds") or 5)

if st["result_ready"]:
    print(requests.get(st["result_url"], headers=HEAD).json())
else:
    print("ended as", st["sume_status"])

Why poll instead of waiting on the submit call?

The default mode is async: submit returns a 202 job envelope, not a video. Jobs that outlast 30 seconds are the reason the docs point to client-side polling, where the wait lives in your client rather than in an open HTTP request.

Poll on the booleans terminal and result_ready, or on sume_status, which is one of queued, processing, completed, failed or canceled. Honor next_poll_after_seconds when it is present.

What happens if I fetch the result too early?

GET /v1/jobs/{id}/result on a job that has not completed returns 409 job_not_completed. That is why the script checks result_ready first. A failed or canceled job ends terminal with no result; read the events URL for the reason.

How do I handle a failure?

A submit can fail before a job exists, for example with a 400, 401, 402, 409, 413 or 429 response; raise_for_status() surfaces those as exceptions in the script above. Check the error body rather than retrying blindly, and keep the same key only for retries of the same request.

If the submit itself times out, resend it with the same Idempotency-Key. The docs say this returns the original job instead of billing a second one.

The core workflow docs say usage is refunded on failure or cancellation before capture, so a failed job does not keep its reservation. The script prints the terminal sume_status in that case so you can log it, then look at the events URL on the job for the sanitized reason.

What should I check before running it?

For what each second costs and how the price is derived, see Motion control API: animate an image with a driving video.

Request fields for the motion-control endpoint, from the Sume OpenAPI reference read 2026-09-29.
FieldRule
image_urlOne visual source, or avatar_id / avatar_handle instead, never both
motion_video_urlPublic HTTPS URL, at most 30 seconds
duration_secondsRequired, 1 to 30; the price is reserved from it
keep_original_soundOptional, default true

Sources

Related posts

More in Models

All Models posts

Written by Sume