> ## Documentation Index
> Fetch the complete documentation index at: https://docs.apiyi.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 画像から動画へのAPIリファレンス

> Sora 2 の画像から動画への API リファレンスとライブプレイグラウンド — 静止画像をアニメーション化するために input_reference を multipart アップロードします。

<Info>
  右側のインタラクティブな Playground では、ライブデバッグをサポートしています。**Authorization** フィールドに API Key を設定し（形式: `Bearer sk-xxx`）、参照画像をアップロードして、prompt を入力し、モデル / サイズ / 秒数を選んで送信してください。
</Info>

<Tip>
  **適用範囲**: このページでは「参照画像から動画を生成」する方法を扱います。1枚の画像を開始フレーム / 視覚アンカーとしてアップロードし、静止画をアニメーション化します。参照画像が不要な場合は、[Text-to-Video エンドポイント](/ja/api-capabilities/sora-2/text-to-video)（同じパス、JSON ボディ）を使用してください。
</Tip>

<Warning>
  **⚠️ 参照画像のサイズは `size` と完全に一致している必要があります**

  * アップロードする画像のピクセル寸法は `size` フィールドと一致していなければなりません（例: `size=1280x720` には 1280×720 の画像が必要です）
  * 不一致の場合は 400 が返ります: `Inpaint image must match the requested width and height`
  * **アップロード前に ffmpeg / Pillow で事前にトリミングしてください**

  その他の注意事項:

  * Content-Type は `multipart/form-data` である必要があります（JSON ではありません）
  * 対応ファイルは1つのみで、フィールド名は `input_reference` に固定されています
  * 対応形式: `image/jpeg` / `image/png` / `image/webp`
</Warning>

## コードサンプル

### Python (OpenAI SDK ドロップイン)

```python theme={null}
from openai import OpenAI
import time

client = OpenAI(
    api_key="sk-your-api-key",
    base_url="https://api.apiyi.com/v1"
)

# Step 1: Submit (the OpenAI SDK auto-handles multipart when input_reference is provided)
with open("./reference.png", "rb") as f:
    video = client.videos.create(
        model="sora-2",
        prompt="Animate this scene: gentle waves lapping against the shore, leaves swaying in the breeze",
        seconds="8",
        size="1280x720",
        input_reference=f
    )
print(f"Video ID: {video.id}, status: {video.status}")

# Step 2: Poll
while True:
    video = client.videos.retrieve(video.id)
    print(f"Status: {video.status}, progress: {getattr(video, 'progress', 0)}%")
    if video.status == "completed":
        break
    if video.status == "failed":
        raise RuntimeError(f"Generation failed: {video}")
    time.sleep(15)

# Step 3: Download
client.videos.download_content(video.id).write_to_file("output.mp4")
print("Saved: output.mp4")
```

### Python (Raw requests + multipart)

```python theme={null}
import requests
import time

API_KEY = "sk-your-api-key"
BASE_URL = "https://api.apiyi.com/v1"
HEADERS = {"Authorization": f"Bearer {API_KEY}"}

# Step 1: Multipart upload (image dimensions must equal size)
with open("./reference.png", "rb") as f:
    resp = requests.post(
        f"{BASE_URL}/videos",
        headers=HEADERS,  # Don't manually set Content-Type — requests handles the multipart boundary
        data={
            "model": "sora-2",
            "prompt": "Animate this scene with cinematic camera push-in, soft golden hour lighting",
            "seconds": "8",
            "size": "1280x720"
        },
        files={
            "input_reference": ("reference.png", f, "image/png")
        },
        timeout=60  # Multipart uploads of large images can be slow; use a 60-second timeout
    ).json()
video_id = resp["id"]
print(f"Video ID: {video_id}, status: {resp['status']}")

# Step 2: Poll
deadline = time.time() + 900
while time.time() < deadline:
    status_resp = requests.get(f"{BASE_URL}/videos/{video_id}", headers=HEADERS).json()
    print(f"Status: {status_resp['status']}, progress: {status_resp.get('progress', 0)}%")
    if status_resp["status"] == "completed":
        break
    if status_resp["status"] == "failed":
        raise RuntimeError(f"Generation failed: {status_resp}")
    time.sleep(15)

# Step 3: Download
with requests.get(f"{BASE_URL}/videos/{video_id}/content", headers=HEADERS, stream=True) as r:
    r.raise_for_status()
    with open("output.mp4", "wb") as f:
        for chunk in r.iter_content(chunk_size=8192):
            f.write(chunk)
print("Saved: output.mp4")
```

