> ## 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.

# Sora 2 Character API

> Create reusable characters for consistent appearances - virtual characters for e-commerce, animation, and more

## Overview

The Sora 2 Character API allows you to create reusable characters from videos and maintain character consistency across multiple video generations. By referencing `@id` (unique Cameo ID or Character ID) in your prompts, you can make specific characters appear in different videos.

<Info>
  Since the API prohibits direct use of human faces, the Character API provides a compliant solution: create a character as a virtual entity first, then reference it via `@id` in prompts.
</Info>

## Core Concepts

How the Character API works:

1. **Create Character**: Generate a unique character ID from a video URL
2. **Reference Character**: Include `@character_id` or `@username` in video generation prompts
3. **Maintain Consistency**: Characters retain their appearance and features across different videos

## Why Use Characters?

<Warning>
  Important Limitation: Real human appearances are NOT allowed. The API does not permit uploading videos containing real human faces as characters.
</Warning>

Typical use cases for the Character API:

### E-commerce Scenarios

<Card title="Product Characterization" icon="shopping-cart">
  Turn product packaging into characters to maintain consistent brand logos, text, and visual identity. Reference the product via `@character_id` in subsequent videos to ensure consistent appearance across different scenes.
</Card>

**Example Applications**:

* Product showcase videos: Same product appearing in different environments (outdoor, indoor, workplace)
* Brand marketing: Brand mascots maintaining consistent imagery across multiple short videos
* Package display: Logos and text on product packaging remain clear and consistent from multiple angles

### Animation Scenarios

<Card title="Virtual Characters" icon="wand-sparkles">
  Virtual personas, cartoon characters, and anime figures are ideal for character creation. Once created, characters can appear repeatedly across different plots and scenes while maintaining consistent design.
</Card>

**Example Applications**:

* Series animation shorts: Protagonists maintaining same appearance across episodes
* Brand IP characters: Virtual spokespersons appearing in different marketing videos
* Educational content: Teaching virtual instructors maintaining consistent image across videos

### Other Scenarios

* **Game Assets**: Promotional videos for game characters
* **Virtual Streamers**: Series video content for virtual content creators
* **IP Derivatives**: Video content creation for existing IP characters

## Endpoint

```
https://api.apiyi.com/sora/v1/characters
```

## Request Parameters

| Parameter    | Type   | Required | Description                                                                                                                                                                                |
| ------------ | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `model`      | string | ✅        | Fixed value: `sora-character`                                                                                                                                                              |
| `url`        | string | ✅        | Video URL containing the character to be created                                                                                                                                           |
| `timestamps` | string | ✅        | Time range where character appears (in seconds), format: `"start,end"`<br />Example: `"1,3"` means seconds 1-3 of the video<br />**Range difference**: minimum 1 second, maximum 3 seconds |

### Parameter Details

<AccordionGroup>
  <Accordion title="url - Video URL Requirements">
    * **Video Length**: 15 seconds or less
    * **Format**: MP4, MOV, and other common formats
    * **Content**: Must NOT contain real human faces or obvious human body outlines
    * **Accessibility**: URL must be publicly accessible (CDN addresses supported)

    **Best Practices**:

    * Use CDN-hosted videos for better success rates
    * Ensure sufficient video clarity (720p or above recommended)
    * Character should have distinctive features (color, shape, texture, etc.)
  </Accordion>

  <Accordion title="timestamps - Time Range Settings">
    * **Format**: String type, e.g., `"1,3"`
    * **Meaning**: Specifies the time segment in the video where the character appears (from which second to which second)
    * **Unit**: Seconds (integers or decimals accepted)
    * **Total Video Length**: Video can be any length up to 15 seconds
    * **Time Segment Constraints**:
      * Segment duration (end seconds - start seconds): minimum 1 second, maximum 3 seconds
      * Start time cannot be less than 0
      * End time cannot exceed total video duration

    **Examples**:

    * Video total length 15s, `timestamps: "1,3"` means use video content from seconds 1-3 as character (segment duration 2s)
    * Video total length 15s, `timestamps: "8,11"` means use video content from seconds 8-11 as character (segment duration 3s)
    * Video total length 10s, `timestamps: "5,6"` means use video content from seconds 5-6 as character (segment duration 1s)

    **Selection Tips**:

    * Find the time segment in the video where character features are most prominent
    * Avoid time segments where character is occluded or motion-blurred
    * Segment duration can be 1-3 seconds based on actual needs, typically around 2 seconds works well
  </Accordion>

  <Accordion title="Future Support: Create from Async Task ID">
    **Current Status**: Not yet available

    Future updates will support creating characters directly using Sora 2 async video generation task `video_id`, eliminating the need to download and re-upload videos to CDN. This will significantly streamline the workflow.

    Expected parameter format:

    ```json theme={null}
    {
      "model": "sora-character",
      "video_id": "video_abc123def456",
      "timestamps": "1,3"
    }
    ```
  </Accordion>
