> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mixroute.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Query Kling Task

> Query Kling gateway task states, video outputs and failure reasons.

All four Kling generation endpoints share this query endpoint. Use `data.id` from the submission response.

## Query Parameters

| Field      | Type   | Required | Description                                                                                                                                    |
| ---------- | ------ | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `task_ids` | string | Yes      | One MixRoute gateway task ID, such as task\_example. Despite the plural name, only one ID is allowed; comma-separated batches are unsupported. |

`external_task_ids` is unsupported. Upstream IDs, callback IDs and custom external\_task\_id values cannot replace the gateway task ID.

```bash theme={null}
curl --get "https://api.mixroute.ai/kling/tasks" \
  --header "Authorization: Bearer $MIXROUTE_API_KEY" \
  --data-urlencode "task_ids=$KLING_TASK_ID"
```

## Response

```json theme={null}
{
  "code": 0,
  "message": "",
  "data": [
    {
      "id": "task_example",
      "status": "succeeded",
      "outputs": [
        {
          "type": "video",
          "url": "https://example.com/generated.mp4",
          "duration": "5.041"
        }
      ]
    }
  ]
}
```

| Field                                | Type            | Required | Description                                                                                                                      |
| ------------------------------------ | --------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `code`                               | integer         | Yes      | 0 means the query request succeeded, not that generation completed. Upstream business errors may use HTTP 200 with nonzero code. |
| `request_id`                         | string          | No       | Identifier for troubleshooting this HTTP request, distinct from the task ID; retain it when present.                             |
| `data`                               | object\[]       | Yes      | Array of query results; select the task matching your id.                                                                        |
| `data[].id`                          | string          | Yes      | Gateway task ID.                                                                                                                 |
| `data[].status`                      | string          | Yes      | submitted, processing, succeeded or failed, as determined by the gateway.                                                        |
| `data[].message`                     | string          | No       | Task failure reason, distinct from the outer request message.                                                                    |
| `data[].outputs`                     | object\[]       | No       | Video outputs on success. Read url from type=video entries; may also contain id, watermark\_url and duration.                    |
| `data[].outputs[].duration`          | string / number | No       | Actual generated seconds, potentially fractional; do not truncate to an integer.                                                 |
| `data[].billing`                     | object\[]       | No       | Optional upstream billing information, not guaranteed and not proof of final MixRoute charges.                                   |
| `data[].create_time` / `update_time` | integer         | No       | Upstream Unix timestamps in milliseconds, when present.                                                                          |
| `data[].external_id`                 | string          | No       | Custom business identifier forwarded from upstream, when present.                                                                |

## State Handling

| State        | Action                            |
| ------------ | --------------------------------- |
| `submitted`  | Accepted; continue polling.       |
| `processing` | Processing; continue polling.     |
| `succeeded`  | Stop polling and read video URLs. |
| `failed`     | Stop polling and inspect message. |

Poll about every 15 seconds and back off on rate limits or transient network errors. Responses contain the latest state obtained by the gateway and may lag by a few seconds. A client wait timeout is not a server task failure; keep the ID and continue querying rather than creating a replacement task.

## Error Handling

```json theme={null}
{
  "code": 400,
  "message": "[gateway] unsupported model on this endpoint",
  "request_id": "REQUEST_ID"
}
```

Check both HTTP status and JSON code. A \[gateway] message prefix identifies gateway rejections, such as a model/endpoint mismatch, empty prompt or batch query. Upstream rejections retain upstream error details and request IDs. Keep request\_id and the task ID for troubleshooting.

## Complete Python Workflow

Install requests and set MIXROUTE\_API\_KEY. The first run submits one task; setting KLING\_TASK\_ID queries an existing task instead. POST is not retried automatically; after a submission timeout, check the console first.

```python theme={null}
import os
import time
from urllib.parse import urlparse
import requests

BASE_URL = "https://api.mixroute.ai/kling"

def parse_response(response):
    try:
        result = response.json()
    except ValueError as exc:
        raise RuntimeError(f"HTTP {response.status_code}: invalid JSON response") from exc
    if not isinstance(result, dict):
        raise RuntimeError(f"HTTP {response.status_code}: expected a JSON object")
    code = result.get("code")
    if not 200 <= response.status_code < 300 or type(code) is not int or code != 0:
        raise RuntimeError(
            f"HTTP {response.status_code}; code={code}; "
            f"request_id={result.get('request_id', '')}; "
            f"message={result.get('message', '')}"
        )
    return result

def create_video(session):
    response = session.post(
        BASE_URL + "/text-to-video/kling-3.0",
        json={
            "prompt": "A ceramic mug slowly rotates on a white tabletop.",
            "settings": {"resolution": "720p", "duration": 5,
                         "aspect_ratio": "16:9", "audio": "off"},
        },
        timeout=120,
        allow_redirects=False,
    )
    task_id = parse_response(response).get("data", {}).get("id")
    if not task_id:
        raise RuntimeError("Submission returned no task ID")
    return task_id

def wait_for_video(session, task_id, timeout_seconds=1800):
    deadline = time.monotonic() + timeout_seconds
    delay = 15
    while time.monotonic() < deadline:
        remaining = deadline - time.monotonic()
        if remaining <= 0:
            break
        try:
            response = session.get(
                BASE_URL + "/tasks", params={"task_ids": task_id},
                timeout=min(30, remaining), allow_redirects=False,
            )
        except (requests.Timeout, requests.ConnectionError):
            delay = min(delay * 2, 60)
        else:
            if response.status_code in (429, 500, 502, 503, 504):
                delay = min(delay * 2, 60)
            else:
                tasks = parse_response(response).get("data")
                if not isinstance(tasks, list):
                    raise RuntimeError("Invalid task response")
                task = next((item for item in tasks if item.get("id") == task_id), None)
                if task is None:
                    raise RuntimeError("Task not found in response")
                status = task.get("status")
                if status == "succeeded":
                    videos = [item for item in task.get("outputs", [])
                              if item.get("type") == "video" and item.get("url")]
                    if not videos:
                        raise RuntimeError("Successful task has no video output")
                    for item in videos:
                        parsed = urlparse(item["url"])
                        if parsed.scheme not in ("https", "http") or not parsed.netloc:
                            raise RuntimeError("Invalid output URL")
                    return videos
                if status == "failed":
                    raise RuntimeError(task.get("message") or task)
                if status not in ("submitted", "processing"):
                    raise RuntimeError("Unknown task status: " + str(status))
                delay = 15
        remaining = deadline - time.monotonic()
        if remaining > 0:
            time.sleep(min(delay, remaining))
    raise TimeoutError("Keep task ID and query later: " + task_id)

if __name__ == "__main__":
    with requests.Session() as session:
        session.headers["Authorization"] = "Bearer " + os.environ["MIXROUTE_API_KEY"]
        task_id = os.environ.get("KLING_TASK_ID") or create_video(session)
        print("task_id:", task_id, flush=True)
        for video in wait_for_video(session, task_id):
            print(video["url"], video.get("duration"))
```

Result URLs point to third-party media and may use expiring signatures or retention limits. Save successful outputs promptly rather than treating their URLs as permanent storage. Do not download them with the authenticated session or append your API Key to the URL.