### cURL

```bash theme={null}
{/* Step 1: Multipart upload + submit */}
curl -X POST "https://api.apiyi.com/v1/videos" \
  -H "Authorization: Bearer sk-your-api-key" \
  -F "model=sora-2" \
  -F "prompt=Animate this scene: gentle waves lapping, leaves swaying, cinematic" \
  -F "seconds=8" \
  -F "size=1280x720" \
  -F "input_reference=@./reference.png;type=image/png"

{/* Step 2: Poll */}
curl -X GET "https://api.apiyi.com/v1/videos/video_abc123" \
  -H "Authorization: Bearer sk-your-api-key"

{/* Step 3: Download */}
curl -X GET "https://api.apiyi.com/v1/videos/video_abc123/content" \
  -H "Authorization: Bearer sk-your-api-key" \
  -o output.mp4
```

### Node.js (fetch + FormData)

```javascript theme={null}
import fs from 'node:fs';
import { fileFromPath } from 'formdata-node/file-from-path';
import { FormData } from 'formdata-node';

const API_KEY = 'sk-your-api-key';
const BASE_URL = 'https://api.apiyi.com/v1';

// Step 1: Multipart upload
const form = new FormData();
form.set('model', 'sora-2');
form.set('prompt', 'Animate this scene with cinematic camera push-in, soft lighting');
form.set('seconds', '8');
form.set('size', '1280x720');
form.set('input_reference', await fileFromPath('./reference.png'));

const submitResp = await fetch(`${BASE_URL}/videos`, {
    method: 'POST',
    headers: { 'Authorization': `Bearer ${API_KEY}` },  // Don't manually set Content-Type
    body: form
});
const { id: videoId } = await submitResp.json();
console.log(`Video ID: ${videoId}`);

// Step 2: Poll
let status = 'queued';
while (status !== 'completed' && status !== 'failed') {
    await new Promise(r => setTimeout(r, 15000));
    const data = await (await fetch(`${BASE_URL}/videos/${videoId}`, {
        headers: { 'Authorization': `Bearer ${API_KEY}` }
    })).json();
    status = data.status;
    console.log(`Status: ${status}, progress: ${data.progress ?? 0}%`);
}

if (status === 'failed') throw new Error('Generation failed');

// Step 3: Download
const contentResp = await fetch(`${BASE_URL}/videos/${videoId}/content`, {
    headers: { 'Authorization': `Bearer ${API_KEY}` }
});
fs.writeFileSync('output.mp4', Buffer.from(await contentResp.arrayBuffer()));
console.log('Saved: output.mp4');
```

### ブラウザ JavaScript

```javascript theme={null}
{/* Demo only; route through your backend in production to avoid leaking the API key. */}
const fileInput = document.getElementById('refImage');  // <input type="file" />
const file = fileInput.files[0];

const form = new FormData();
form.append('model', 'sora-2');
form.append('prompt', 'Animate this scene, gentle motion');
form.append('seconds', '4');
form.append('size', '1280x720');
form.append('input_reference', file);

const submitResp = await fetch('https://api.apiyi.com/v1/videos', {
    method: 'POST',
    headers: { 'Authorization': 'Bearer sk-your-api-key' },
    body: form
});
const { id } = await submitResp.json();
console.log('Video ID:', id);

{/* After polling completes, route the video URL through a backend proxy to avoid downloading large files in the browser. */}
```

## パラメータ クイックリファレンス

| パラメータ             | 型      | 必須  | デフォルト      | 説明                                                                                    |
| ----------------- | ------ | --- | ---------- | ------------------------------------------------------------------------------------- |
| `model`           | string | Yes | —          | `sora-2`（720p のみ）または `sora-2-pro`（720p / 1024p / 1080p の各ティア）                         |
| `prompt`          | string | Yes | —          | 動画の説明。**静止画像がどのようにアニメーションするか**（カメラの動き、オブジェクトの動き、ライティングの変化）に重点を置いてください                 |
| `seconds`         | string | No  | `"4"`      | **文字列の列挙型**としての期間: `"4"` / `"8"` / `"12"`                                             |
| `size`            | string | No  | `720x1280` | 出力解像度。**`input_reference`の画像サイズと完全に一致している必要があります**                                    |
| `input_reference` | file   | Yes | —          | 参照画像ファイル: `image/jpeg` / `image/png` / `image/webp`、**サイズは `size` と完全に一致している必要があります** |