</AccordionGroup>

## Code Examples

### cURL Example

```bash cURL theme={null}
curl -X POST "https://api.apiyi.com/sora/v1/characters" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d '{
    "model": "sora-character",
    "url": "https://mycdn-gg.oss-us-west-1.aliyuncs.com/sora/64ba6b88-8540-4b73-bf05-7bd2e96bebd1.mp4",
    "timestamps": "1,3"
  }'
```

### Python Examples

<CodeGroup>
  ```python Python Basic Example theme={null}
  import requests
  import json

  # API Configuration
  API_URL = "https://api.apiyi.com/sora/v1/characters"
  API_KEY = "YOUR_API_KEY"

  # Request parameters
  payload = {
      "model": "sora-character",
      "url": "https://mycdn-gg.oss-us-west-1.aliyuncs.com/sora/64ba6b88-8540-4b73-bf05-7bd2e96bebd1.mp4",
      "timestamps": "1,3"
  }

  # Send request
  headers = {
      "Content-Type": "application/json",
      "Authorization": f"Bearer {API_KEY}"
  }

  response = requests.post(API_URL, headers=headers, json=payload)
  result = response.json()

  # Output results
  print(f"Character ID: {result['id']}")
  print(f"Username: {result['username']}")
  print(f"Display Name: {result['display_name']}")
  print(f"Permalink: {result['permalink']}")
  print(f"Profile Picture: {result['profile_picture_url']}")
  ```

  ```python Python Complete Example (with Retry) theme={null}
  import requests
  import json
  import time

  # API Configuration
  API_URL = "https://api.apiyi.com/sora/v1/characters"
  API_KEY = "YOUR_API_KEY"

  def create_character(video_url, start_time, end_time, max_retries=3):
      """
      Create a character with automatic retry

      Args:
          video_url: Video URL
          start_time: Start time (seconds)
          end_time: End time (seconds)
          max_retries: Maximum retry attempts

      Returns:
          dict: Character information or None (on failure)
      """

      # Parameter validation
      time_diff = end_time - start_time
      if time_diff < 1 or time_diff > 3:
          print(f"❌ Invalid time range: {time_diff}s (allowed: 1-3s)")
          return None

      payload = {
          "model": "sora-character",
          "url": video_url,
          "timestamps": f"{start_time},{end_time}"
      }

      headers = {
          "Content-Type": "application/json",
          "Authorization": f"Bearer {API_KEY}"
      }

      # Retry logic
      for attempt in range(1, max_retries + 1):
          try:
              print(f"\n🔄 Attempting to create character... (Attempt {attempt}/{max_retries})")
              print(f"   Video: {video_url}")
              print(f"   Time Range: {start_time}-{end_time} seconds")

              response = requests.post(API_URL, headers=headers, json=payload, timeout=30)

              if response.status_code == 200:
                  result = response.json()
                  print(f"\n✅ Character created successfully!")
                  print(f"   Character ID: {result['id']}")
                  print(f"   Username: @{result['username']}")
                  print(f"   Display Name: {result['display_name']}")
                  print(f"   Permalink: {result['permalink']}")
                  print(f"   Profile Picture: {result['profile_picture_url']}")
                  return result
              else:
                  error_msg = response.text
                  print(f"❌ Creation failed (HTTP {response.status_code}): {error_msg}")

                  # Don't retry on client errors (4xx)
                  if 400 <= response.status_code < 500:
                      print("⚠️  Client error, skipping retry")
                      return None

          except requests.exceptions.Timeout:
              print(f"⏱️  Request timeout")
          except requests.exceptions.RequestException as e:
              print(f"❌ Network error: {str(e)}")
          except Exception as e:
              print(f"❌ Unknown error: {str(e)}")

          # Wait before retry (if not last attempt)
          if attempt < max_retries:
              wait_time = 2 ** attempt  # Exponential backoff: 2s, 4s, 8s
              print(f"⏳ Waiting {wait_time} seconds before retry...")
              time.sleep(wait_time)

      print(f"\n❌ Character creation failed: Maximum retries reached ({max_retries})")
      return None


  # Usage example
  if __name__ == "__main__":
      # Example: Create a virtual character
      character = create_character(
          video_url="https://mycdn-gg.oss-us-west-1.aliyuncs.com/sora/64ba6b88-8540-4b73-bf05-7bd2e96bebd1.mp4",
          start_time=1,
          end_time=3,
          max_retries=3
      )

      if character:
          print("\n" + "=" * 60)
          print("🎉 Character created, ready for video generation:")
          print("=" * 60)
          print(f"Prompt Examples:")
          print(f"  \"@{character['username']} running through a forest, sunlight filtering through leaves\"")
          print(f"  \"Make @{character['username']} play with a little bird\"")
  ```

  ```python Python Batch Creation theme={null}
  import requests
  import json
  import time
  from concurrent.futures import ThreadPoolExecutor, as_completed

  API_URL = "https://api.apiyi.com/sora/v1/characters"
  API_KEY = "YOUR_API_KEY"

  def create_single_character(video_info):
      """Create a single character (for concurrent execution)"""
      payload = {
          "model": "sora-character",
          "url": video_info["url"],
          "timestamps": video_info["timestamps"]
      }

      headers = {
          "Content-Type": "application/json",
          "Authorization": f"Bearer {API_KEY}"
      }

      try:
          response = requests.post(API_URL, headers=headers, json=payload, timeout=30)
          if response.status_code == 200:
              return {
                  "success": True,
                  "name": video_info["name"],
                  "data": response.json()
              }
          else:
              return {
                  "success": False,
                  "name": video_info["name"],
                  "error": f"HTTP {response.status_code}: {response.text}"
              }
      except Exception as e:
          return {
              "success": False,
              "name": video_info["name"],
              "error": str(e)
          }

  def batch_create_characters(video_list, max_workers=3):
      """
      Batch create characters

      Args:
          video_list: List of video info, each containing name, url, timestamps
          max_workers: Maximum concurrent workers
      """
      print(f"Starting batch creation of {len(video_list)} characters...\n")

      results = {
          "success": [],
          "failed": []
      }

      with ThreadPoolExecutor(max_workers=max_workers) as executor:
          # Submit all tasks
          future_to_video = {
              executor.submit(create_single_character, video): video
              for video in video_list
          }

          # Process completed tasks
          for future in as_completed(future_to_video):
              video = future_to_video[future]
              try:
                  result = future.result()

                  if result["success"]:
                      print(f"✅ {result['name']}: Created successfully")
                      print(f"   @{result['data']['username']}")
                      results["success"].append(result)
                  else:
                      print(f"❌ {result['name']}: Creation failed")
                      print(f"   {result['error']}")
                      results["failed"].append(result)

              except Exception as e:
                  print(f"❌ {video['name']}: Exception - {str(e)}")
                  results["failed"].append({
                      "success": False,
                      "name": video["name"],
                      "error": str(e)
                  })

      # Summary
      print("\n" + "=" * 60)
      print(f"Batch creation complete:")
      print(f"  Success: {len(results['success'])} characters")
      print(f"  Failed: {len(results['failed'])} characters")
      print("=" * 60)

      return results

  # Usage example
  if __name__ == "__main__":
      videos = [
          {
              "name": "Product Packaging Character",
              "url": "https://example.com/product-video.mp4",
              "timestamps": "1,3"
          },
          {
              "name": "Brand Mascot",
              "url": "https://example.com/mascot-video.mp4",
              "timestamps": "0,2"
          },
          {
              "name": "Virtual Streamer Character",
              "url": "https://example.com/vtuber-video.mp4",
              "timestamps": "2,4"
          }
      ]

      results = batch_create_characters(videos, max_workers=3)

      # Print successfully created characters
      if results["success"]:
          print("\nSuccessfully created characters:")
          for item in results["success"]:
              char = item["data"]
              print(f"  - {item['name']}: @{char['username']}")
  ```
