> ## 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 레퍼런스 및 라이브 플레이그라운드 — JSON 요청 본문, 비동기 3단계 플로우, 유연한 4 / 8 / 12초 길이.

<Info>
  오른쪽의 대화형 플레이그라운드는 실시간 디버깅을 지원합니다. **Authorization** 필드에 API 키를 설정합니다(형식: `Bearer sk-xxx`), prompt를 입력하고 model / size / seconds를 선택한 다음 전송합니다.
</Info>

<Tip>
  **범위**: 이 페이지는 텍스트만으로 동영상을 생성하는 기능만 다룹니다. `input_reference`가 없으며, 요청 본문은 `application/json`입니다. 참조 이미지에서 동영상으로 애니메이션을 만들려면 [이미지-투-비디오 엔드포인트](/ko/api-capabilities/sora-2/image-to-video)를 사용합니다(동일한 경로 + multipart 업로드).
</Tip>

<Warning>
  **⚠️ 3단계 비동기 흐름 — 이 페이지는 1단계(제출)만 다룹니다**

  * **1단계(이 페이지)**: `POST /v1/videos` → `video_id` + `status: "queued"`를 반환합니다
  * **2단계**: `GET /v1/videos/{video_id}`를 `status: "completed"`가 될 때까지 폴링합니다
  * **3단계**: `GET /v1/videos/{video_id}/content`에서 다운로드합니다(MP4 파일을 반환합니다)

  POST 자체는 몇 초가 걸리며 생성이 끝날 때까지 **블로킹하지 않습니다**. 전체 흐름은 아래 Python 샘플에 나와 있습니다.
</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 task
video = client.videos.create(
    model="sora-2",
    prompt="A golden retriever running on the beach at sunset, cinematic, golden hour, slow motion",
    seconds="8",
    size="1280x720"
)
print(f"Video ID: {video.id}, status: {video.status}")

# Step 2: Poll status
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 the video
content = client.videos.download_content(video.id)
content.write_to_file("output.mp4")
print("Saved: output.mp4")
```

### Python (Raw requests)

```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: Submit (JSON body)
resp = requests.post(
    f"{BASE_URL}/videos",
    headers={**HEADERS, "Content-Type": "application/json"},
    json={
        "model": "sora-2",
        "prompt": "A serene Japanese garden with cherry blossoms, koi pond, traditional bridge, golden hour, ultra detailed",
        "seconds": "8",
        "size": "1280x720"
    },
    timeout=30  # The POST is just enqueueing; 30 seconds is enough
).json()
video_id = resp["id"]
print(f"Video ID: {video_id}, status: {resp['status']}")

# Step 2: Poll (max wait 15 minutes)
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: Submit the task */}
curl -X POST "https://api.apiyi.com/v1/videos" \
  -H "Authorization: Bearer sk-your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "sora-2",
    "prompt": "A futuristic cityscape at night with neon lights and flying vehicles, cyberpunk style, cinematic",
    "seconds": "8",
    "size": "1280x720"
  }'

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

{/* Step 3: Download the video file */}
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)

```javascript theme={null}
import fs from 'node:fs';

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

// Step 1: Submit
const submitResp = await fetch(`${BASE_URL}/videos`, {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${API_KEY}`
    },
    body: JSON.stringify({
        model: 'sora-2',
        prompt: 'Aerial drone shot over snowy mountain range at sunrise, cinematic, ultra wide',
        seconds: '8',
        size: '1280x720'
    })
});
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 statusResp = await fetch(`${BASE_URL}/videos/${videoId}`, {
        headers: { 'Authorization': `Bearer ${API_KEY}` }
    });
    const data = await statusResp.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}` }
});
const buffer = Buffer.from(await contentResp.arrayBuffer());
fs.writeFileSync('output.mp4', buffer);
console.log('Saved: output.mp4');
```

### 브라우저 JavaScript

