Wan 3.0 API:Python で text-to-video と image-to-video
1 つのスクリプトで 2 本のクリップを作りました。コード、実際の所要時間、Alibaba の 1 秒あたりの料金です。
このページの内容
Wan 3.0 は Alibaba Cloud Model Studio で wan3.0-video として動き、高速版の wan3.0-video-prime もあります。呼び出しはすべて非同期です。タスクを作り、ID をポーリングし、MP4 をダウンロードします。Python スクリプトで、テキストから 5 秒の 720P クリップを 2 min 42 s、静止画から 3 min 20 s で作りました。どちらも音声付きです。
OMNIAKEY_API_KEYにキーを入れます。- モデル、操作、プロンプト、パラメータ、新しい
Idempotency-Keyを付けてhttps://api.omniakey.com/v1/media/tasksに POST します。 - ステータスが succeeded になるまで
GET /v1/media/tasks/{id}をポーリングし、すぐにassets[0].urlをダウンロードします。
Wan 3.0 に公式 API はある?どのモデルを使う?
あります。Alibaba Cloud Model Studio が公式 API で、wan3.0-video と wan3.0-video-prime の両方が使えます(リファレンス更新 2026年10月15日)。作成リクエストには X-DashScope-Async: enable ヘッダーが必要で、Alibaba が示す Python の道は DashScope SDK 2.0.1 以降です。
| Alibaba Model Studio | OmniaKey | |
|---|---|---|
| モデル ID 2 種 | 対応 | 対応 |
| 非同期ヘッダー | 必要 | 不要 |
| 動画リンクの期限 | 24 時間 | 約 1 時間、更新可 |
| 課金単位 | 1 秒ごと | 1 秒ごと |
| Python | DashScope SDK | 標準ライブラリ |
どちらの ID もボディの形は 1 つで、送ったメディアからモデルがモードを決めます。下のスクリプトは wan3.0-video を実行し、MODEL の行で切り替えます。Wan 3.0 Prime は実行していません。OmniaKey のドキュメントは、同じ機能をおよそ半分の時間で返すものと説明しています(2026年10月31日確認)。
最初の呼び出しまでに何が必要?
3 つです。まずキーから始めます。
-
OmniaKey の API キーをシェルで export します。
bashexport OMNIAKEY_API_KEY="your-key" -
Python 3。スクリプトは標準ライブラリだけを import するので、python3 でそのまま動きます。
-
image to video には最初のフレームが要ります。幅およそ 1024 px の JPEG か、公開 HTTPS URL です。スクリプトは 700,000 バイトを超えるファイルを拒否します。Base64 で 3 分の 1 増え、ボディの上限が 1 MiB だからです(ドキュメント、2026年10月31日確認)。
どのパラメータが効く?
7 つのフィールドがほぼすべてを決めます。どれも parameters の中の値です。
| パラメータ | 何をするか | デフォルト |
|---|---|---|
| Duration | クリップの長さ(秒) | 5 |
| Resolution | 解像度の段階 | 1080P |
| Ratio | 画面の縦横比 | adaptive |
| Audio | 音声トラック | on |
| Seed | 再現性のヒント | -1 |
| Prompt extend | プロンプトを書き換える | on |
| Watermark | 隅にマークを付ける | off |
スクリプトが設定するのは duration、resolution、ratio です。720P を要求しており、定価での 1 秒あたりの料金は Alibaba のデフォルトである 1080P の半分です。
Python のコードはどんな形?
下のスクリプトは 2026年10月31日 にそのまま実行しました。標準ライブラリのみ、2 つのモード、変えるのは MODEL の 1 行だけです。
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",
))
各部分の役割:
run_taskはジョブごとに新しいIdempotency-Keyを発行します。同じキーと同じボディを送り直すと元のタスクが返り、二重に課金されることはありません(ドキュメント、2026年10月31日確認)。- 5 秒ごとに最大 15 分ポーリングし、それでも終わらなければ TimeoutError を送出します。
- タイムアウトしたら同じ ID を後でポーリングします。送り直すと新しいキーで 2 つ目のジョブができ、課金されます。
- アセット URL は署名付きで期限が短いので、最初の URL をすぐにダウンロードします。
image_to_videoは 700,000 バイトを超えるフレームを拒否します。Base64 で 3 分の 1 増え、ボディの上限が 1 MiB です。- 画像の呼び出しでは ratio を
adaptiveにするので、クリップは静止画の形に従います。
出力はこうなりました:
task task_<id>
done task_<id>
task task_<id>
done task_<id>
2 回の呼び出しで何ができた?
どちらのファイルも 5.04 秒の MP4 で、30 fps・150 フレーム、H.264、44.1 kHz の AAC ステレオトラックでした。
Text to video:鍛冶場

