要約: AI APIで動画を生成するには、動画モデルのエンドポイントにテキストプロンプトをPOSTし、ジョブIDを受け取り、完成したMP4が用意されるまでそのジョブをポーリングします。Pythonでおよそ10行です。GPTProtoの統合APIでは、5つの動画モデルを1つのキーと1つの残高で利用できます。Seedance 2.0、ViduQ3-pro、Kling v3.0 std、Hailuo 2.3 Pro、Veo 3.1です。URLの文字列を1つ変更するだけでモデルを切り替えられ、認証、リクエスト形式、ポーリング方法はすべて同じです。料金は前払いの従量課金制で、動画1秒あたり約0.04ドルから利用できます。
これまで、5つの異なる動画モデルを呼び出すには、5つのアカウント、5つのSDK、5つの請求ダッシュボードが必要でした。ByteDanceのSeedance 2.0を使いたいですか?BytePlus(Volcano Engine)のアカウントと、中国国外の多くの人にとって午後だけでは完了できない本人確認が必要です。GoogleのVeoを使いたいですか?コンソールもキーも別です。同じプロンプトで2つのモデルをA/Bテストしたいですか?共有できるものが何もない2つの統合を保守することになります。
このガイドでは、そのすべてを省略します。Python約10行で最初のtext-to-video API呼び出しを行い、完成したMP4をポーリングで取得した後、URLの文字列を1つ変えるだけでさらに4つのモデルにアクセスできます。キーもコードも残高も同じです。
率直に言うと、私はGPTProto向けにこれらの統合ガイドを書いているため、どのエンドポイントを使うかについて利害関係があります。それでも、以下の1つのモデルでは直接利用するよりもGPTProto経由の方が高くなることも含め、数字は正直に記載しています。その場合はそう明記します。
AI動画生成APIとは実際には何か
ノーコードのボタンではありません。非同期ジョブです。プロンプトをPOSTすると、APIがIDを返し、モデルが30秒から数分かけてレンダリングし、動画の準備が整うまで2つ目のエンドポイントをポーリングします。このレンダリング待ち時間こそが2段階設計の理由です。動画はHTTP接続を開いたままにするには時間がかかりすぎるため、ファイルの代わりにジョブチケットを受け取ります。
構築する機能のほぼすべては、次の3種類のリクエスト形式でカバーできます。
- text-to-video — プロンプトから動画クリップを生成します。
- image-to-video — 静止画を動かします。
- reference-to-video — 1枚以上の参照画像を使い、ショット間でキャラクターやオブジェクトの一貫性を保ちます。
以下ではすべて同じベースURL(https://gptproto.com/api/v3)と同じヘッダーを使用します。キーを取得して、最初の呼び出しを行いましょう。
APIキーを取得する
gptproto.comで登録し、ダッシュボードのAPI Keysセクションを開いてキーを作成します。キーはsk-...のような形式です。ここには常設の無料プランはありません。料金は前払いの従量課金制で、残高をチャージすると最初のリクエストからすべての呼び出しで残高が消費されます。テスト用に数ドルを用意してください。短いクリップを数本生成しても、それ以上はかかりません。
依存関係を1つだけインストールします。
pip install requests
最初の呼び出しの前に正しく設定しておくべき点があります。ここでつまずく人が多いためです。ネイティブの/api/v3/では、AuthorizationヘッダーにBearerプレフィックスなしの生のキーを指定します。GPT ProtoにはOpenAI互換の/v1/サーフェスもあり、こちらではBearerを使用します。これを取り違えることが、私が最もよく目にする401エラーの原因です。このチュートリアル全体ではネイティブサーフェスを使用するため、キーはそのまま指定します。
最初の呼び出し:Seedance 2.0、text-to-video
Seedance 2.0はByteDanceの第2世代動画モデルです。ネイティブで同期された音声付きの4〜15秒のクリップを最大1080pでレンダリングでき、カメラが切り替わっても被写体の一貫性を保つマルチショットのシーン向けに作られています。以下はtext-to-video送信の完全な例です。
import requests
import time
API_KEY = "sk-your-key-here" # raw, no "Bearer" prefix
BASE = "https://gptproto.com/api/v3"
headers = {
"Authorization": API_KEY,
"Content-Type": "application/json",
}
def submit_seedance(prompt):
url = f"{BASE}/bytedance/dreamina-seedance-2-0-260128/text-to-video"
payload = {
"prompt": prompt,
"duration": 5, # 4-15 seconds
"aspect_ratio": "16:9",
"resolution": "1080p",
"generate_audio": True,
"camera_fixed": False,
"seed": -1, # -1 = random
}
resp = requests.post(url, headers=headers, json=payload)
resp.raise_for_status()
return resp.json()["data"]["id"]
prompt = (
"A lighthouse keeper climbs a narrow spiral staircase at dawn. "
"Cut to a wide shot of the lamp room as the light sweeps across "
"grey water. Waves crash below. Ambient wind and distant gulls."
)
job_id = submit_seedance(prompt)
print("submitted:", job_id)
通信形式を確認したい場合は、同じリクエストをcURLでも実行できます。
curl -X POST "https://gptproto.com/api/v3/bytedance/dreamina-seedance-2-0-260128/text-to-video" \
-H "Authorization: sk-your-key-here" \
-H "Content-Type: application/json" \
-d '{
"prompt": "A lighthouse keeper climbs a spiral staircase at dawn, then the lamp sweeps across grey water.",
"duration": 5,
"aspect_ratio": "16:9",
"resolution": "1080p",
"generate_audio": true,
"camera_fixed": false,
"seed": -1
}'
レスポンスに動画は含まれません。ジョブが含まれます。
{
"data": {
"id": "cgt-20260709xxxxxx-abc",
"status": "created",
"outputs": [],
"urls": { "get": "https://gptproto.com/api/v3/predictions/cgt-20260709xxxxxx-abc/result" }
},
"message": "success",
"code": 200
}
data.idがチケットです。data.urls.getは、IDがあらかじめ埋め込まれた、そのまま使えるポーリングURLです。つまり、作業を送信し、IDを受け取っただけで、まだ何もレンダリングされていません。
結果をポーリングする
ここで結果エンドポイントを呼び出し、statusがcompletedに変わるまで待ちます。その後、outputsから動画URLを読み取ります。プラットフォーム上のすべてのモデルが同じポーリングエンドポイントとレスポンス形式を使うため、後述する「文字列を1つ変える」方法が機能します。
def wait_for_video(job_id, every=5, timeout=600):
url = f"{BASE}/predictions/{job_id}/result"
waited = 0
while waited < timeout:
resp = requests.get(url, headers=headers)
resp.raise_for_status()
data = resp.json()["data"]
status = data["status"]
if status == "completed":
return data["outputs"][0] # the MP4 URL
if data.get("error"):
raise RuntimeError(data["error"])
print(f" {status}... {waited}s")
time.sleep(every)
waited += every
raise TimeoutError(f"timed out after {timeout}s (last status: {status})")
video_url = wait_for_video(job_id)
print("done:", video_url)
これで生成処理はすべて完了です。2つの関数、1つのプロンプト、1つのMP4だけで済みます。5秒のクリップなら通常1〜2分で完了しますが、音声付きの15秒1080pレンダリングはそれ以上かかる場合があります。数秒おきにポーリングし、過剰なリクエストは避けてください。
ループについて補足します。errorの場合は例外を発生させ、completedでもエラーでもないステータスは「待機を継続」として扱っています。これは意図的な設計です。モデルによって中間ステータスの報告方法が異なるため、変更される可能性のあるステータス一覧を固定するよりも、致命的な失敗でない限り待機を続ける方が安全です。
文字列を1つ変えてエンジンを切り替える:ViduQ3-pro
ここがこの仕組みの利点です。SeedanceではなくViduQ3-proで同じプロンプトをレンダリングするには、パスを変更するだけで済みます。それ以外は何も変わりません。キー、ヘッダー、wait_for_video()はすべてそのまま使えます。
def submit_vidu(prompt):
url = f"{BASE}/vidu/viduq3-pro/text-to-video"
payload = {
"prompt": prompt,
"duration": 5, # ViduQ3 goes up to 16s
"aspect_ratio": "16:9",
"resolution": "1080p",
"audio": True, # note: ViduQ3 uses `audio`, not `generate_audio`
"seed": -1,
}
resp = requests.post(url, headers=headers, json=payload)
resp.raise_for_status()
return resp.json()["data"]["id"]
job_id = submit_vidu(prompt)
video_url = wait_for_video(job_id) # same poller, unchanged
ViduQ3-proは最大16秒のクリップに対応しており、Seedanceの実用的な範囲の2倍です。また、この中では1秒あたりの料金が最も安いモデルです。パラメーター名はモデルによって少し異なります(ViduQ3はaudioで音声を制御し、Seedanceはgenerate_audioを使います)。正確なフィールド一覧はモデルページで確認してください。ただし、リクエスト、送信、ポーリングという流れは変わりません。
キャラクターの一貫性を保つ。 どちらの主要モデルもtext-to-video以外に対応しています。静止画をアニメーション化するには、ViduQ3で/vidu/viduq3-pro/image-to-videoを指定し、imageフィールドを追加します。または、Seedanceのreference-to-videoモードで参照画像を使い、別々のショット間で同じ顔を維持できます。送信とポーリングの流れは同じで、新しいAPIを覚える必要はありません。画像入力を追加するだけです。本番環境に組み込む前に、各モデルページで参照フィールド名を確認してください。
どのモデルを呼び出すべきか?
5つのモデルを1つのキーで利用できます。ただし、数字が大きいほど常に優れているという階層ではなく、それぞれ用途が異なります。クリップに必要な要素に応じて選んでください。
| モデル |
パス({provider}/{model}) |
ネイティブ音声 |
最大長 |
料金 |
適した用途 |
| Seedance 2.0 |
bytedance/dreamina-seedance-2-0-260128 |
あり |
約15秒 |
1回0.2957ドルから |
音声が同期したマルチショットのシーン |
| ViduQ3-pro |
vidu/viduq3-pro |
あり |
16秒 |
1秒0.04ドルから |
長いクリップ、最も低い料金 |
| Kling v3.0 std |
kling/kling-v3.0-std |
あり |
約10秒 |
1回0.2016ドルから |
安価で信頼性の高いimage-to-video |
| Hailuo 2.3 Pro |
minimax/hailuo-2.3-pro |
なし |
約10秒 |
1回0.441ドル |
音声を後から追加する場合のきれいな1080p映像 |
| Veo 3.1 |
google/veo3.1 |
あり |
— |
1回0.5ドルから |
4K出力が必要な場合 |
契約する前に、2点確認しておきましょう。Hailuo 2.3 Proは無音の動画を返します。映像はすばらしいですが、音声は後処理で付ける必要があります。また、Veo 3.1はすべての出力に埋め込みSynthID透かしを付け、無効化できません。クライアントが出所を示すマークの埋め込みを望まない場合は重要な点です。
プラットフォームがこれらすべてを1つの送信/ポーリング契約の下で統一しているため、モデル間の反復は書き直しではなく辞書の参照で済みます。
MODELS = {
"seedance": "bytedance/dreamina-seedance-2-0-260128",
"vidu": "vidu/viduq3-pro",
"kling": "kling/kling-v3.0-std",
"hailuo": "minimax/hailuo-2.3-pro",
"veo": "google/veo3.1",
}
def submit(model_key, scene, payload):
url = f"{BASE}/{MODELS[model_key]}/{scene}"
resp = requests.post(url, headers=headers, json=payload)
resp.raise_for_status()
return resp.json()["data"]["id"]
# same wait_for_video() retrieves any of them
job_id = submit("kling", "image-to-video", {"prompt": prompt, "image": image_url})
モデルごとにペイロードは異なります。調整するのはこの部分です。しかし、モデルの検出、認証、結果の取得方法は変わりません。
実際にかかる料金
動画料金は1秒単位または1回単位で計算され、解像度、長さ、音声生成の有無によって大きく変わります。モデルページに表示される目立つ数字は最低料金であり、実際の予測額ではありません。実例として、Seedanceで720p、16:9、音声付きの5秒クリップを生成すると約0.605ドルになります。基準料金の0.2957ドルを大きく上回るのは、すべてのコスト要因を同時に引き上げているためです。表示価格ではなく、ダッシュボードの見積もりから実際の設定の料金を見積もってください。
各ベンダーを直接呼び出す場合との正直な比較は次のとおりです。
- ViduQ3-pro — 1秒0.04ドルから。市場の目安である約0.05ドル/秒をおよそ20%下回ります。
- Kling v3.0 std — 1回0.2016ドルから。約0.252ドルの基準額を約20%下回ります。
- Hailuo 2.3 Pro — 1回0.441ドル。MiniMaxの約0.49ドルに対して約10%安価です。
- Seedance 2.0 — 1回0.2957ドルから。ByteDance独自のDreaminaで同じクリップを直接レンダリングする場合より、約10% 高くなります。この点をごまかすつもりはありません。API経由で呼び出す理由は低価格ではなく、安定したプログラムアクセス、1つの残高、BytePlusの本人確認が不要なことです。Seedanceについてコストだけを重視するなら、直接利用する方が安価です。
- Veo 3.1 — 1回0.5ドルから。ページ上に明確な市場基準がないため、割引ではなく利便性を重視した選択と考えてください。
無料プランも月額サブスクリプションもありません。最初のリクエストから呼び出しごとに支払います。特定のモデルの料金は、モデルカタログで現在の秒数別・解像度別料金表を確認できます。
エラーと注意点
動作しない場合に確認すべき短いフィールドガイドです。
- 401 Unauthorized — ほぼ常に
Bearerの取り違えです。ネイティブの/api/v3/サーフェスでは、生のキーが必要です。
- 403 Forbidden — 通常は権限の問題ではなく、残高不足です。チャージしてください。
- 429 — レート制限に達しています。間隔を空けて再試行してください。
- コンテンツメッセージ付きの400 — プロンプトがモデレーションに抵触しました。レスポンスには、パイプラインで確認する価値のある
has_nsfw_contentsフラグも含まれます。
- ポーリングが完了しない — 長時間のレンダリングもあります。そのため、
wait_for_video()には無限ループではなくタイムアウトが設定されています。
- 音声パラメーターが間違っている —
generate_audio(Seedance)とaudio(ViduQ3)の違いです。間違った方を送信してもエラーにならず、何も起こらないため、かえって厄介です。モデルごとにフィールド名を確認してください。
ここから始める
最短ルートは、キーを取得し、上記のSeedanceとViduQ3のスニペットを実行してから、モデルの文字列を変更して残りを試すことです。モデル選びに必要な情報(最新料金、対応モード、解像度別料金表)は、カタログの各モデルページに掲載されています。1つのキー、1つの残高、5つの動画エンジン。