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

# Async API (Outdated Legacy Route)

> Legacy Sora 2 Async API documentation for historical troubleshooting only; use Sora Official API Forwarding for current video generation.

<Warning>
  This is legacy Sora2 route documentation and is now outdated. Use [Sora Official API Forwarding](/en/api-capabilities/sora2/official-forward) for the currently available video route.
</Warning>

<Info>
  **Async Endpoint:** `https://api2.laozhang.ai/v1/videos`

  **Call Method:** Three steps (Create Task → Query Status → Get Video)

  **Advantages:** More Stable | Task Queue | Long-running Tasks Support
</Info>

<Warning>
  **Important Difference - Image-to-Video Upload Method**

  Async API differs significantly from Sync API for image-to-video:

  | Feature        | Sync API               | Async API           |
  | -------------- | ---------------------- | ------------------- |
  | Image Upload   | Supports URL or Base64 | **Local file only** |
  | Request Format | JSON                   | multipart/form-data |

  **If you have an image URL**: Download it locally first, then upload via Async API.

  **Example Comparison**:

  ```python theme={null}
  {/* Sync API - Supports URL */}
  "image_url": {"url": "https://example.com/image.png"}

  {/* Async API - Local file only */}
  files = {'input_reference': open('/path/to/image.png', 'rb')}
  ```
</Warning>

## Why Choose Async API?

<CardGroup cols={2}>
  <Card title="Higher Stability" icon="shield-check">
    Task queue based, avoiding long connection timeout issues
  </Card>

  <Card title="No Charge on Failure ⭐" icon="badge-dollar-sign">
    **Key Advantage**: No charges for failures of any kind

    * ✓ Content violation → No charge
    * ✓ Queue timeout → No charge
    * ✓ Generation failure → No charge

    Sync API charges as long as request succeeds (HTTP 200), even if generation ultimately fails!
  </Card>

  <Card title="Flexible Polling" icon="refresh-ccw">
    Query task status and progress anytime
  </Card>

  <Card title="Parameterized Control" icon="sliders-horizontal">
    Specify resolution and duration via parameters, more flexible
  </Card>
</CardGroup>

## Sync vs Async Comparison

| Feature            | Sync API                             | Async API (Recommended)                   |
| ------------------ | ------------------------------------ | ----------------------------------------- |
| **Call Method**    | Single request, wait for completion  | Create task then poll for results         |
| **Endpoint**       | `/v1/chat/completions`               | `/v1/videos`                              |
| **Resolution**     | Model name (`sora_video2-landscape`) | Parameter (`size: "1280x720"`)            |
| **Duration**       | Model name (`sora_video2-15s`)       | Parameter (`seconds: "15"`)               |
| **Image-to-Video** | Supports image URL                   | Local file upload only                    |
| **Failed Billing** | Charged even if generation fails     | ⭐ No charge on failure                    |
| **Stability**      | Depends on long connection           | ⭐⭐⭐⭐⭐ More Stable                         |
| **Progress**       | Stream output                        | Poll for progress percentage              |
| **Timeout**        | Needs long timeout setting           | Task runs independently, no timeout limit |
| **Use Cases**      | Quick testing, real-time feedback    | Production environment, batch generation  |

<Tip>
  **Recommended to use Async API**, especially in production environments or when batch generating videos, for better stability.
</Tip>

## Quick Start

Async calling consists of three steps:

<Steps>
  <Step title="Create Video Task">
    POST request to create task, get task ID
  </Step>

  <Step title="Query Task Status">
    Periodically poll to check generation progress
  </Step>

  <Step title="Download Video">
    Retrieve video file after task completes
  </Step>
</Steps>

### Complete Example