```javascript theme={null}
{/* Demo only; route through your backend in production to avoid leaking the API key. Browsers also aren't ideal for downloading large video files. */}
const submitResp = await fetch('https://api.apiyi.com/v1/videos', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
        'Authorization': 'Bearer sk-your-api-key'
    },
    body: JSON.stringify({
        model: 'sora-2',
        prompt: 'Watercolor northern lights over snowy mountains, gentle motion',
        seconds: '4',
        size: '720x1280'
    })
});
const { id } = await submitResp.json();
console.log('Video ID:', id);

{/* After polling completes, hand the video URL to a backend proxy for download and serve it back to the client. */}
```

## 매개변수 빠른 참조

| 매개변수      | 유형     | 필수  | 기본값        | 설명                                                                                              |
| --------- | ------ | --- | ---------- | ----------------------------------------------------------------------------------------------- |
| `model`   | string | Yes | —          | `sora-2` (720p 전용) 또는 `sora-2-pro` (720p / 1024p / 1080p 티어)                                    |
| `prompt`  | string | Yes | —          | 동영상 설명; 장면, 카메라 움직임, 스타일, 조명을 자세히 설명합니다                                                         |
| `seconds` | string | No  | `"4"`      | 기간은 **문자열 enum**입니다: `"4"` / `"8"` / `"12"` (**숫자가 아닙니다**)                                      |
| `size`    | string | No  | `720x1280` | 출력 해상도; 모델이 지원하는 티어와 일치해야 합니다([기술 사양](/ko/api-capabilities/sora-2/overview#technical-specs) 참조) |

<Tip>
  자세한 매개변수 제약, 허용 값, 예시는 오른쪽 플레이그라운드에서 확인할 수 있습니다 — 모든 enum 필드에는 드롭다운이 제공됩니다. **이미지-투-동영상 매개변수(`input_reference` 업로드)의 경우 [이미지-투-동영상 엔드포인트](/ko/api-capabilities/sora-2/image-to-video)를 참조하십시오**.
</Tip>

## 응답 형식

### 단계 1 — 즉시 제출 응답

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

### 단계 2 — 실행 중 폴링

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

### 단계 2 — 완료 후 폴링

```json theme={null}
{
  "id": "video_abc123def456",
  "object": "video",
  "model": "sora-2",
  "status": "completed",
  "progress": 100,
  "created_at": 1712697600,
  "completed_at": 1712697900,
  "size": "1280x720",
  "seconds": "8"
}
```

<Warning>
  **⚠️ 응답 필드 주의사항**

  * **직접 `video_url` 필드 없음** — 동영상 파일은 `GET /v1/videos/{id}/content`에서 다운로드해야 합니다(`video/mp4` 바이너리 스트림을 반환합니다). **JSON 응답에서 CDN URL이 있을 것이라고 기대하지 마십시오.**
  * `progress`은 엄격하게 선형적이지 않습니다 — 건너뛸 수 있습니다(예: 0 → 45 → 80 → 100)
  * `status: "failed"`에서는 `error` 필드가 항상 존재하지 않습니다 — 대부분의 실패는 콘텐츠 정책 또는 용량 문제이며, **그냥 다시 시도하거나 프롬프트를 수정하십시오**
  * OpenAI의 동영상 콘텐츠는 **1일 동안만** 보관됩니다 — `/content`은 만료 후 404를 반환합니다
</Warning>

<Info>
  이 엔드포인트는 비동기 작업 진입점입니다. **작업이 완료되면 과금은 `seconds` 요율로 정산됩니다**([가격표](/ko/api-capabilities/sora-2/overview#pricing) 참조). POST 제출, 상태 폴링, 콘텐츠 다운로드 자체는 **과금되지 않으며**, 실패한 작업도 **과금되지 않습니다**.
</Info>


## OpenAPI

````yaml api-reference/sora-2-text-to-video-openapi-en.yaml POST /v1/videos
openapi: 3.1.0
info:
  title: Sora 2 Text-to-Video API
  description: >
    OpenAI Sora 2 / Sora 2 Pro video generation — text-to-video endpoint (no
    `input_reference`, JSON request body).


    - Two active models: `sora-2` (720p, \$0.10/sec) and `sora-2-pro` (720p /
    1024p / 1080p tiers)

    - Duration: `"4"` / `"8"` / `"12"` seconds (**string enum**)

    - Outputs video with synchronized audio (ambient sound, dialogue, score)

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

    - Videos are retained on OpenAI servers for 1 day only — download
    immediately after completion

    - For image-conditioned generation, see the [Image-to-Video
    endpoint](/en/api-capabilities/sora-2/image-to-video) (same path + multipart
    `input_reference`)


    **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**, otherwise you'll get
    403


    **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: 'Text-to-video: submit a video generation task from text'
      description: >
        Submits a Sora 2 video generation task (async). Returns `video_id` and
        `status: "queued"`.


        - Required: `model`, `prompt`

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

        - **Response does not include the video file** — poll `GET
        /v1/videos/{video_id}` until `status: "completed"`, then call `GET
        /v1/videos/{video_id}/content` to download the MP4

        - Typical generation time: sora-2 4 sec ≈ 3 minutes; sora-2-pro 12 sec
        at 1080p ≈ 8–10 minutes

        - For image-conditioned generation, use the [Image-to-Video
        endpoint](/en/api-capabilities/sora-2/image-to-video) instead
      operationId: generateSora2TextToVideoEn
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Sora2TextToVideoRequest'
            example:
              model: sora-2
              prompt: >-
                A golden retriever running on the beach at sunset, cinematic,
                golden hour, slow motion
              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 (seconds not in 4/8/12, size not supported by the
            model, prompt missing, etc.)
        '401':
          description: Unauthorized — invalid API Key
        '403':
          description: Content policy / not on usage-based billing / group is not Sora2官转
        '429':
          description: Rate limit exceeded or insufficient balance
        '500':
          description: Upstream OpenAI gateway error — retry 1–2 times
      security:
        - bearerAuth: []
components:
  schemas:
    Sora2TextToVideoRequest:
      type: object
      required:
        - model
        - prompt
      properties:
        model:
          type: string
          description: >-
            Model ID. `sora-2` supports 720p only; `sora-2-pro` supports 720p /
            1024p / 1080p tiers
          enum:
            - sora-2
            - sora-2-pro
          default: sora-2
        prompt:
          type: string
          description: >-
            Video generation prompt; describe scene, camera motion, style,
            lighting, and character actions in detail
          example: >-
            A serene Japanese garden with cherry blossoms, koi pond, traditional
            bridge, golden hour, ultra detailed
        seconds:
          type: string
          description: >
            Video duration as a **string enum** (not a number):

            - `"4"` — 4 seconds (default), ideal for short demos, single shots,
            fast prompt iteration

            - `"8"` — 8 seconds, standard short-form video, most common

            - `"12"` — 12 seconds, long shots and continuous action


            Passing `"10"` / `"15"` or the integer `4` returns 400
          enum:
            - '4'
            - '8'
            - '12'
          default: '4'
        size:
          type: string
          description: >
            Output resolution. **`sora-2` and `sora-2-pro` support different
            tiers**:


            - `sora-2` (720p only): `720x1280` (portrait, default) / `1280x720`
            (landscape)

            - `sora-2-pro` additionally supports:
              - `1024x1792` / `1792x1024` (1024p, \$0.50/sec)
              - `1080x1920` / `1920x1080` (1080p, \$0.70/sec)

            Passing 1024p / 1080p sizes to `sora-2` returns 400
          enum:
            - 720x1280
            - 1280x720
            - 1024x1792
            - 1792x1024
            - 1080x1920
            - 1920x1080
          default: 720x1280
    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)

````