🎬

Sora 2 API を触ってみた

に公開

OpenAI DevDay 2025 で Sora2 の API が公開されたので公式ドキュメントを参考に試してみました。

https://platform.openai.com/docs/guides/video-generation

概要

Sora2 は 2025年10月現在 OpenAI の最新の動画生成モデルです。Sora2 を使うための Video API では、Sora 2 (id=sora-2) と Sora 2 Pro (id=sora-2-pro) モデルの二つを利用して動画を生成することができます。

公式ドキュメント によると、無印の Sora 2 は速度と柔軟性、Sora 2 Pro はより高品質なプロダクションレベルの出力を得るために使うのが良さそうです。

API から動画を生成してみる

以下、 Python で Video API を試します。 事前に openai ライブラリを最新の状態にアップデートして下さい。

Video API では、非同期に動画が生成されます。生成が完了するまで、create() の出力として得られた video_id を使って /videos/{video_id} のエンドポイントに確認し続ける方法か、Webhook で生成完了時に通知を送る方法の2種類があります。

from openai import OpenAI

openai = OpenAI()

video = openai.videos.create(
    model="sora-2",
    prompt="コーギーがラーメンを食べながら渋谷の街を走り回る動画",
)

print("Video generation started:", video)
出力
Video generation started: Video(id='video_hoge', completed_at=None, created_at=1759774864, error=None, expires_at=None, model='sora-2', object='video', progress=0, remixed_from_video_id=None, seconds='4', size='720x1280', status='queued')

シンプルに生成の進捗を確認するには、以下のようにします。status='completed' となっていれば生成完了です。

openai.videos.poll(video.id)
出力
Video(id='video_hoge', completed_at=1759774941, created_at=1759774864, error=None, expires_at=1759861341, model='sora-2', object='video', progress=100, remixed_from_video_id=None, seconds='4', size='720x1280', status='completed')

生成が完了していそうなので、ダウンロードします。

content = openai.videos.download_content(video.id)
content.write_to_file("video-dog.mp4")

コーギーがラーメンを食べながら渋谷の街を走り回る動画
コーギーがラーメンを食べながら渋谷の街を走り回る動画

以下に、公式ドキュメントに書かれたサンプルコードを添付するので、気になる方はご覧ください。

AsyncOpenAI で非同期に生成ステータスを確認するサンプルコード

import asyncio

from openai import AsyncOpenAI

client = AsyncOpenAI()


async def main() -> None:
    video = await client.videos.create_and_poll(
        model="sora-2",
        prompt="A video of a cat on a motorcycle",
    )

    if video.status == "completed":
        print("Video successfully completed: ", video)
    else:
        print("Video creation failed. Status: ", video.status)


asyncio.run(main())
生成からダウンロードまでの一連のサンプルコード

from openai import OpenAI
import sys
import time


openai = OpenAI()

video = openai.videos.create(
    model="sora-2",
    prompt="A video of a cool cat on a motorcycle in the night",
)

print("Video generation started:", video)

progress = getattr(video, "progress", 0)
bar_length = 30