<CodeGroup>
  ```bash Step 1: Create Task (Text-to-Video) theme={null}
  curl -X POST "https://api2.laozhang.ai/v1/videos" \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "model": "sora-2",
      "prompt": "A cute cat playing with a ball in a sunny garden",
      "size": "1280x720",
      "seconds": "15"
    }'

  # Response example
  {
    "id": "video_abc123",
    "object": "task",
    "model": "sora-2",
    "status": "submitted",
    "created_at": 1760945092,
    "expires_at": 1761031492
  }
  ```

  ```bash Step 1: Create Task (Image-to-Video) theme={null}
  curl -X POST "https://api2.laozhang.ai/v1/videos" \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -F "model=sora-2" \
    -F "prompt=Animate this scene with natural dynamic effects" \
    -F "input_reference=@/path/to/image.png" \
    -F "size=1280x720" \
    -F "seconds=10"

  # Response example
  {
    "id": "video_def456",
    "object": "task",
    "model": "sora-2",
    "status": "submitted",
    "created_at": 1760945092,
    "expires_at": 1761031492
  }
  ```

  ```bash Step 2: Query Status theme={null}
  curl -X GET "https://api2.laozhang.ai/v1/videos/video_abc123" \
    -H "Authorization: Bearer YOUR_API_KEY"

  # In-progress response
  {
    "id": "video_abc123",
    "object": "video",
    "model": "sora-2",
    "status": "in_progress",
    "progress": 65,
    "created_at": 1760945092
  }

  # Completed response
  {
    "id": "video_abc123",
    "object": "video",
    "model": "sora-2",
    "status": "completed",
    "progress": 100,
    "url": "/v1/videos/video_abc123/content",
    "created_at": 1760945092,
    "completed_at": 1760945379
  }
  ```

  ```bash Step 3: Download Video theme={null}
  curl -X GET "https://api2.laozhang.ai/v1/videos/video_abc123/content" \
    -H "Authorization: Bearer YOUR_API_KEY" \
    --output video.mp4
  ```
</CodeGroup>

## API Endpoints

### 1. Create Video Task

<Info>
  **POST** `https://api2.laozhang.ai/v1/videos`

  Create a new video generation task
</Info>

#### Request Parameters

| Parameter         | Type   | Required                    | Options                    | Description                                                                                                                                    |
| ----------------- | ------ | --------------------------- | -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `model`           | string | ✓                           | `"sora-2"`, `"sora-2-pro"` | Model name                                                                                                                                     |
| `prompt`          | string | ✓                           | -                          | Video generation prompt                                                                                                                        |
| `size`            | string | ✓                           | `"1280x720"`, `"720x1280"` | Video resolution (landscape/portrait)                                                                                                          |
| `seconds`         | string | ✓                           | `"10"`, `"15"`             | Video duration (seconds)                                                                                                                       |
| `input_reference` | file   | Required for image-to-video | -                          | Local image file (local upload only)<br />Supported formats: JPG, PNG, WebP<br />Recommended size: 1280x720<br />Recommended file size: \< 5MB |

<Note>
  **Model Selection**

  * `sora-2`: Base model, 720P resolution, extremely high stability, \$0.15/call
  * `sora-2-pro`: HD model, 1080P resolution, generation time \~10 minutes, \$0.8/call
</Note>

#### Response Fields

| Field        | Type    | Description                                   |
| ------------ | ------- | --------------------------------------------- |
| `id`         | string  | Unique task identifier for subsequent queries |
| `object`     | string  | Fixed as `"task"`                             |
| `model`      | string  | Model used                                    |
| `status`     | string  | Task status: `"submitted"`                    |
| `created_at` | integer | Creation timestamp                            |
| `expires_at` | integer | Expiration timestamp (24 hours later)         |

### 2. Query Task Status

<Info>
  **GET** `https://api2.laozhang.ai/v1/videos/{video_id}`

  Query current status and progress of video generation task
</Info>

#### Path Parameters

| Parameter  | Type   | Required | Description                         |
| ---------- | ------ | -------- | ----------------------------------- |
| `video_id` | string | ✓        | Task ID returned when creating task |

#### Response Fields

| Field          | Type    | Description                                  |
| -------------- | ------- | -------------------------------------------- |
| `id`           | string  | Task ID                                      |
| `object`       | string  | Fixed as `"video"`                           |
| `model`        | string  | Model used                                   |
| `status`       | string  | Task status (see below)                      |
| `progress`     | integer | Progress percentage (0-100)                  |
| `url`          | string  | Video download path (completed status only)  |
| `created_at`   | integer | Creation timestamp                           |
| `completed_at` | integer | Completion timestamp (completed status only) |
| `error`        | object  | Error information (failed status only)       |

#### Task Status

