> ## 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 для генерации видео из изображения

> Справочник API Sora 2 для генерации видео из изображения и live-песочница — multipart-загрузка input_reference для анимации статичного изображения.

<Info>
  Интерактивная Playground справа поддерживает отладку в реальном времени. Укажите ваш API-ключ в поле **Authorization** (формат: `Bearer sk-xxx`), загрузите опорное изображение, введите prompt, выберите model / size / seconds и отправьте.
</Info>

<Tip>
  **Область применения**: На этой странице описано «генерирование видео по опорному изображению» — загрузите одно изображение как стартовый кадр / визуальную опору, чтобы анимировать статичные визуальные материалы. Если опорное изображение не требуется, используйте [эндпоинт Text-to-Video](/ru/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)
  * Поддерживается только один файл; имя поля фиксировано как `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 (сырые запросы + 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 | Да          | —            | `sora-2` (только для 720p) или `sora-2-pro` (для уровней 720p / 1024p / 1080p)                                                                       |
| `prompt`          | string | Да          | —            | Описание видео; сосредоточьтесь на **том, как должно анимироваться статичное изображение** (движение камеры, движение объектов, изменения освещения) |
| `seconds`         | string | Нет         | `"4"`        | Длительность в виде **строкового enum**: `"4"` / `"8"` / `"12"`                                                                                      |
| `size`            | string | Нет         | `720x1280`   | Выходное разрешение, **должно точно совпадать с размерами изображения `input_reference`**                                                            |
| `input_reference` | file   | Да          | —            | Файл референсного изображения: `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")
    ```

    Или однострочный 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;">
    Референсное изображение уже задает визуальную часть. Промпт должен быть сосредоточен на **том, как это должно анимироваться**: движение камеры вперед/назад, движение объектов, изменения освещения, выражения лица персонажей и т. д. Пример: `"Camera slowly pushes in, leaves gently swaying, sunlight flickering through branches"`.
  </Step>
</Steps>

## Формат ответа

Формат ответа идентичен [Text-to-Video](/ru/api-capabilities/sora-2/text-to-video#response-format): при отправке возвращаются `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>
  Image-to-video и text-to-video имеют **одинаковую тарификацию по секундам** (оплата списывается по `seconds`); загрузка опорного изображения не требует доплаты. См. [таблицу тарификации](/ru/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)

````