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:
- Submit a task — Send a generation request and immediately receive a task ID
- Poll for status — Use the task ID to check progress, or receive a callback notification
- Retrieve results — Once complete, get the generated resource URLs from the response
Submit a Task
POST /v1/task/submit
Request Parameters
| Field | Type | Required | Description |
|---|---|---|---|
model | string | Yes | Model name |
input | object | Yes | Model-specific generation parameters |
callback_url | string | No | Webhook URL for task status updates |
Request Examples
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"
}'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}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
{
"id": "task_abc123",
"status": "queued",
"created_at": 1711234567
}Query a Task
GET /v1/task/{task_id}
Request Examples
curl https://api.calgine.com.cn/v1/task/task_abc123 \
-H "Authorization: Bearer sk-your-token"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 completedconst 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 completedResponse Examples
Task in progress:
{
"id": "task_abc123",
"status": "in_progress",
"created_at": 1711234567
}Task completed (token-based billing):
{
"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):
{
"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:
{
"id": "task_abc123",
"status": "failed",
"created_at": 1711234567,
"completed_at": 1711234590,
"error": "Content policy violation detected"
}Task Statuses
| Status | Description |
|---|---|
queued | Task submitted, waiting to be processed |
in_progress | Task is currently being generated |
completed | Task finished — results available in outputs |
failed | Task 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:
{
"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
}
}- The callback URL must be publicly accessible over HTTPS
- Callback requests have a 10-second timeout
- Callback URLs cannot point to
localhost,127.0.0.1, or the platform's own domain - Even with callbacks enabled, implement polling as a fallback to avoid missing notifications due to network issues
Complete Polling Example
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)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
| Field | Type | Description |
|---|---|---|
id | string | Unique task identifier |
status | string | Task status: queued / in_progress / completed / failed |
created_at | integer | Task creation time (Unix timestamp in seconds) |
completed_at | integer | Task completion time (Unix timestamp in seconds), only present when completed or failed |
outputs | string[] | List of result URLs (video/image/audio), only present when completed |
usage | object | Task usage information; see the "Usage Information" section below. Only present in terminal states for supported models |
error | string | Error 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:
{
"type": "tokens",
"input_tokens": 14,
"input_token_details": {
"audio_tokens": 14
},
"output_tokens": 45,
"total_tokens": 59
}| Field | Type | Description |
|---|---|---|
type | string | Always "tokens" |
input_tokens | integer | Total input tokens |
input_token_details | object | Input 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_tokens | integer | Text input tokens |
input_token_details.audio_tokens | integer | Audio input tokens |
input_token_details.image_tokens | integer | Image input tokens |
input_token_details.cached_tokens | integer | Tokens served from prompt cache (compatible with OpenAI prompt cache) |
output_tokens | integer | Total output tokens |
output_token_details | object | Output token breakdown by modality (optional); same omission rules as input_token_details |
total_tokens | integer | input_tokens + output_tokens total |
type=duration (duration-based billing)
Used by Sora / Xai-video and other video generation tasks billed by output duration:
{
"type": "duration",
"seconds": 27
}| Field | Type | Description |
|---|---|---|
type | string | Always "duration" |
seconds | number | Task usage duration in seconds (decimal allowed) |
TIP
- Tasks billed strictly per-call (e.g., Flux / Replicate / Midjourney) do not return a
usagefield - The
usagefield is not returned while the task isqueuedorin_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_urlat the top level of the request body; every Flux model supports it. webhook_url/webhook_secretinsideinputare not sent upstream. The official endpoints offlux-3-image,flux-3-video,flux-tools/video-edit-v1andflux-tools/outpainting-v1accept no webhook fields, so they are ignored there; usecallback_urlor polling.- Without a callback, poll
GET /v1/task/{task_id}after submitting (every 3–10 seconds; video tasks usually take several minutes).
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
- Authentication uses standard
Authorization: Bearer <API_KEY>headers - The
inputparameters vary by model — click on the model in the model list of the Calgine Tokens Console to view its documentation - Recommended polling interval is 3–10 seconds to avoid excessive requests
- Resource URLs in task results may have expiration limits — download and save promptly
- To be notified when a Flux task finishes, use the top-level
callback_url; see "Completion notifications for Flux models" above