GPT-6 Sol・Claude Opus 5.5 提供開始GPT-6 Sol の料金は 5.6 Sol の半額

Wan 3.0 API:Python で text-to-video と image-to-video

1 つのスクリプトで 2 本のクリップを作りました。コード、実際の所要時間、Alibaba の 1 秒あたりの料金です。

Kiki, OmniaKey COO読了目安 8分
このページの内容

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 で作りました。どちらも音声付きです。

  1. OMNIAKEY_API_KEY にキーを入れます。
  2. モデル、操作、プロンプト、パラメータ、新しい Idempotency-Key を付けて https://api.omniakey.com/v1/media/tasks に POST します。
  3. ステータスが 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 StudioOmniaKey
モデル ID 2 種対応対応
非同期ヘッダー必要不要
動画リンクの期限24 時間約 1 時間、更新可
課金単位1 秒ごと1 秒ごと
PythonDashScope SDK標準ライブラリ

どちらの ID もボディの形は 1 つで、送ったメディアからモデルがモードを決めます。下のスクリプトは wan3.0-video を実行し、MODEL の行で切り替えます。Wan 3.0 Prime は実行していません。OmniaKey のドキュメントは、同じ機能をおよそ半分の時間で返すものと説明しています(2026年10月31日確認)。

最初の呼び出しまでに何が必要?

3 つです。まずキーから始めます。

  1. OmniaKey の API キーをシェルで export します。

    bash
    export OMNIAKEY_API_KEY="your-key"
    
  2. Python 3。スクリプトは標準ライブラリだけを import するので、python3 でそのまま動きます。

  3. 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 行だけです。

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",
    ))

各部分の役割:

  • 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 にするので、クリップは静止画の形に従います。

出力はこうなりました:

text
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:鍛冶場

Wan 3.0 の鍛冶場クリップから 4 フレーム
Text to video、720P で 5 s:0.3 秒、2.2 秒、3.5 秒、4.7 秒のフレーム。

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

Image to video:紙の舟

Wan 3.0 の紙の舟クリップの最初のフレームと 3 枚のフレーム
Image to video:送った静止画(左上)と、1.6 秒、3.5 秒、4.7 秒のクリップ。

最初のフレームは 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 s1280×720金属音、音楽なし
紙の舟(画像)3 min 20 s1284×716"no music" でもピアノ
ランタン、音声オフ4 min 36 s854×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 料金ページ で確認しました。

Model480P720P1080P
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.0video APIPythontext-to-videoimage-to-video

関連記事

最近の記事