| Status        | Description                       | Progress |
| ------------- | --------------------------------- | -------- |
| `submitted`   | Submitted, waiting for processing | 0%       |
| `in_progress` | Generating                        | 1-99%    |
| `completed`   | Generation completed              | 100%     |
| `failed`      | Generation failed                 | -        |

### 3. Get Video Content

<Info>
  **GET** `https://api2.laozhang.ai/v1/videos/{video_id}/content`

  Download completed video file
</Info>

#### Path Parameters

| Parameter  | Type   | Required | Description |
| ---------- | ------ | -------- | ----------- |
| `video_id` | string | ✓        | Task ID     |

#### Response

Returns binary stream of video file (MP4 format)

<Warning>
  **Important Notice**

  Video files are stored for **24 hours** only. Please download and save to local storage promptly!
</Warning>

## Complete Code Examples

### Python Example (with Polling Logic)

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

API_KEY = "YOUR_API_KEY"
BASE_URL = "https://api2.laozhang.ai/v1"

# Step 1: Create video task (text-to-video)
def create_video_task(prompt, size="1280x720", seconds="15"):
    """Create video generation task"""
    url = f"{BASE_URL}/videos"
    headers = {
        "Authorization": f"Bearer {API_KEY}",
        "Content-Type": "application/json"
    }
    data = {
        "model": "sora-2",
        "prompt": prompt,
        "size": size,
        "seconds": seconds
    }

    response = requests.post(url, headers=headers, json=data)
    response.raise_for_status()
    return response.json()

# Step 1: Create video task (image-to-video)
def create_video_task_with_image(prompt, image_path, size="1280x720", seconds="10"):
    """Create image-to-video task"""
    url = f"{BASE_URL}/videos"
    headers = {"Authorization": f"Bearer {API_KEY}"}

    # Ensure file remains open during request
    with open(image_path, 'rb') as image_file:
        files = {
            # Specify filename and MIME type for proper upload
            'input_reference': ('image.png', image_file, 'image/png')
        }
        data = {
            'model': 'sora-2',
            'prompt': prompt,
            'size': size,
            'seconds': seconds
        }

        # Send request within with block to keep file handle valid
        response = requests.post(url, headers=headers, files=files, data=data)
        response.raise_for_status()
        return response.json()

# Step 2: Poll for status
def wait_for_video(video_id, poll_interval=5, timeout=600):
    """Wait for video generation to complete"""
    url = f"{BASE_URL}/videos/{video_id}"
    headers = {"Authorization": f"Bearer {API_KEY}"}
    start_time = time.time()

    while True:
        # Check timeout
        if time.time() - start_time > timeout:
            raise TimeoutError(f"Video generation timeout ({timeout}s)")

        # Query status
        response = requests.get(url, headers=headers)
        response.raise_for_status()
        task = response.json()

        status = task["status"]
        progress = task.get("progress", 0)

        print(f"Status: {status}, Progress: {progress}%")

        if status == "completed":
            return task
        elif status == "failed":
            error = task.get("error", {})
            raise Exception(f"Generation failed: {error.get('message', 'Unknown error')}")

        # Wait before retry
        time.sleep(poll_interval)

# Step 3: Download video
def download_video(video_id, save_path="video.mp4"):
    """Download generated video"""
    url = f"{BASE_URL}/videos/{video_id}/content"
    headers = {"Authorization": f"Bearer {API_KEY}"}

    response = requests.get(url, headers=headers, stream=True)
    response.raise_for_status()

    with open(save_path, 'wb') as f:
        for chunk in response.iter_content(chunk_size=8192):
            if chunk:
                f.write(chunk)

    print(f"Video saved to: {save_path}")

# Complete workflow (text-to-video)
def generate_video_async(prompt, size="1280x720", seconds="15"):
    """Complete async video generation workflow"""
    print("1. Creating video task...")
    task = create_video_task(prompt, size, seconds)
    video_id = task["id"]
    print(f"   Task ID: {video_id}")

    print("\n2. Waiting for video generation...")
    completed_task = wait_for_video(video_id)
    duration = completed_task['completed_at'] - completed_task['created_at']
    print(f"   Completed! Duration: {duration}s")

    print("\n3. Downloading video...")
    download_video(video_id)
    print("\n✅ Done!")