<Tip>
  詳細なパラメータ制約、許可される値、例は右側の Playground で確認できます。**`input_reference` は multipart でアップロードする必要があります** — URL と base64 は受け付けられません。
</Tip>

## 参照画像の準備

<Steps>
  <Step title="ターゲット解像度を選びます">
    用途に応じて、まず `size` を選びます: 縦長 `720x1280`、横長 `1280x720`、Pro 1080p 横長 `1920x1080` など。
  </Step>

  <Step title="ローカルでピクセル単位まで正確に切り抜きます">
    Pillow / ffmpeg を使って、画像をターゲットサイズに切り抜きます:

    ```python theme={null}
    from PIL import Image
    img = Image.open("source.jpg")
    img = img.resize((1280, 720), Image.LANCZOS)  # Or crop first then resize to preserve aspect ratio
    img.save("reference.png")
    ```

    または、1 行の ffmpeg で:

    ```bash theme={null}
    ffmpeg -i source.jpg -vf "scale=1280:720" reference.png
    ```
  </Step>

  <Step title="適切なフォーマットを選びます">
    PNG（ロスレスで、イラスト / スクリーンショットに最適）を優先し、写真には容量を節約できる JPEG、透過が必要なら WebP を使います。
  </Step>

  <Step title="Focus the prompt on &#x22;motion&#x22; not &#x22;appearance&#x22;">
    参照画像でビジュアルはすでに決まっています。prompt では **どのようにアニメーションさせるか** に集中してください: カメラの前進/後退、オブジェクトの動き、ライティングの変化、キャラクターの表情など。例: `"Camera slowly pushes in, leaves gently swaying, sunlight flickering through branches"`.
  </Step>
</Steps>

## レスポンス形式