</CodeGroup>

## Response Format

### Success Response

```json theme={null}
{
  "id": "char_abc123def456",
  "username": "my_character_2024",
  "display_name": "My Virtual Character",
  "permalink": "https://api.apiyi.com/characters/my_character_2024",
  "profile_picture_url": "https://cdn.apiyi.com/characters/char_abc123def456/profile.jpg"
}
```

### Response Fields

| Field                 | Type   | Description                                                                                                           |
| --------------------- | ------ | --------------------------------------------------------------------------------------------------------------------- |
| `id`                  | string | Unique character identifier (Character ID)<br />Can be referenced as `@char_abc123def456` in video generation prompts |
| `username`            | string | Character username (unique)<br />Can be referenced as `@my_character_2024` in video generation prompts                |
| `display_name`        | string | Character display name (can be duplicated)<br />For display purposes only, cannot be used for referencing             |
| `permalink`           | string | Permanent link to character homepage                                                                                  |
| `profile_picture_url` | string | URL of character profile picture                                                                                      |

<Tip>
  **Recommended Usage**: Use `@username` in video generation for better readability, e.g., `"@my_character_2024 walking in the park"`
</Tip>

## Pricing

| Item               | Price            | Notes                                  |
| ------------------ | ---------------- | -------------------------------------- |
| Character Creation | \$0.01 / request | Promotional pricing during beta period |

