Skip to content

Async Tasks ​

Unified async task API for video generation, image generation, music generation and other long-running tasks.

The unified async task API provides a standardized submit → poll → retrieve workflow, abstracting away provider-specific differences.

Overview ​

AI tasks such as video generation, image generation, and music generation can take anywhere from tens of seconds to several minutes. To ensure a good user experience and system stability, these tasks are handled through an async task API:

  1. Submit a task — Send a generation request and immediately receive a task ID
  2. Poll for status — Use the task ID to check progress, or receive a callback notification
  3. Retrieve results — Once complete, get the generated resource URLs from the response

Submit a Task ​

POST /v1/task/submit

Request Parameters ​

FieldTypeRequiredDescription
modelstringYesModel name
inputobjectYesModel-specific generation parameters
callback_urlstringNoWebhook URL for task status updates

Request Examples ​

bash
curl -X POST https://api.calgine.com.cn/v1/task/submit \
  -H "Authorization: Bearer sk-your-token" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "kling-v3/text-to-video",
    "input": {
      "prompt": "A golden retriever running under cherry blossom trees, slow motion, cinematic",
      "duration": "5",
      "aspect_ratio": "16:9"
    },
    "callback_url": "https://your-server.com/webhook/task"
  }'
python
import requests

response = requests.post(
    "https://api.calgine.com.cn/v1/task/submit",
    headers={
        "Authorization": "Bearer sk-your-token",
        "Content-Type": "application/json",
    },
    json={
        "model": "kling-v3/text-to-video",
        "input": {
            "prompt": "A golden retriever running under cherry blossom trees, slow motion, cinematic",
            "duration": "5",
            "aspect_ratio": "16:9",
        },
        "callback_url": "https://your-server.com/webhook/task",
    },
)

data = response.json()
print(data)
# {"id": "task_abc123", "status": "queued", "created_at": 1711234567}
js
const response = await fetch("https://api.calgine.com.cn/v1/task/submit", {
  method: "POST",
  headers: {
    Authorization: "Bearer sk-your-token",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    model: "kling-v3/text-to-video",
    input: {
      prompt: "A golden retriever running under cherry blossom trees, slow motion, cinematic",
      duration: "5",
      aspect_ratio: "16:9",
    },
    callback_url: "https://your-server.com/webhook/task",
  }),
});

const data = await response.json();
console.log(data);
// {"id": "task_abc123", "status": "queued", "created_at": 1711234567}

Response Example ​

json
{
  "id": "task_abc123",
  "status": "queued",
  "created_at": 1711234567
}

Query a Task ​

GET /v1/task/{task_id}

Request Examples ​

bash
curl https://api.calgine.com.cn/v1/task/task_abc123 \
  -H "Authorization: Bearer sk-your-token"
python
import requests

response = requests.get(
    "https://api.calgine.com.cn/v1/task/task_abc123",
    headers={"Authorization": "Bearer sk-your-token"},
)

data = response.json()
print(data["status"])   # "queued" | "in_progress" | "completed" | "failed"
print(data["outputs"])  # Resource URLs when completed
js
const response = await fetch("https://api.calgine.com.cn/v1/task/task_abc123", {
  headers: { Authorization: "Bearer sk-your-token" },
});

const data = await response.json();
console.log(data.status);  // "queued" | "in_progress" | "completed" | "failed"
console.log(data.outputs); // Resource URLs when completed

Response Examples ​

Task in progress:

json
{
  "id": "task_abc123",
  "status": "in_progress",
  "created_at": 1711234567
}

Task completed (token-based billing):

json
{
  "id": "task_abc123",
  "status": "completed",
  "created_at": 1711234567,
  "completed_at": 1711234620,
  "outputs": [
    "https://cdn.example.com/video/result.mp4"
  ],
  "usage": {
    "type": "tokens",
    "input_tokens": 14,
    "input_token_details": {
      "audio_tokens": 14
    },
    "output_tokens": 45,
    "total_tokens": 59
  }
}

Task completed (duration-based billing):

json
{
  "id": "task_xyz789",
  "status": "completed",
  "created_at": 1711234567,
  "completed_at": 1711234620,
  "outputs": [
    "https://cdn.example.com/video/result.mp4"
  ],
  "usage": {
    "type": "duration",
    "seconds": 27
  }
}

Task failed:

json
{
  "id": "task_abc123",
  "status": "failed",
  "created_at": 1711234567,
  "completed_at": 1711234590,
  "error": "Content policy violation detected"
}

Task Statuses ​

StatusDescription
queuedTask submitted, waiting to be processed
in_progressTask is currently being generated
completedTask finished — results available in outputs
failedTask failed — reason available in error

Callback Notifications ​

When you include a callback_url in your submit request, the system will send a POST request to that URL whenever the task status changes. The callback body uses the same format as the query response:

json
{
  "id": "task_abc123",
  "status": "completed",
  "created_at": 1711234567,
  "completed_at": 1711234620,
  "outputs": [
    "https://cdn.example.com/video/result.mp4"
  ],
  "usage": {
    "type": "tokens",
    "input_tokens": 14,
    "output_tokens": 45,
    "total_tokens": 59
  }
}
  1. The callback URL must be publicly accessible over HTTPS
  2. Callback requests have a 10-second timeout
  3. Callback URLs cannot point to localhost, 127.0.0.1, or the platform's own domain
  4. Even with callbacks enabled, implement polling as a fallback to avoid missing notifications due to network issues

Complete Polling Example ​

python
import time
import requests

API_KEY = "sk-your-token"
BASE_URL = "https://api.calgine.com.cn/v1"

