# Wan 3.0 API: Text-to-Video and Image-to-Video in Python

> Call Wan 3.0 from Python: the script we ran on October 31, 2026 for text-to-video and image-to-video, its parameters, real clips, errors and Alibaba's price.

- Canonical HTML: https://omniakey.com/blog/wan-3-0-api
- Markdown: https://omniakey.com/blog/wan-3-0-api.md
- Published: 2026-10-31 (updated 2026-10-31)
- Author: Kiki, COO at OmniaKey
- Language: en
- Category: API setup
- Reading time: 8 min
- All posts: https://omniakey.com/blog/llms.txt

---

[Wan 3.0](https://omniakey.com/wan3-0-video) runs on Alibaba Cloud Model Studio as `wan3.0-video` and the faster `wan3.0-video-prime`. Every call is asynchronous: create a task, poll the ID, download the MP4. Our Python script made a 5-second 720P clip from text in 2 min 42 s and one from a still in 3 min 20 s, both with sound.

1. Put your key in `OMNIAKEY_API_KEY`.
2. POST `https://api.omniakey.com/v1/media/tasks` with the model, the operation, the prompt, the parameters and a new `Idempotency-Key`.
3. Poll `GET /v1/media/tasks/{id}` until the status reads succeeded, then download `assets[0].url` at once.

## Is there an official Wan 3.0 API, and which model should I use?

Yes. Alibaba Cloud Model Studio is the official API, with both `wan3.0-video` and `wan3.0-video-prime` (reference updated October 15, 2026). Its create call needs an `X-DashScope-Async: enable` header, and Alibaba's Python path is the DashScope SDK 2.0.1 or newer.

||Alibaba Model Studio|OmniaKey|
|-|-|-|
|Both model IDs|Yes|Yes|
|Async header needed|Yes|No|
|Video link lasts|24 hours|About 1 hour, renewable|
|Billed|Per second|Per second|
|Python|DashScope SDK|Standard library|

Both IDs take one body shape, and the model picks the mode from the media you send. The script below runs `wan3.0-video`, and its `MODEL` line switches it. We did not run [Wan 3.0 Prime](https://omniakey.com/wan3-0-video-prime), which OmniaKey's docs describe as the same capabilities at roughly half the turnaround (checked October 31, 2026).

## What do you need before the first call?

Three things, and the first one is the key:

1. An OmniaKey API key, exported in your shell:

   ```bash
   export OMNIAKEY_API_KEY="your-key"
   ```

2. Python 3. The script imports the standard library only, so python3 runs it as is.

3. For image to video, a first frame: a JPEG about 1024 px wide, or a public HTTPS URL. The script refuses a file over 700,000 bytes, because Base64 adds a third and the body cap is 1 MiB (docs, checked October 31, 2026).

## Which parameters matter?

Seven fields do most of the work, and each is a value inside `parameters`.

|Parameter|What it does|Default|
|-|-|-|
|Duration|Clip length, seconds|5|
|Resolution|Picture size tier|1080P|
|Ratio|Frame shape|adaptive|
|Audio|Sound track|on|
|Seed|Repeatability hint|-1|
|Prompt extend|Rewrites your prompt|on|
|Watermark|Adds a corner mark|off|

The script sets duration, resolution and ratio. It asks for 720P, which costs half as much per second at list prices as Alibaba's 1080P default.

## What does the Python code look like?

The script below ran unchanged on October 31, 2026: standard library only, both modes, one MODEL line to change.

```python
import base64
import json
import os
import time
import urllib.error
import urllib.request
import uuid

API = "https://api.omniakey.com/v1/media/tasks"
KEY = os.environ["OMNIAKEY_API_KEY"]
MODEL = "wan3.0-video"

def request(method, url, body=None, extra_headers=None):
    headers = {"Authorization": f"Bearer {KEY}", "User-Agent": "omniakey-example/1"}
    data = None
    if body is not None:
        headers["Content-Type"] = "application/json"
        data = json.dumps(body).encode()
    headers.update(extra_headers or {})
    req = urllib.request.Request(url, data=data, headers=headers, method=method)
    with urllib.request.urlopen(req, timeout=120) as resp:
        return json.load(resp)

def poll(url, attempts=5):
    for attempt in range(attempts):
        try:
            return request("GET", url)
        except urllib.error.HTTPError as err:
            if err.code < 500 or attempt == attempts - 1:
                raise
        except urllib.error.URLError:
            if attempt == attempts - 1:
                raise
        time.sleep(5 * (attempt + 1))

def run_task(body, out_path):
    task = request("POST", API, body, {"Idempotency-Key": str(uuid.uuid4())})["id"]
    print("task", task, flush=True)
    for _ in range(180):  # 5-second polls, up to 15 minutes
        time.sleep(5)
        state = poll(f"{API}/{task}")
        if state["status"] not in ("queued", "processing"):
            break
    else:
        raise TimeoutError(f"{task} is still {state['status']}: poll it again later, do not resubmit")
    if state["status"] != "succeeded":
        raise RuntimeError(state.get("error") or state["status"])
    asset_url = state["assets"][0]["url"]  # signed link: short-lived, so download now
    req = urllib.request.Request(asset_url, headers={"User-Agent": "omniakey-example/1"})
    with urllib.request.urlopen(req, timeout=300) as resp, open(out_path, "wb") as f:
        f.write(resp.read())
    return task

def text_to_video(prompt, out_path, duration=5, resolution="720P", ratio="16:9"):
    body = {
        "model": MODEL,
        "type": "video",
        "operation": "text_to_video",
        "input": {"prompt": prompt},
        "parameters": {"duration": duration, "resolution": resolution, "ratio": ratio},
    }
    return run_task(body, out_path)

def image_to_video(prompt, image_path, out_path, duration=5, resolution="720P"):
    with open(image_path, "rb") as f:
        raw = f.read()
    if len(raw) > 700_000:  # base64 adds a third, and the whole body must stay under 1 MiB
        raise ValueError("first frame too large: save it as a JPEG about 1024 px wide")
    frame = "data:image/jpeg;base64," + base64.b64encode(raw).decode()
    body = {
        "model": MODEL,
        "type": "video",
        "operation": "image_to_video",
        "input": {"prompt": prompt, "media": [{"type": "first_frame", "url": frame}]},
        "parameters": {"duration": duration, "resolution": resolution, "ratio": "adaptive"},
    }
    return run_task(body, out_path)

if __name__ == "__main__":
    print("done", text_to_video(
        "A blacksmith hammers a glowing horseshoe on an anvil in a dark forge. Sparks fly at each "
        "strike and light his leather apron. The camera slowly pushes in. Sound of the hammer ringing "
        "on steel and the fire crackling. No dialogue, no music, no text.",
        "forge.mp4",
    ))
    print("done", image_to_video(
        "Image 1 is the first frame. The paper boat drifts slowly to the right across the puddle. "
        "Light rain makes small ripples and the cafe lights shimmer in the water. The camera stays "
        "low and still. Sound of light rain. No dialogue, no music, no text.",
        "boat.jpg",
        "boat-drifts.mp4",
    ))
```

What each part does:

- `run_task` mints a new `Idempotency-Key` for each job. Resending the same key with the same body returns the original task and never charges twice (docs, checked October 31, 2026).
- It polls every 5 seconds for up to 15 minutes, then raises TimeoutError.
- A timeout means poll the same ID again later; a resubmit mints a new key, creates a second job and bills it.
- It downloads the first asset URL at once, because the signed link is short-lived.
- `image_to_video` refuses a frame over 700,000 bytes: Base64 adds a third, and the body cap is 1 MiB.
- The image call sets ratio to `adaptive`, so the clip follows the still's shape.

What it printed:

```text
task task_<id>
done task_<id>
task task_<id>
done task_<id>
```

## What did the two calls produce?

Both files came back as 5.04-second MP4s, 150 frames at 30 fps, H.264, with an AAC stereo track at 44.1 kHz.

### Text to video: the forge

![Four frames from the Wan 3.0 forge clip](https://omniakey.com/_img/blog/wan-3-0-api/wan-forge-grid.4c1b058b.1288.webp)

_Text to video, 5 s at 720P: frames at 0.3, 2.2, 3.5 and 4.7 seconds._

The clip follows the prompt: a blacksmith in a dark shirt and leather apron hammers a glowing orange horseshoe, sparks burst at 2.2 seconds, and the camera pushes in a little. The flaw is a stamped maker's mark of garbled letters on the anvil, though the prompt asked for no text. Start to file took 2 min 42 s; the MP4 is 1280×720 at 6.6 MB.

### Image to video: the paper boat

![The first-frame still and three frames from the Wan 3.0 paper-boat clip](https://omniakey.com/_img/blog/wan-3-0-api/wan-paper-boat-grid.e2ffd154.1288.webp)

_Image to video: the still we sent (top left), then the clip at 1.6, 3.5 and 4.7 seconds._

The first frame was a still we made with Nano Banana 2.1, a red paper boat on a rain puddle, resized to a 1024×571 JPEG of 158 KB and sent inline as a data URL. The clip keeps that dusk light and the wet cobblestones, drifts the boat left to right as asked, ripples at 1.6 seconds, and holds the camera low and still. It came back as 1284×716 because adaptive followed the still's shape, in 3 min 20 s at 7.4 MB.

### Is the sound on, and does "no music" work?

Sound is on by default. Gemini 3.8 Flash described the forge track as metallic clinking with no speech and no music, and the paper-boat track as a melancholic, calm piano melody, although that prompt ended with "no music". Alibaba's [prompt guide](https://www.alibabacloud.com/help/en/model-studio/wan3-video-generation-prompt-guide) (updated October 16, 2026) suggests writing "No BGM" or "No background music", and our prompt used neither.

With `"audio": false` the file carries a video track only: our 2-second 480P lantern job had no sound track, and Alibaba charges the same either way.

|Clip|Time|Size|Sound|
|-|-|-|-|
|Forge (text)|2 min 42 s|1280×720|Clinking, no music|
|Paper boat (image)|3 min 20 s|1284×716|Piano despite "no music"|
|Lantern, audio off|4 min 36 s|854×480|No sound track|

Queue time varies: the 2-second audio-off job took 4 min 36 s, longer than either 5-second 720P clip. For prompt wording, the [Wan 3.0 prompt guide](https://omniakey.com/blog/wan-3-0-prompt-guide) has the structure and example prompts.

## How do you get the video file, and how long do links last?

Poll `GET /v1/media/tasks/{id}` until the status reads succeeded, failed or canceled; while the job runs it reads queued or processing. A finished task returns a signed asset URL that lasts about an hour (OmniaKey docs, checked October 31, 2026). Download it at once, then poll the task again for a fresh URL whenever the old one expires, because the record lives for 24 hours.

The file is an MP4 with H.264 video at 30 fps, and Alibaba's own video URL lasts 24 hours. Cancelling a Wan 3.0 job does not stop the render, and a job that succeeds after a cancel is still billed, so check duration and resolution before you submit.

## Which errors will you hit?

Most of these come back before a task exists, and each has a fix in the request or the key.

- **400 `model_price_error`** — duration outside 2 to 30 seconds. Our `"duration": 1` probe failed in 0.9 s with "media duration must be between 2 and 30 seconds"; no task was created and nothing was billed.
- **400 `missing_idempotency_key`** — no Idempotency-Key header. Add one, up to 255 characters.
- **409 `idempotency_key_reused`** — same key, different body. Mint a new key for each prompt.
- **403 `model_not_allowed`** — the key may not call that model. Check the `model` value, then the key's settings.
- **403 with a balance or spending-cap message** — low balance or a per-key cap. Top up or raise the cap.
- **404 `task_not_found`** — wrong ID, or a task over 24 hours old. Poll the ID the create call returned.
- **409 `task_state_conflict`** — the job already finished, as when a cancel lands after success. Read the status first.
- **429** — too many requests. OmniaKey publishes no fixed rate limits, so back off with exponential delay and jitter.
- **503 `no_available_channel`** — no channel for that operation, such as video to video. Use text to video or image to video.
- **`TimeoutError`** — still running after 15 minutes. Poll the same ID later; a resubmit makes and bills a second job.

## How much does a Wan 3.0 clip cost?

Alibaba bills per second of video, and a failed request is not billed (pricing page updated October 29, 2026). The table holds Alibaba's Singapore list prices per second, checked October 31, 2026, from [Alibaba's Model Studio pricing page](https://www.alibabacloud.com/help/en/model-studio/model-pricing).

|Model|480P|720P|1080P|
|-|-|-|-|
|Wan 3.0|$0.05|$0.1|$0.2|
|Wan 3.0 Prime|$0.068|$0.14|$0.28|

A 5-second 720P clip is $0.50 on Wan 3.0 (5 × $0.1) and $0.70 on Prime (5 × $0.14). The three Wan 3.0 prices carry a "Limited-time 30% off" marking, and the page does not say whether the figures include it. Sound on or off costs the same.

The free quota is 30 seconds of video, input plus output, on `wan3.0-video` in Singapore; Prime has none. China (Beijing) lists lower dollar rates for the standard model: $0.041256, $0.082513 and $0.165025 per second at 480P, 720P and 1080P.

OmniaKey bills per second too, with current rates on the [Wan 3.0](https://omniakey.com/wan3-0-video) and [Wan 3.0 Prime](https://omniakey.com/wan3-0-video-prime) model pages.

## Frequently asked questions

### How long does a call take?

Alibaba says a job typically takes 1 to 5 minutes and suggests polling every 15 seconds (reference, checked October 31, 2026). Our three jobs took 2 min 42 s, 3 min 20 s and 4 min 36 s.

### How do I turn the sound off?

Set `"audio": false` inside `parameters`, and the MP4 arrives with a video track only, as our lantern job did. The price stays the same.

### Should I use wan3.0-video or wan3.0-video-prime?

Pick `wan3.0-video` for Alibaba's lower list rate and its 30-second free quota in Singapore, and Prime when turnaround matters more. OmniaKey describes Prime as the same capabilities at roughly half the time.

### How long does the video link last?

About an hour on OmniaKey, and polling the task again returns a fresh link while the record lasts 24 hours. Alibaba's own video URL lasts 24 hours.

## Sources checked

- [Wan 3.0 video generation API reference](https://www.alibabacloud.com/help/en/model-studio/wan3-video-generation-api-reference), checked October 31, 2026
- [Wan 3.0 video generation guide](https://www.alibabacloud.com/help/en/model-studio/wan3-video-generation-guide), checked October 31, 2026
- [Wan 3.0 video generation prompt guide](https://www.alibabacloud.com/help/en/model-studio/wan3-video-generation-prompt-guide), checked October 31, 2026
- [Alibaba Cloud Model Studio model pricing](https://www.alibabacloud.com/help/en/model-studio/model-pricing), checked October 31, 2026
- [OmniaKey video API docs](https://docs.omniakey.com/en/media/video), checked October 31, 2026
- [OmniaKey media API overview](https://docs.omniakey.com/en/media/overview), checked October 31, 2026
- [OmniaKey API error codes](https://docs.omniakey.com/en/api/errors), checked October 31, 2026
