Skip to main content
Some AIsa endpoints — notably video generation — take longer than an HTTP request can reasonably hold open. These endpoints use an async task pattern: one call creates the task and returns a task ID; a second call polls the task until it’s done.
LLM inference, image generation, and most data APIs are synchronous — they return the result in a single response. The async pattern only applies to endpoints that return a task_id instead of a result.

The pattern

1

Create a task

Send the generation request with the header X-DashScope-Async: enable. The response returns immediately with a task_id and initial status (PENDING or RUNNING).
2

Poll for completion

Call GET /apis/v1/services/aigc/tasks?task_id={task_id} every few seconds until task_status is SUCCEEDED or FAILED.
3

Download the result

On success, the response contains a time-limited URL (e.g., video_url). Download the asset and store it — the URL expires after a few hours.

Example: video generation

1. Create the task

curl -X POST "https://api.aisa.one/apis/v1/services/aigc/video-generation/video-synthesis" \
  -H "Authorization: Bearer $AISA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "X-DashScope-Async: enable" \
  -d '{
    "model": "wan2.6-t2v",
    "input": {
      "prompt": "cinematic close-up, slow push-in, shallow depth of field"
    }
  }'
Response:
{
  "output": {
    "task_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "task_status": "PENDING"
  },
  "request_id": "req_01JABCD9F1YYEXAMPLE"
}

2. Poll the task

task_id is a path parameter, not a query string. GET /services/aigc/tasks?task_id=... returns 500 unsupported uri.
curl "https://api.aisa.one/apis/v1/services/aigc/tasks/a1b2c3d4-e5f6-7890-abcd-ef1234567890" \
  -H "Authorization: Bearer $AISA_API_KEY"
While still running:
{
  "output": {
    "task_id": "a1b2c3d4-...",
    "task_status": "RUNNING"
  },
  "request_id": "8a931b13-c44f-9230-a76b-83487d840060"
}
On completion:
{
  "output": {
    "task_id": "a1b2c3d4-...",
    "task_status": "SUCCEEDED",
    "video_url": "https://cdn.aisa.one/videos/wan2.6/....mp4",
    "orig_prompt": "cinematic slow push-in",
    "submit_time": "2026-04-18 15:16:01.841",
    "scheduled_time": "2026-04-18 15:16:01.867",
    "end_time": "2026-04-18 15:16:42.512"
  },
  "usage": {
    "output_video_duration": 5,
    "video_count": 1,
    "SR": 720
  },
  "request_id": "..."
}
On failure, the error fields are flat inside output (not nested under error):
{
  "output": {
    "task_id": "a1b2c3d4-...",
    "task_status": "FAILED",
    "code": "InvalidParameter",
    "message": "prompt must contain words",
    "submit_time": "2026-04-18 15:16:01.841",
    "scheduled_time": "2026-04-18 15:16:01.867",
    "end_time": "2026-04-18 15:16:01.941"
  },
  "request_id": "..."
}

3. Download the asset

curl -L "https://cdn.aisa.one/videos/wan2.6/....mp4" -o output.mp4

Task lifecycle states

StatusMeaningBilling
PENDINGTask accepted, queued for executionNot billed yet
RUNNINGActively generatingBilled on success
SUCCEEDEDGeneration complete. output.video_url is populatedBilled in full
FAILEDGeneration failed. output.code and output.message explain whyNot billed
CANCELEDTask was cancelled (e.g., user request or policy)Partial billing possible
UNKNOWNThe task_id is not recognized or has expired

Polling best practices

Don’t poll faster than 2 Hz (every 500 ms). Most video jobs take 20–90 seconds, so polling every 3–5 s is plenty.
1

Backoff on transient states

When status is PENDING / RUNNING / UNKNOWN, wait before the next poll. A simple linear delay works; exponential is overkill for a bounded-duration job.
3s → 3s → 3s → ... → until SUCCEEDED or FAILED
2

Cap the total wait

Enforce a hard timeout (e.g., 5 minutes). If the task hasn’t completed, log the task_id for later inspection and surface a timeout to the caller.
3

Handle `FAILED` explicitly

A failed task is not billed, but you should inspect error.code and error.message to decide whether to retry. Common causes: safety-policy violation, invalid prompt, upstream outage.
4

Cache the result URL

Result URLs expire within a few hours. Download the asset and persist it to your own storage before the URL expires.

Minimal poller (Python)

import time, requests, os

API = "https://api.aisa.one/apis/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['AISA_API_KEY']}"}

def create_video(prompt, model="wan2.6-t2v"):
    r = requests.post(
        f"{API}/services/aigc/video-generation/video-synthesis",
        headers={**HEADERS, "X-DashScope-Async": "enable"},
        json={"model": model, "input": {"prompt": prompt}},
    )
    r.raise_for_status()
    return r.json()["output"]["task_id"]

def wait_for_task(task_id, interval=3, timeout=300):
    deadline = time.time() + timeout
    while time.time() < deadline:
        # task_id goes in the PATH, not as a query parameter
        r = requests.get(f"{API}/services/aigc/tasks/{task_id}", headers=HEADERS)
        r.raise_for_status()
        out = r.json()["output"]
        status = out["task_status"]
        if status == "SUCCEEDED":
            return out["video_url"]
        if status == "FAILED":
            raise RuntimeError(f"Task failed: {out.get('code')}: {out.get('message')}")
        time.sleep(interval)
    raise TimeoutError(f"Task {task_id} did not complete within {timeout}s")

task_id = create_video("cinematic slow push-in on a neon city")
video_url = wait_for_task(task_id)
print("Video ready:", video_url)

Concurrency limits

Async endpoints cap concurrent in-flight tasks per key. Typical default:
EndpointConcurrent tasks per key
Video synthesis3
Image generation30 RPM (synchronous)
Hitting the concurrency cap returns 429 on the create call. The Retry-After header tells you how long to wait. See Rate Limits for the full table.

Video synthesis endpoint

Reference for the task-creation endpoint.

Task status endpoint

Reference for the polling endpoint.

Media Gen skill

Agent-skill wrapper that handles task polling for you.

Error Codes

Handling failures and retries.