# Complete workflow (image-to-video)
def generate_video_from_image_async(image_path, prompt, size="1280x720", seconds="10"):
    """Complete workflow for generating video from local image"""
    import os

    if not os.path.exists(image_path):
        print(f"Error: Image file not found {image_path}")
        return None

    print(f"1. Uploading image and creating task... ({image_path})")
    task = create_video_task_with_image(prompt, image_path, size, seconds)
    video_id = task["id"]
    print(f"   Task ID: {video_id}")

    print("\n2. Waiting for video generation...")
    completed_task = wait_for_video(video_id)
    duration = completed_task['completed_at'] - completed_task['created_at']
    print(f"   Completed! Duration: {duration}s")

    print("\n3. Downloading video...")
    download_video(video_id, save_path=f"image_to_video_{video_id}.mp4")
    print("\n✅ Done!")

    return video_id

# Usage examples
if __name__ == "__main__":
    # Text-to-video
    generate_video_async(
        prompt="A cute cat playing with a ball in a sunny garden",
        size="1280x720",  # Landscape
        seconds="15"       # 15 seconds
    )

    # Image-to-video
    generate_video_from_image_async(
        image_path="/path/to/your/image.png",
        prompt="Animate this scene with natural dynamic effects",
        size="1280x720",  # Landscape
        seconds="10"       # 10 seconds
    )
```

### JavaScript/Node.js Example

```javascript theme={null}
const axios = require('axios');
const fs = require('fs');
const FormData = require('form-data');

const API_KEY = 'YOUR_API_KEY';
const BASE_URL = 'https://api2.laozhang.ai/v1';

// Create video task (text-to-video)
async function createVideoTask(prompt, size = '1280x720', seconds = '15') {
  const response = await axios.post(`${BASE_URL}/videos`, {
    model: 'sora-2',
    prompt,
    size,
    seconds
  }, {
    headers: { 'Authorization': `Bearer ${API_KEY}` }
  });

  return response.data;
}

// Create video task (image-to-video)
async function createVideoTaskWithImage(prompt, imagePath, size = '1280x720', seconds = '10') {
  const formData = new FormData();
  formData.append('model', 'sora-2');
  formData.append('prompt', prompt);
  formData.append('size', size);
  formData.append('seconds', seconds);
  formData.append('input_reference', fs.createReadStream(imagePath));

  const response = await axios.post(`${BASE_URL}/videos`, formData, {
    headers: {
      'Authorization': `Bearer ${API_KEY}`,
      ...formData.getHeaders()
    }
  });

  return response.data;
}

// Poll for status
async function waitForVideo(videoId, pollInterval = 5000, timeout = 600000) {
  const startTime = Date.now();

  while (true) {
    // Check timeout
    if (Date.now() - startTime > timeout) {
      throw new Error(`Video generation timeout (${timeout/1000}s)`);
    }

    // Query status
    const response = await axios.get(`${BASE_URL}/videos/${videoId}`, {
      headers: { 'Authorization': `Bearer ${API_KEY}` }
    });

    const task = response.data;
    const { status, progress = 0 } = task;

    console.log(`Status: ${status}, Progress: ${progress}%`);

    if (status === 'completed') {
      return task;
    } else if (status === 'failed') {
      const error = task.error || {};
      throw new Error(`Generation failed: ${error.message || 'Unknown error'}`);
    }

    // Wait before retry
    await new Promise(resolve => setTimeout(resolve, pollInterval));
  }
}

// Download video
async function downloadVideo(videoId, savePath = 'video.mp4') {
  const response = await axios.get(
    `${BASE_URL}/videos/${videoId}/content`,
    {
      headers: { 'Authorization': `Bearer ${API_KEY}` },
      responseType: 'stream'
    }
  );

  const writer = fs.createWriteStream(savePath);
  response.data.pipe(writer);

  return new Promise((resolve, reject) => {
    writer.on('finish', () => {
      console.log(`Video saved to: ${savePath}`);
      resolve();
    });
    writer.on('error', reject);
  });
}