# 1. Submit task
submit_resp = requests.post(
    f"{BASE_URL}/task/submit",
    headers={
        "Authorization": f"Bearer {API_KEY}",
        "Content-Type": "application/json",
    },
    json={
        "model": "kling-v3/text-to-video",
        "input": {
            "prompt": "A golden retriever running under cherry blossom trees",
            "duration": "5",
            "aspect_ratio": "16:9",
        },
    },
)
task = submit_resp.json()
task_id = task["id"]
print(f"Task submitted: {task_id}")

# 2. Poll until complete
while True:
    resp = requests.get(
        f"{BASE_URL}/task/{task_id}",
        headers={"Authorization": f"Bearer {API_KEY}"},
    )
    result = resp.json()
    status = result["status"]
    print(f"Status: {status}")

    if status == "completed":
        print("Results:", result["outputs"])
        break
    elif status == "failed":
        print("Failed:", result.get("error", "Unknown error"))
        break

    time.sleep(5)
js
const API_KEY = "sk-your-token";
const BASE_URL = "https://api.calgine.com.cn/v1";

// 1. Submit task
const submitResp = await fetch(`${BASE_URL}/task/submit`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    model: "kling-v3/text-to-video",
    input: {
      prompt: "A golden retriever running under cherry blossom trees",
      duration: "5",
      aspect_ratio: "16:9",
    },
  }),
});

const task = await submitResp.json();
const taskId = task.id;
console.log(`Task submitted: ${taskId}`);

// 2. Poll until complete
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));

while (true) {
  const resp = await fetch(`${BASE_URL}/task/${taskId}`, {
    headers: { Authorization: `Bearer ${API_KEY}` },
  });
  const result = await resp.json();
  console.log(`Status: ${result.status}`);

  if (result.status === "completed") {
    console.log("Results:", result.outputs);
    break;
  } else if (result.status === "failed") {
    console.log("Failed:", result.error);
    break;
  }

  await sleep(5000);
}

Response Fields ​

FieldTypeDescription
idstringUnique task identifier
statusstringTask status: queued / in_progress / completed / failed
created_atintegerTask creation time (Unix timestamp in seconds)
completed_atintegerTask completion time (Unix timestamp in seconds), only present when completed or failed
outputsstring[]List of result URLs (video/image/audio), only present when completed
usageobjectTask usage information; see the "Usage Information" section below. Only present in terminal states for supported models
errorstringError message, only present when failed

Usage Information ​

After a task reaches a terminal state (completed / failed), some models include a usage field in the response that reports the actual resources consumed. The usage field is a tagged union: the type field distinguishes the metering unit.

type=tokens (token-based billing) ​

Used by Doubao / Vidu / Ali / Kling text-based / Gemini and other token-billed tasks:

json
{
  "type": "tokens",
  "input_tokens": 14,
  "input_token_details": {
    "audio_tokens": 14
  },
  "output_tokens": 45,
  "total_tokens": 59
}
FieldTypeDescription
typestringAlways "tokens"
input_tokensintegerTotal input tokens
input_token_detailsobjectInput token breakdown by modality (optional); zero-valued sub-fields are omitted, and the entire object is omitted when all sub-fields are zero
input_token_details.text_tokensintegerText input tokens
input_token_details.audio_tokensintegerAudio input tokens
input_token_details.image_tokensintegerImage input tokens
input_token_details.cached_tokensintegerTokens served from prompt cache (compatible with OpenAI prompt cache)
output_tokensintegerTotal output tokens
output_token_detailsobjectOutput token breakdown by modality (optional); same omission rules as input_token_details
total_tokensintegerinput_tokens + output_tokens total

type=duration (duration-based billing) ​

Used by Sora / Xai-video and other video generation tasks billed by output duration:

json
{
  "type": "duration",
  "seconds": 27
}
FieldTypeDescription
typestringAlways "duration"
secondsnumberTask usage duration in seconds (decimal allowed)

TIP

  • Tasks billed strictly per-call (e.g., Flux / Replicate / Midjourney) do not return a usage field
  • The usage field is not returned while the task is queued or in_progress
  • Historical tasks and tasks where the upstream did not report usage data also omit this field
  • Sub-fields with a zero value (e.g., text_tokens: 0) are omitted

Completion Notifications for Flux Models ​

  • To be notified when a task finishes, pass callback_url at the top level of the request body; every Flux model supports it.
  • webhook_url / webhook_secret inside input are not sent upstream. The official endpoints of flux-3-image, flux-3-video, flux-tools/video-edit-v1 and flux-tools/outpainting-v1 accept no webhook fields, so they are ignored there; use callback_url or polling.
  • Without a callback, poll GET /v1/task/{task_id} after submitting (every 3–10 seconds; video tasks usually take several minutes).
bash
curl -X POST "https://api.calgine.com.cn/v1/task/submit" \
  -H "Authorization: Bearer sk-your-token" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "flux-tools/outpainting-v1",
    "callback_url": "https://your-server.com/hook",
    "input": {
      "images": ["https://example.com/base.png"],
      "width": 1200,
      "height": 800
    }
  }'

# Poll the task status
curl "https://api.calgine.com.cn/v1/task/{task_id}" \
  -H "Authorization: Bearer sk-your-token"

Notes ​

  1. Authentication uses standard Authorization: Bearer <API_KEY> headers
  2. The input parameters vary by model — click on the model in the model list of the Calgine Tokens Console to view its documentation
  3. Recommended polling interval is 3–10 seconds to avoid excessive requests
  4. Resource URLs in task results may have expiration limits — download and save promptly
  5. To be notified when a Flux task finishes, use the top-level callback_url; see "Completion notifications for Flux models" above