while video.status in ("in_progress", "queued"):
    # Refresh status
    video = openai.videos.retrieve(video.id)
    progress = getattr(video, "progress", 0)

    filled_length = int((progress / 100) * bar_length)
    bar = "=" * filled_length + "-" * (bar_length - filled_length)
    status_text = "Queued" if video.status == "queued" else "Processing"

    sys.stdout.write(f"
{status_text}: [{bar}] {progress:.1f}%")
    sys.stdout.flush()
    time.sleep(2)

# Move to next line after progress loop
sys.stdout.write("
")

if video.status == "failed":
    message = getattr(
        getattr(video, "error", None), "message", "Video generation failed"
    )
    print(message)
    return

print("Video generation completed:", video)
print("Downloading video content...")

content = openai.videos.download_content(video.id, variant="video")
content.write_to_file("video.mp4")

print("Wrote video.mp4")

補助アセットのダウンロード

動画生成をすると、サムネイルと動画をいくつかのコマに分けたようなスプライトシートも同時にダウンロード可能になります。ダウンロードは生成動画と同じく openai.videos.download_content() の引数の variantthumbnailspritesheet を指定することで実現できます。

# サムネのダウンロード
content = openai.videos.download_content(video.id, variant='thumbnail')
content.write_to_file("thumbnail.jpg")

# スプライトシートのダウンロード
content = openai.videos.download_content(video.id, variant='spritesheet')
content.write_to_file("spritesheet.png")

# ビデオのダウンロード (variant を指定しない場合のデフォルト)
content = openai.videos.download_content(video.id, variant='video')
content.write_to_file("video.mp4")

参照画像を用いた動画生成

動画の初期フレームとして参照画像を指定して動画を生成することもできます。

このサンプル画像を使います。

from openai import OpenAI

openai = OpenAI()

img_path = 'woman_skyline_original_720p.jpeg'

with open(img_path, 'rb') as image_file:
    video = openai.videos.create(
        model="sora-2-pro",
        prompt="彼女は振り返って微笑み、ゆっくりとフレームから出て行きます。",
        input_reference=image_file,
        size='1280x720',
    )

print("Video generation started:", video)

動画のリミックス(特定箇所の編集)機能

リミックス機能を使うと、既に生成が完了した動画をベースにプロンプトを反映した編集を行うことができます。

from openai import OpenAI

openai = OpenAI()

video = openai.videos.remix(
    video_id="video_hoge",
    prompt="コーギーを猫に変えて下さい",
)

print("Video generation started:", video)

見事、先ほどの動画のコーギーが猫に変更された動画が生成されました。

猫がラーメンを食べながら渋谷を走り回っている動画
リミックス機能で既存の生成動画を編集した動画

Webhook で生成完了を通知する

定期的に video_id のエンドポイントに問い合わせる代わりに、Webhookを登録して自動的に通知を送信することもできます。

Webhook は設定ページから登録でき、動画生成が完了すると 生成成功/失敗のそれぞれについて、video.completedvideo.failed のイベント情報が返されます。

Webhook のサンプルペイロード
{
  "id": "evt_abc123",
  "object": "event",
  "created_at": 1758941485,
  "type": "video.completed", // or "video.failed"
  "data": {
    "id": "video_abc123"
  }
}

過去に生成した動画の確認と削除

openai.videos.list() 関数で、削除されていなければ過去に生成した動画が確認できます。

openai.videos.list()
# SyncConversationCursorPage[Video](data=[Video(id='video_hoge', completed_at=1759777411, created_at=1759777316, error=None, expires_at=1759863811, model='sora-2', object='video', progress=100, remixed_from_video_id='video_fuga', seconds='4', size='720x1280', status='completed')...

生成動画の削除は openai.videos.delete() で行えます。

openai.videos.delete(video.id)

生成コンテンツの制限

Video API では、以下のような制限があります。(公式ドキュメントを翻訳)

  • 18歳未満でもみられる適切なコンテンツのみ対象(この制限の回避手段は後に追加予定とのこと)
  • 著作権で保護されたキャラや楽曲は拒否される
  • 著名人などの実在の人物は生成できない
  • 現在、人間の顔を含む画像の入力は拒否される

Sora 2 のプロンプト設計

Sora 2 の動画生成で最適な結果を得るには、ショットの種類、被写体、動作、背景、照明条件 を具体的に書くことが求められます。

以下は、公式の例を日本語に翻訳したものです。

  • 「芝生の公園で、黄金色の夕焼け空を背景に、赤い凧を飛ばす子どものワイドショット。カメラがゆっくりと上方にパンしていく様子」
  • 「木製テーブルの上にある湯気を立てるコーヒーカップのクローズアップ。ブラインド越しの朝の光、柔らかな被写界深度」

より詳細なガイドは以下の Sora 2 Prompting Guide をご参照ください。

https://cookbook.openai.com/examples/sora/sora2_prompting_guide

生成にかかる料金

Sora 2 の動画生成は秒数ごとの課金となっており、2025年10月現在以下のようなレートになっています。Sora 2 Pro だと一つの動画に数百円かかる計算になります。

Sora 2 の生成にかかる料金表

まとめ

Sora 2 の API が公開されたことで、超高品質な動画生成が手軽に行えるようになりました。個人的には API では Sora 2 のウォーターマークが消されているように見えますが、実際に Google の SynthID のようなものが埋め込まれているのかが気になります。

みなさんも生成物に気をつけて動画生成を楽しみましょう。

Discussion