クリップはプロンプトに従っています。暗いシャツに革のエプロンの鍛冶職人が、赤く光る蹄鉄を金床で打ち、2.2 秒で火花が散り、カメラが少し寄ります。欠点は、テキストなしを頼んだのに、金床に文字化けした刻印が入っていることです。開始からファイルまで 2 min 42 s、MP4 は 1280×720 で 6.6 MB です。
Image to video:紙の舟

最初のフレームは Nano Banana 2.1 で作った静止画で、雨の水たまりに浮かぶ赤い紙の舟です。1024×571、158 KB の JPEG に縮小し、data URL としてインラインで送りました。クリップはその夕暮れの光と濡れた石畳を保ち、舟は指示どおり左から右へ流れ、1.6 秒で波紋が立ち、カメラは低いままで動きません。adaptive が静止画の形に従ったため 1284×716 で返り、3 min 20 s、7.4 MB でした。
音声はオン?「no music」は効く?
音声はデフォルトでオンです。Gemini 3.8 Flash は、鍛冶場のトラックを話し声も音楽もない金属的な音、紙の舟のトラックをもの悲しく静かなピアノの旋律と説明しました。紙の舟のプロンプトの最後は "no music" だったのに、です。Alibaba の prompt guide(2026年10月16日更新)は "No BGM" か "No background music" と書くよう勧めていて、使ったプロンプトはどちらでもありませんでした。
"audio": false にすると、ファイルに入るのは映像トラックだけです。2 秒の 480P のランタンのジョブには音声トラックがなく、Alibaba の料金はどちらでも同じでした。
| クリップ | 時間 | サイズ | 音声 |
|---|---|---|---|
| 鍛冶場(テキスト) | 2 min 42 s | 1280×720 | 金属音、音楽なし |
| 紙の舟(画像) | 3 min 20 s | 1284×716 | "no music" でもピアノ |
| ランタン、音声オフ | 4 min 36 s | 854×480 | 音声トラックなし |
キューの待ち時間は変わります。2 秒で音声オフのジョブは 4 min 36 s かかり、5 秒の 720P のどちらのクリップより長くかかりました。プロンプトの書き方は Wan 3.0 プロンプトガイド に構成と例があります。
動画ファイルはどう取る?リンクはどれだけ有効?
ステータスが succeeded、failed、canceled のいずれかになるまで GET /v1/media/tasks/{id} をポーリングします。実行中は queued か processing です。終わったタスクは、およそ 1 時間有効な署名付きアセット URL を返します(OmniaKey のドキュメント、2026年10月31日確認)。すぐにダウンロードし、期限が切れたらタスクをもう一度ポーリングして新しい URL を取ります。レコードは 24 時間残ります。
ファイルは 30 fps の H.264 映像の MP4 で、Alibaba 自身の動画 URL は 24 時間有効です。Wan 3.0 のジョブをキャンセルしてもレンダリングは止まらず、キャンセルの後に成功したジョブも課金されます。送信の前に duration と resolution を確認してください。
どんなエラーが出る?
ほとんどはタスクができる前に返り、どれもリクエストかキーを直せば解決します。
- 400
model_price_error— duration が 2〜30 秒の範囲外です。"duration": 1のテストは 0.9 s で "media duration must be between 2 and 30 seconds" として失敗しました。タスクは作られず、課金もありませんでした。 - 400
missing_idempotency_key— Idempotency-Key ヘッダーがありません。255 文字までで 1 つ付けます。 - 409
idempotency_key_reused— 同じキーで違うボディです。プロンプトごとに新しいキーを発行します。 - 403
model_not_allowed— そのキーではそのモデルを呼べません。modelの値を確認し、次にキーの設定を見ます。 - 403 と残高や上限のメッセージ — 残高不足かキーごとの上限です。チャージするか上限を上げます。
- 404
task_not_found— ID が違うか、24 時間を超えたタスクです。作成時に返った ID をポーリングします。 - 409
task_state_conflict— ジョブはもう終わっています。成功の後にキャンセルが届いた場合などです。まずステータスを読みます。 - 429 — リクエストが多すぎます。OmniaKey は固定のレート制限を公開していないので、指数バックオフとジッターで間隔を空けます。
- 503
no_available_channel— その操作を扱う提供元がありません。video to video などです。text to video か image to video を使います。 TimeoutError— 15 分たっても実行中です。同じ ID を後でポーリングします。送り直すと 2 つ目のジョブができ、課金されます。
Wan 3.0 のクリップはいくら?
Alibaba は動画の秒数で課金し、失敗したリクエストは課金しません(料金ページ更新 2026年10月29日)。表は Alibaba のシンガポール定価を 1 秒あたりで示したもので、2026年10月31日 に Alibaba の Model Studio 料金ページ で確認しました。
| Model | 480P | 720P | 1080P |
|---|---|---|---|
| Wan 3.0 | $0.05 | $0.1 | $0.2 |
| Wan 3.0 Prime | $0.068 | $0.14 | $0.28 |
5 秒の 720P クリップは Wan 3.0 で $0.50(5 × $0.1)、Prime で $0.70(5 × $0.14)です。Wan 3.0 の 3 つの価格には "Limited-time 30% off" の表示があり、その数字が割引込みかどうかはページに書かれていません。音声がオンでもオフでも同じ料金です。
無料枠は、シンガポールの wan3.0-video で入力と出力を合わせて 30 秒の動画です。Prime にはありません。Alibaba の中国(北京)は標準モデルのドル建ての単価がより低く、480P、720P、1080P の 1 秒あたり $0.041256、$0.082513、$0.165025 です。
OmniaKey も 1 秒単位で課金します。現在の料金は Wan 3.0 と Wan 3.0 Prime のモデルページにあります。
よくある質問
1 回の呼び出しにどれくらいかかる?
Alibaba は通常 1〜5 分で、15 秒ごとのポーリングを勧めています(リファレンス、2026年10月31日確認)。3 つのジョブは 2 min 42 s、3 min 20 s、4 min 36 s でした。
音声をオフにするには?
parameters の中で "audio": false を設定すると、MP4 に入るのは映像トラックだけです。ランタンのジョブがそうでした。料金は変わりません。
wan3.0-video と wan3.0-video-prime のどちらを使う?
Alibaba の安い定価とシンガポールの 30 秒の無料枠を取るなら wan3.0-video、時間を優先するなら Prime です。OmniaKey は Prime を、同じ機能をおよそ半分の時間で返すものと説明しています。
動画リンクはどれだけ有効?
OmniaKey ではおよそ 1 時間です。タスクをもう一度ポーリングすると新しいリンクが返り、レコードは 24 時間残ります。Alibaba 自身の動画 URL は 24 時間有効です。
出典(確認日)
- Wan 3.0 動画生成 API リファレンス、2026年10月31日確認
- Wan 3.0 動画生成ガイド、2026年10月31日確認
- Wan 3.0 動画生成プロンプトガイド、2026年10月31日確認
- Alibaba Cloud Model Studio のモデル料金、2026年10月31日確認
- OmniaKey 動画 API ドキュメント、2026年10月31日確認
- OmniaKey メディア API の概要、2026年10月31日確認
- OmniaKey API エラーコード、2026年10月31日確認