<Info>
  Currently in promotional period with very low pricing. Official pricing may be adjusted. Please follow official announcements.
</Info>

## Using Characters in Video Generation

After creating a character, use it in Sora 2 video generation:

### Usage Method

Reference the character in the `prompt` parameter:

```python theme={null}
# Method 1: Use @username
prompt = "@my_character_2024 running through a forest, sunlight filtering through leaves"

# Method 2: Use @id
prompt = "@char_abc123def456 playing with a little bird, warm and fun"
```

### Complete Example

```python theme={null}
import requests

# Configuration
SORA_API_URL = "https://api.apiyi.com/v1/videos"
API_KEY = "YOUR_API_KEY"

# Generate video with character reference
payload = {
    "model": "sora-2",
    "prompt": "@my_character_2024 skateboarding on city streets, bustling commercial district background, sunny day",
    "size": "1280x720",
    "seconds": "10"
}

headers = {
    "Content-Type": "multipart/form-data",
    "Authorization": f"Bearer {API_KEY}"
}

response = requests.post(SORA_API_URL, headers=headers, files=payload)
result = response.json()

print(f"Video Task ID: {result['id']}")
```

See: [Sora 2 Video Generation Documentation](/en/api-capabilities/sora-2-video)

## Limitations and Considerations

<Warning>
  **Critical Limitation - Face Detection**: Videos must NOT contain real human faces or obvious human body outlines, otherwise character creation will fail. Use virtual characters, cartoon images, products, objects, or other non-face content.
</Warning>

### Success Rate Factors

<CardGroup cols={2}>
  <Card title="Factors Affecting Success Rate" icon="triangle-alert">
    * Real human faces or bodies in video
    * Low video quality (blurry, shaky)
    * Indistinct character features
    * Instability of iOS reverse engineering technology
  </Card>

  <Card title="Tips to Improve Success Rate" icon="check">
    * Use clear virtual character videos
    * Select segments with prominent character features
    * Ensure stable, shake-free video
    * Retry if necessary
  </Card>
</CardGroup>

### Video Requirements

| Requirement            | Description                                                                        |
| ---------------------- | ---------------------------------------------------------------------------------- |
| Maximum Length         | 15 seconds or less                                                                 |
| Recommended Resolution | 720p or above                                                                      |
| Format                 | MP4, MOV, and other common formats                                                 |
| Content Restrictions   | Must NOT contain real faces, violence, adult content, or other prohibited material |
| Character Clarity      | Character should occupy reasonable screen proportion with distinctive features     |

### Technical Limitations

<Info>
  Since character creation is based on iOS reverse engineering technology, there may be some technical instability. If creation fails, we recommend:

  1. Check if video content meets requirements
  2. Adjust `timestamps` time range to select clearer segments
  3. Use retry mechanism (2-3 attempts recommended)
  4. Contact technical support if failures persist
</Info>

## Best Practices

### Selecting Optimal Video Segments