// Complete workflow (text-to-video)
async function generateVideoAsync(prompt, size = '1280x720', seconds = '15') {
  try {
    console.log('1. Creating video task...');
    const task = await createVideoTask(prompt, size, seconds);
    const videoId = task.id;
    console.log(`   Task ID: ${videoId}`);

    console.log('\n2. Waiting for video generation...');
    const completedTask = await waitForVideo(videoId);
    const duration = completedTask.completed_at - completedTask.created_at;
    console.log(`   Completed! Duration: ${duration}s`);

    console.log('\n3. Downloading video...');
    await downloadVideo(videoId);
    console.log('\n✅ Done!');

  } catch (error) {
    console.error('Error:', error.message);
  }
}

// Complete workflow (image-to-video)
async function generateVideoFromImageAsync(imagePath, prompt, size = '1280x720', seconds = '10') {
  try {
    if (!fs.existsSync(imagePath)) {
      console.error(`Error: Image file not found ${imagePath}`);
      return null;
    }

    console.log(`1. Uploading image and creating task... (${imagePath})`);
    const task = await createVideoTaskWithImage(prompt, imagePath, size, seconds);
    const videoId = task.id;
    console.log(`   Task ID: ${videoId}`);

    console.log('\n2. Waiting for video generation...');
    const completedTask = await waitForVideo(videoId);
    const duration = completedTask.completed_at - completedTask.created_at;
    console.log(`   Completed! Duration: ${duration}s`);

    console.log('\n3. Downloading video...');
    await downloadVideo(videoId, `image_to_video_${videoId}.mp4`);
    console.log('\n✅ Done!');

    return videoId;

  } catch (error) {
    console.error('Error:', error.message);
    return null;
  }
}

// Usage examples
// Text-to-video
generateVideoAsync(
  'A cute cat playing with a ball in a sunny garden',
  '1280x720',  // Landscape
  '15'         // 15 seconds
);

