Kling motion control sync mode: a 30-second wait, then poll

Sume's sync and subscribe modes on Kling 3.0 Motion Control wait at most 30 seconds. A clip usually outlasts that, so poll status_url; do not resubmit.

5 min readSume
All posts

Using mode: "sync" on Kling 3.0 Motion Control does not make the call wait for the clip. Sume blocks for at most wait_timeout_seconds, clamped to 0 to 30, then returns the current job state with polling URLs. Motion clips can outlast that, so treat the response as a receipt, keep the job id, and poll status_url.

What do the four modes do?

async is the default. sync and subscribe are aliases for the same bounded wait. webhook returns at once and stores a callback. Every mode returns the job id in the first response, so a 2xx means the job exists and paid work is in flight, not that it finished.

Source: Sume OpenAPI and docs, read 2026-10-02. Communication modes table in Jobs and results.
ModeWhat the HTTP call doesNext step
asyncReturns 202 with the envelopePoll status_url until terminal is true
syncWaits up to wait_timeout_seconds (max 30)Not terminal: poll, do not resubmit
subscribeSame as syncSame as sync
webhookReturns 202, stores the callbackWait for the terminal callback and keep polling as a backup

What if the wait runs out?

The response is still 2xx and still carries the job id. sync.timed_out is true, or sync.capacity_exhausted is true if the server skipped the wait because its waiter budget was full. Continue with GET status_url, honoring next_poll_after_seconds when present.

Never submit a new paid job for the same intent. Retrying the submit is fine only with the same Idempotency-Key, which returns the original job.

Should I use a webhook instead?

For anything that can run past 30 seconds, async or webhook is the recommended shape. The webhook is terminal-only: job.completed, job.failed, job.canceled, with no progress callbacks, so keep polling available.

The webhook URL must be public HTTPS; localhost, private-network and non-HTTPS URLs are rejected. Sending webhook_url with no mode selects webhook.

import asyncio, os
import httpx

KEY = os.environ.get("SUME_API_KEY", "")
if not KEY:
    raise SystemExit("set SUME_API_KEY")

async def main():
    body = {
        "image_url": "https://example.com/still.png",
        "motion_video_url": "https://example.com/move.mp4",
        "duration_seconds": 10,
        "mode": "async",
    }
    h = {"x-api-key": KEY, "Idempotency-Key": "mc-poll-001"}
    async with httpx.AsyncClient(timeout=60) as c:
        r = await c.post("https://api.sume.com/v1/kling/3.0/motion-control", headers=h, json=body)
        r.raise_for_status()
        d = r.json()["data"]
        while not d["terminal"]:
            await asyncio.sleep(d.get("next_poll_after_seconds") or 5)
            d = (await c.get(d["status_url"], headers=h)).json()["data"]
        print(d["terminal"], d["result_ready"], d["result_url"])

asyncio.run(main())

Sources

Related posts

More in Developers

All Developers posts

Written by Sume