```python theme={null}
# Recommended: Prominent character features, stable footage
timestamps = "2,4"  # Select 2-second segment with clear, front-facing character

# Avoid: Occluded character, motion blur
timestamps = "0,1"  # Beginning may have transition effects, unstable
```

### Retry Strategy

```python theme={null}
def create_character_with_retry(video_url, timestamps, max_retries=3):
    """Retry strategy with exponential backoff"""
    for i in range(max_retries):
        try:
            result = create_character(video_url, timestamps)
            return result
        except Exception as e:
            if i == max_retries - 1:
                raise
            wait_time = 2 ** i  # 2s, 4s, 8s
            print(f"Retry {i+1}/{max_retries}, waiting {wait_time} seconds...")
            time.sleep(wait_time)
```

### Character Management

Maintain a character database:

```python theme={null}
characters_db = {
    "product_A": {
        "id": "char_abc123",
        "username": "product_a_2024",
        "description": "Product A packaging character",
        "created_at": "2024-01-15"
    },
    "mascot_B": {
        "id": "char_def456",
        "username": "mascot_b_2024",
        "description": "Brand mascot",
        "created_at": "2024-01-16"
    }
}
```

## FAQ

<AccordionGroup>
  <Accordion title="Why does creation fail with 'contains human face' error?">
    The API has strict restrictions on real human faces. Even AI-generated realistic faces may be detected. Recommended alternatives:

    * Cartoon/anime-style virtual characters
    * Products, objects, or other non-human content
    * Abstract artistic style characters

    If you must use humanoid characters, use highly stylized designs.
  </Accordion>

  <Accordion title="Do created characters expire?">
    Once successfully created, characters remain valid long-term and can be used repeatedly in video generation. However, we recommend saving the character's `id` and `username` information.
  </Accordion>

  <Accordion title="Can I modify an existing character?">
    Currently, modifying existing characters is not supported. If adjustments are needed:

    1. Create a new character
    2. Use the new character in new video generations
    3. Old characters remain usable and won't be deleted
  </Accordion>

  <Accordion title="How to improve character consistency in videos?">
    * **Explicit Reference**: Use `@username` at the beginning of prompts
    * **Detailed Description**: Describe character actions and scenes, but avoid repeating character appearance descriptions
    * **Examples**:
      ```
      ✅ Good prompt:
      "@my_character running in the park, chasing a butterfly, sunny day"

      ❌ Poor prompts:
      "A red character in the park"  # No character ID reference
      "@my_character red body in park"  # Repeating appearance may cause conflicts
      ```
  </Accordion>

  <Accordion title="How to choose the best timestamps?">
    Selection tips:

    * **Watch Video**: Review entire video to find clearest character segments
    * **Front Angle**: Prioritize front-facing or 3/4 angle segments
    * **Good Lighting**: Avoid too dark or overexposed segments
    * **No Occlusion**: Ensure character is complete and unobstructed
    * **Stable Footage**: Avoid rapid movement or camera shake

    **Testing Method**: Try different time ranges and compare results.
  </Accordion>

  <Accordion title="Are there limits for batch character creation?">
    * **Rate Limits**: Follow API request rate limits (see API manual for details)
    * **Concurrency Control**: Recommend 3-5 concurrent requests to avoid throttling
    * **Cost Considerations**: While current pricing is low (\$0.01/request), batch creation still incurs costs

    Use the batch creation function in example code, which includes proper concurrency control.
  </Accordion>

  <Accordion title="Can characters be used in different Sora models?">
    After creation, characters are primarily used for Sora 2 video generation. Future support for other Sora series models may be added. Please follow official updates.
  </Accordion>
</AccordionGroup>

## Related Resources

<CardGroup cols={2}>
  <Card title="Sora 2 Video Generation" icon="video" href="/en/api-capabilities/sora-2-video">
    Learn how to use characters in video generation
  </Card>

  <Card title="Sora 2 Async API" icon="clock" href="/en/api-capabilities/sora-2-video-async">
    Batch generate videos with characters
  </Card>

  <Card title="API Manual" icon="code" href="/en/api-manual">
    Learn about API authentication, request formats, and basics
  </Card>

  <Card title="FAQ" icon="circle-question" href="/en/faq/model-selection-guide">
    View more frequently asked questions
  </Card>
</CardGroup>