// Image-to-video
generateVideoFromImageAsync(
  '/path/to/your/image.png',
  'Animate this scene with natural dynamic effects',
  '1280x720',  // Landscape
  '10'         // 10 seconds
);
```

## Best Practices

<AccordionGroup>
  <Accordion title="Polling Interval" icon="clock">
    **Recommended polling interval: 3-5 seconds**

    ```python theme={null}
    # Recommended
    poll_interval = 5  # Query every 5 seconds

    # Not recommended
    poll_interval = 1  # Too frequent, wastes requests
    poll_interval = 30 # Too slow, poor user experience
    ```

    **Reasoning:**

    * Video generation typically takes 2-5 minutes
    * 3-5 seconds provides timely progress feedback
    * Avoids excessive requests
  </Accordion>

  <Accordion title="Timeout Handling" icon="timer">
    **Recommended timeout: 10 minutes (600 seconds)**

    ```python theme={null}
    def wait_for_video(video_id, timeout=600):
        start_time = time.time()

        while True:
            if time.time() - start_time > timeout:
                # Timeout handling
                print(f"Task {video_id} timeout, can continue querying later")
                break

            # Query logic...
    ```

    **Note:**

    * Task timeout doesn't auto-cancel
    * Can continue querying same video\_id later
    * Task validity period is 24 hours
  </Accordion>

  <Accordion title="Retry Strategy" icon="refresh-cw">
    **Suggested retry logic:**

    ```python theme={null}
    def create_video_with_retry(prompt, max_retries=3):
        for i in range(max_retries):
            try:
                return create_video_task(prompt)
            except Exception as e:
                if i < max_retries - 1:
                    print(f"Failed, retrying in 5s... ({i+1}/{max_retries})")
                    time.sleep(5)
                else:
                    raise
    ```

    **Retry scenarios:**

    * ✓ Network errors → Retry
    * ✓ Service busy (503) → Retry
    * ✗ Content violation → Don't retry, modify prompt
    * ✗ Insufficient balance → Don't retry, confirm account credit first
  </Accordion>

  <Accordion title="Batch Generation" icon="list">
    **Concurrency control recommendations:**

    ```python theme={null}
    import concurrent.futures

    def batch_generate_videos(prompts, max_workers=5):
        """Batch generate videos with concurrency control"""
        with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor:
            # Create all tasks
            future_to_prompt = {
                executor.submit(create_video_task, prompt): prompt
                for prompt in prompts
            }

            video_ids = []
            for future in concurrent.futures.as_completed(future_to_prompt):
                task = future.result()
                video_ids.append(task['id'])

            # Concurrently wait for all tasks
            futures = [executor.submit(wait_for_video, vid) for vid in video_ids]
            results = [f.result() for f in concurrent.futures.as_completed(futures)]

            return results
    ```

    **Recommendations:**

    * Creating tasks: High concurrency OK (10-30)
    * Querying status: Recommended concurrency ≤ 10
    * Downloading videos: Recommended concurrency ≤ 5
  </Accordion>
</AccordionGroup>

## Pricing

<Info>
  Async API has **exactly the same pricing** as Sync API, billed per call.
</Info>

| Parameter Combination          | Price       |
| ------------------------------ | ----------- |
| 10s video (landscape/portrait) | \$0.15/call |
| 15s video (landscape/portrait) | \$0.15/call |
| **HD (sora-2-pro)**            | \$0.8/call  |

**Billing Rules:**

* ✓ Charges are based on console order status and the returned generation result
* ✗ Failed, timeout, and cancelled tasks should be checked in console order status
* ✗ Content safety issues should be checked in console order status (important difference from Sync API⭐)
* ✗ Status query billing follows console records

<Tip>
  **Key Advantage of Async API**: No charges for failures of any kind, including content safety review failures. Sync API charges as long as the request succeeds, even if generation ultimately fails.
</Tip>

## FAQ

<AccordionGroup>
  <Accordion title="How long is task validity?" icon="calendar">
    **Task validity: 24 hours**

    * After creating task, `expires_at` field shows expiration time
    * Can query task status anytime within 24 hours
    * After video generation completes, file is stored for 24 hours
    * After 24 hours, task and video will be automatically cleaned

    **Recommendations:**

    * Download video immediately after completion
    * Don't rely on server for long-term storage
  </Accordion>

  <Accordion title="How to cancel a running task?" icon="ban">
    **Manual task cancellation not currently supported**

    * Once created, task will auto-queue for execution
    * If no longer needed, simply ignore it
    * Incomplete tasks won't be charged

    **Alternatives:**

    * Wait for task to naturally complete or fail
    * Task auto-expires after 24 hours
  </Accordion>

  <Accordion title="Why do I get 404 when querying?" icon="search">
    **Possible reasons:**

    1. **Incorrect video\_id** - Check if fully copied
    2. **Task expired** - Over 24 hours
    3. **Network issues** - Retry request

    **Solution:**

    ```python theme={null}
    try:
        response = client.get(f"/videos/{video_id}")
        task = response.json()
    except requests.exceptions.HTTPError as e:
        if e.response.status_code == 404:
            print("Task doesn't exist or expired")
        else:
            raise
    ```
  </Accordion>

  <Accordion title="Can async and sync APIs be mixed?" icon="git-merge">
    **Yes, they're completely independent**

    Two API systems are completely independent:

    * Different endpoints
    * Different calling methods
    * Same pricing
    * Share same API Key and balance

    **Usage recommendations:**

    * Quick testing → Use sync API
    * Production environment → Use async API (more stable)
    * Batch generation → Use async API
  </Accordion>

  <Accordion title="Why isn't progress field updating?" icon="activity">
    **Possible reasons:**

    1. **Normal** - Some processing stages update slowly
    2. **Queue waiting** - May be queued during peak hours
    3. **Task stuck** - Rare cases where task may get stuck

    **Handling:**

    * Continue waiting 5-10 minutes
    * If no change after 10 minutes, contact support
    * Provide video\_id for troubleshooting
  </Accordion>

  <Accordion title="Does image-to-video support image URLs?" icon="image">
    **No! Only local file upload is supported.**

    **Correct approach:**

    ```python theme={null}
    # ✓ Correct: Upload local file
    with open('/path/to/image.png', 'rb') as f:
        files = {'input_reference': f}
        response = requests.post(url, files=files, data=data)
    ```

    **Not supported:**

    * ✗ Image URLs
    * ✗ Base64 encoding
    * ✗ Online image links

    **Reason:** Async API uses `multipart/form-data` format, supporting only local file streams.
  </Accordion>

  <Accordion title="What image formats are supported?" icon="file-image">
    **Supported formats:**

    * ✓ JPG / JPEG
    * ✓ PNG
    * ✓ WebP

    **Image requirements:**

    * **File size:** \< 5MB (recommended)
    * **Resolution:** Recommended 1280x720 or similar ratio
    * **Source:** Must be local file

    **Auto-detect MIME type:**

    ```python theme={null}
    import os

    def get_mime_type(path):
        ext = os.path.splitext(path)[1].lower()
        types = {
            '.jpg': 'image/jpeg',
            '.jpeg': 'image/jpeg',
            '.png': 'image/png',
            '.webp': 'image/webp'
        }
        return types.get(ext, 'image/jpeg')
    ```
  </Accordion>

  <Accordion title="Is prompt required for image-to-video?" icon="message-square">
    **Yes, prompt parameter is required!**

    Even if you just want the image to "naturally animate", you need to provide a description:

    **Recommended simple prompts:**

    ```
    "Animate the scene naturally"
    "Add dynamic effects while keeping original style"
    "Bring this scene to life with gentle motion"
    ```

    **More specific prompts work better:**

    ```
    "Create ripples on the lake, trees swaying in wind, clouds moving slowly"
    "Make the cat jump from left to right, tail wagging"
    "360-degree slow rotation showcasing the product with lighting effects"
    ```
  </Accordion>

  <Accordion title="Is image-to-video more expensive?" icon="coins">
    **Exactly the same price!**

    | Video Length             | Price       |
    | ------------------------ | ----------- |
    | 10s (landscape/portrait) | \$0.15/call |
    | 15s (landscape/portrait) | \$0.15/call |

    **Billing notes:**

    * Image-to-video and text-to-video cost the same
    * Only charged when successfully generated
    * No charge on failure (including format errors, content violations, etc.)
  </Accordion>

  <Accordion title="Does image affect generation time?" icon="clock">
    **Almost no impact.**

    Image-to-video and text-to-video take similar time:

    * **Typical time:** 2-5 minutes
    * **Factors:** Video length, queue size, complexity

    **Image size impact:**

    * Recommended \< 5MB: Fast upload, fast processing
    * Large images: Only affects upload time (seconds), no significant impact on generation time
  </Accordion>
</AccordionGroup>

## Error Handling

### Common Error Codes

| HTTP Status | Error Type            | Description                     | Action                            |
| ----------- | --------------------- | ------------------------------- | --------------------------------- |
| 400         | Bad Request           | Invalid request parameters      | Check parameter format and values |
| 401         | Unauthorized          | Invalid API Key                 | Check Authorization header        |
| 402         | Payment Required      | Insufficient balance            | Account Credit and retry          |
| 404         | Not Found             | Task doesn't exist              | Check video\_id or task expired   |
| 429         | Too Many Requests     | Requests too frequent           | Reduce polling frequency          |
| 500         | Internal Server Error | Server error                    | Retry later                       |
| 503         | Service Unavailable   | Service temporarily unavailable | Wait and retry                    |

### Error Response Format

```json theme={null}
{
  "error": {
    "message": "Error description",
    "type": "invalid_request_error",
    "code": "invalid_parameter"
  }
}
```

## Technical Support

<Card title="Need Help?" icon="headset">
  Contact us if you have any questions:

  * **Email:** [hi@laozhang.ai](mailto:hi@laozhang.ai)
  * **Telegram:** [https://t.me/laozhang\_cn](https://t.me/laozhang_cn)
  * **Documentation:** [https://docs.laozhang.ai](https://docs.laozhang.ai)
</Card>

## Next Steps

<CardGroup cols={2}>
  <Card title="Sync API" icon="bolt" href="/en/api-capabilities/sora2/quick-start">
    Check out sync API approach
  </Card>

  <Card title="Examples" icon="code" href="/en/api-capabilities/sora2/examples">
    View more usage examples
  </Card>

  <Card title="Pricing" icon="tag" href="/en/api-capabilities/sora2/models-pricing">
    Learn about detailed pricing
  </Card>

  <Card title="FAQ" icon="circle-question-mark" href="/en/api-capabilities/sora2/troubleshooting">
    View more Q\&A
  </Card>
</CardGroup>