レスポンスの形は [Text-to-Video](/ja/api-capabilities/sora-2/text-to-video#response-format) と**同一**です。submit は `id` + `status: "queued"` を返し、ポーリングで進行状況を報告し、完了後は `/v1/videos/{id}/content` 経由で MP4 としてダウンロードします。

```json theme={null}
{
  "id": "video_abc123def456",
  "object": "video",
  "model": "sora-2",
  "status": "queued",
  "progress": 0,
  "created_at": 1712697600,
  "size": "1280x720",
  "seconds": "8",
  "quality": "standard"
}
```

<Warning>
  **⚠️ よくある 400 エラー**

  * `Inpaint image must match the requested width and height` — 参照画像のサイズが `size` と一致しません。**最も一般的です。** アップロード前にクライアント側でサイズを検証してください
  * `Invalid file format` — アップロードされたファイルが jpeg / png / webp ではないか、破損しています
  * `Missing required parameter: input_reference` — multipart のフィールド名が誤っています（`input_reference` である必要があり、`image` や `reference` ではありません）
  * `seconds must be one of "4", "8", "12"` — 文字列の `"4"` ではなく、整数の `4` を渡しています
</Warning>

<Info>
  画像から動画とテキストから動画は、**同じ 1 秒あたりの料金**です（`seconds` によって課金されます）。参照画像をアップロードしても追加料金はかかりません。[料金表](/ja/api-capabilities/sora-2/overview#pricing) をご覧ください。
</Info>


## OpenAPI

````yaml api-reference/sora-2-image-to-video-openapi-en.yaml POST /v1/videos
openapi: 3.1.0
info:
  title: Sora 2 Image-to-Video API
  description: >
    OpenAI Sora 2 / Sora 2 Pro image-to-video endpoint (multipart/form-data
    upload of `input_reference`).


    - **Reference image dimensions must exactly match `size`**, otherwise you
    get `Inpaint image must match the requested width and height`

    - Accepted image formats: `image/jpeg` / `image/png` / `image/webp`

    - Same per-second pricing as text-to-video — uploading a reference image
    does not cost extra

    - **Async task endpoint**: this call only submits the task; combine with
    `GET /v1/videos/{id}` polling and `GET /v1/videos/{id}/content` download


    **Authentication**: Add `Authorization: Bearer YOUR_API_KEY` to the request
    header


    **API Key configuration**: In your APIYI console, set the group to **Sora2官转
    (Sora2 Official)** and billing mode to **usage-based**


    **Get API Key**: Visit the [APIYI Console](https://api.apiyi.com/token) to
    create a token
  version: 1.0.0
servers:
  - url: https://api.apiyi.com
    description: Primary endpoint
  - url: https://vip.apiyi.com
    description: Backup endpoint
security:
  - bearerAuth: []
paths:
  /v1/videos:
    post:
      tags:
        - Video Generation
      summary: 'Image-to-video: submit a video generation task from a reference image'
      description: >
        Submits a Sora 2 image-to-video task. The client must use
        multipart/form-data to upload one reference image plus text fields.


        - Required: `model`, `prompt`, `input_reference`

        - Optional: `seconds` (default `"4"`), `size` (default `"720x1280"`)

        - **`input_reference` image dimensions must equal `size`** — pre-crop
        with ffmpeg / Pillow before upload

        - Response shape and polling/download flow are identical to
        text-to-video
      operationId: generateSora2ImageToVideoEn
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/Sora2ImageToVideoRequest'
            example:
              model: sora-2
              prompt: >-
                Animate this scene: gentle waves lapping, leaves swaying,
                cinematic camera push-in
              seconds: '8'
              size: 1280x720
      responses:
        '200':
          description: Task submitted, returns video_id with queued status
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Sora2VideoTask'
        '400':
          description: >-
            Invalid parameters (most common: reference image dimensions mismatch
            with size; also unsupported file format, seconds out of range)
        '401':
          description: Unauthorized — invalid API Key
        '403':
          description: Content policy / not on usage-based billing / group is not Sora2官转
        '413':
          description: Uploaded image too large
        '429':
          description: Rate limit exceeded or insufficient balance
        '500':
          description: Upstream OpenAI gateway error — retry 1–2 times
      security:
        - bearerAuth: []
components:
  schemas:
    Sora2ImageToVideoRequest:
      type: object
      required:
        - model
        - prompt
        - input_reference
      properties:
        model:
          type: string
          description: >-
            Model ID. `sora-2` supports 720p only; `sora-2-pro` supports 720p /
            1024p / 1080p
          enum:
            - sora-2
            - sora-2-pro
          default: sora-2
        prompt:
          type: string
          description: >-
            Video generation prompt. **Focus on how the image should animate**:
            camera motion, object motion, lighting changes
          example: >-
            Animate this scene: gentle waves lapping, leaves swaying, cinematic
            camera push-in
        seconds:
          type: string
          description: 'Video duration as **string enum**: `"4"` / `"8"` / `"12"`'
          enum:
            - '4'
            - '8'
            - '12'
          default: '4'
        size:
          type: string
          description: >
            Output resolution. **Must exactly match the `input_reference` image
            dimensions**:


            - `sora-2` (720p only): `720x1280` / `1280x720`

            - `sora-2-pro` additionally: `1024x1792` / `1792x1024` / `1080x1920`
            / `1920x1080`
          enum:
            - 720x1280
            - 1280x720
            - 1024x1792
            - 1792x1024
            - 1080x1920
            - 1920x1080
          default: 720x1280
        input_reference:
          type: string
          format: binary
          description: >
            Reference image file used as the video's starting frame / visual
            anchor.


            - Accepted formats: `image/jpeg` / `image/png` / `image/webp`

            - **Dimensions must equal `size`**, otherwise you get `Inpaint image
            must match the requested width and height`

            - Only one file is supported; field name is fixed as
            `input_reference`
    Sora2VideoTask:
      type: object
      properties:
        id:
          type: string
          description: Task ID for subsequent polling and download
          example: video_abc123def456
        object:
          type: string
          description: Object type, fixed `video`
          example: video
        model:
          type: string
          description: Model ID used for this task
          example: sora-2
        status:
          type: string
          description: |
            Task status:
            - `queued` — submitted, waiting in queue
            - `in_progress` — generating
            - `completed` — done, ready to download (`/v1/videos/{id}/content`)
            - `failed` — failed (not billed), safe to retry
          enum:
            - queued
            - in_progress
            - completed
            - failed
          example: queued
        progress:
          type: integer
          description: Generation progress percentage (0–100), not strictly linear
          example: 0
        created_at:
          type: integer
          description: Task creation Unix timestamp (seconds)
          example: 1712697600
        completed_at:
          type: integer
          description: >-
            Task completion Unix timestamp (seconds), present only on completed
            status
          example: 1712697900
        size:
          type: string
          description: Actual output resolution (matches the requested `size`)
          example: 1280x720
        seconds:
          type: string
          description: Actual duration generated (matches the requested `seconds`)
          example: '8'
        quality:
          type: string
          description: Quality tier (`standard` for sora-2, `high` for sora-2-pro)
          example: standard
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: >-
        API Key from the APIYI console (must use Sora2官转 group + usage-based
        billing)

````