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

# Async API

> VEO 3.1 asynchronous API reference for batch processing scenarios

<Info>
  **Async API:** Best for batch processing scenarios. Submit tasks and retrieve results via polling. For real-time interaction, use the [synchronous API](/en/api-capabilities/veo/quick-start).
</Info>

## Models & Pricing

VEO 3.1 offers 8 model variants with the following naming convention:

* **Base name**: `veo-3.1`
* **`-landscape`**: Landscape mode (1280x720), default is portrait (720x1280)
* **`-fast`**: Fast generation, quicker speed and lower price
* **`-fl`**: Frame-to-Video mode, supports first/last frame input

| Model                       | Description                       | Resolution | Price  |
| --------------------------- | --------------------------------- | ---------- | ------ |
| `veo-3.1`                   | Default portrait video            | 720 x 1280 | \$0.25 |
| `veo-3.1-fl`                | Portrait + frame-to-video         | 720 x 1280 | \$0.25 |
| `veo-3.1-fast`              | Portrait + fast                   | 720 x 1280 | \$0.15 |
| `veo-3.1-fast-fl`           | Portrait + fast + frame-to-video  | 720 x 1280 | \$0.15 |
| `veo-3.1-landscape`         | Landscape video                   | 1280 x 720 | \$0.25 |
| `veo-3.1-landscape-fl`      | Landscape + frame-to-video        | 1280 x 720 | \$0.25 |
| `veo-3.1-landscape-fast`    | Landscape + fast                  | 1280 x 720 | \$0.15 |
| `veo-3.1-landscape-fast-fl` | Landscape + fast + frame-to-video | 1280 x 720 | \$0.15 |

<Note>
  **Pay on success:** Only charged for successfully generated videos. All models generate **8-second** videos with auto-generated audio tracks.
</Note>

## API Endpoints

### 1. Create Video Task

<ParamField method="POST" path="/v1/videos">
  Create an async video generation task
</ParamField>

**Request Headers**

```text theme={null}
Content-Type: application/json
Authorization: sk-APIKEY
```

**Request Parameters**

<ParamField body="prompt" type="string" required>
  Text description for video generation
</ParamField>

<ParamField body="model" type="string" required>
  Model name, e.g., `veo-3.1`, `veo-3.1-fast`, etc.
</ParamField>

**Request Examples**

<CodeGroup>
  ```bash cURL theme={null}
  curl --location --request POST 'https://api.apiyi.com/v1/videos' \
  --header 'Authorization: sk-your-api-key' \
  --header 'Content-Type: application/json' \
  --data-raw '{
      "prompt": "A cute kitten playing",
      "model": "veo-3.1"
  }'
  ```

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

  url = "https://api.apiyi.com/v1/videos"
  headers = {
      "Content-Type": "application/json",
      "Authorization": "sk-your-api-key"
  }
  data = {
      "prompt": "A cute kitten playing",
      "model": "veo-3.1"
  }

  response = requests.post(url, json=data, headers=headers)
  print(response.json())
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch('https://api.apiyi.com/v1/videos', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': 'sk-your-api-key'
    },
    body: JSON.stringify({
      prompt: 'A cute kitten playing',
      model: 'veo-3.1'
    })
  });

  const data = await response.json();
  console.log(data);
  ```
</CodeGroup>

**Response Example**

```json theme={null}
{
  "id": "video_abc123",
  "object": "video",
  "created": 1762181811,
  "status": "queued",
  "model": "veo-3.1"
}
```

### 2. Query Task Status

<ParamField method="GET" path="/v1/videos/{video_id}">
  Query the current status of a video generation task
</ParamField>

**Path Parameters**

<ParamField path="video_id" type="string" required>
  Video task ID (returned from the create endpoint)
</ParamField>

**Request Examples**

<CodeGroup>
  ```bash cURL theme={null}
  curl --location --request GET 'https://api.apiyi.com/v1/videos/video_abc123' \
  --header 'Authorization: sk-your-api-key'
  ```

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

  video_id = "video_abc123"
  url = f"https://api.apiyi.com/v1/videos/{video_id}"
  headers = {
      "Authorization": "sk-your-api-key"
  }

  response = requests.get(url, headers=headers)
  print(response.json())
  ```

  ```javascript JavaScript theme={null}
  const videoId = 'video_abc123';
  const response = await fetch(`https://api.apiyi.com/v1/videos/${videoId}`, {
    headers: {
      'Authorization': 'sk-your-api-key'
    }
  });

  const data = await response.json();
  console.log(data);
  ```
</CodeGroup>

**Status Reference**

| Status       | Description         | Next Action           |
| ------------ | ------------------- | --------------------- |
| `queued`     | Task is queued      | Continue polling      |
| `processing` | Task is processing  | Continue polling      |
| `completed`  | Generation complete | Call content endpoint |
| `failed`     | Generation failed   | Check error message   |

### 3. Get Video Content

<ParamField method="GET" path="/v1/videos/{video_id}/content">
  Get the actual content of a generated video
</ParamField>

**Request Examples**

<CodeGroup>
  ```bash cURL theme={null}
  curl --location --request GET 'https://api.apiyi.com/v1/videos/video_abc123/content' \
  --header 'Authorization: sk-your-api-key'
  ```

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

  video_id = "video_abc123"
  url = f"https://api.apiyi.com/v1/videos/{video_id}/content"
  headers = {
      "Authorization": "sk-your-api-key"
  }

  response = requests.get(url, headers=headers)
  print(response.json())
  ```

  ```javascript JavaScript theme={null}
  const videoId = 'video_abc123';
  const response = await fetch(`https://api.apiyi.com/v1/videos/${videoId}/content`, {
    headers: {
      'Authorization': 'sk-your-api-key'
    }
  });

  const data = await response.json();
  console.log(data);
  ```
</CodeGroup>

**Response Example**

```json theme={null}
{
  "id": "video_abc123",
  "object": "video",
  "created": 1762181811,
  "status": "completed",
  "model": "veo-3.1",
  "prompt": "A cute kitten playing",
  "url": "https://veo-video.gptkey.asia/assets/flow/xxx.mp4",
  "duration": 8,
  "resolution": "720x1280"
}
```

<Info>
  Video URLs are typically valid for 24 hours. Download and save promptly.
</Info>

## Complete Workflow

<Steps>
  <Step title="Create Task">
    Call `POST /v1/videos` to create a video generation task and get the `video_id`
  </Step>

  <Step title="Poll Status">
    Use `GET /v1/videos/{video_id}` to poll task status (recommended: every 5-10 seconds) until status is `completed`
  </Step>

  <Step title="Get Video">
    Call `GET /v1/videos/{video_id}/content` to get the video URL
  </Step>

  <Step title="Download Video">
    Download and save the video file from the returned URL
  </Step>
</Steps>

## Python Complete Example

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

class VEOClient:
    """VEO 3.1 Async API Client"""

    def __init__(self, api_key: str, base_url: str = "https://api.apiyi.com"):
        self.base_url = base_url
        self.headers = {
            "Content-Type": "application/json",
            "Authorization": api_key
        }

    def create_video(self, prompt: str, model: str = "veo-3.1") -> str:
        """Create video task, returns video_id"""
        response = requests.post(
            f"{self.base_url}/v1/videos",
            headers=self.headers,
            json={"prompt": prompt, "model": model}
        )
        response.raise_for_status()
        return response.json()["id"]

    def get_status(self, video_id: str) -> dict:
        """Query task status"""
        response = requests.get(
            f"{self.base_url}/v1/videos/{video_id}",
            headers=self.headers
        )
        response.raise_for_status()
        return response.json()

    def get_content(self, video_id: str) -> dict:
        """Get video content"""
        response = requests.get(
            f"{self.base_url}/v1/videos/{video_id}/content",
            headers=self.headers
        )
        response.raise_for_status()
        return response.json()

    def wait_for_completion(self, video_id: str, timeout: int = 600, interval: int = 5) -> dict:
        """Wait for task completion"""
        start_time = time.time()
        while time.time() - start_time < timeout:
            status_data = self.get_status(video_id)
            status = status_data.get("status")

            if status == "completed":
                return self.get_content(video_id)
            elif status == "failed":
                raise Exception(f"Video generation failed: {status_data}")

            print(f"Task in progress... Status: {status}")
            time.sleep(interval)

        raise TimeoutError("Timeout waiting for completion")


# Usage example
if __name__ == "__main__":
    client = VEOClient("sk-your-api-key")

    try:
        # 1. Create task
        video_id = client.create_video(
            prompt="A kitten walking in a sunny garden",
            model="veo-3.1-fast"
        )
        print(f"Task created, ID: {video_id}")

        # 2. Wait for completion and get result
        result = client.wait_for_completion(video_id)
        print(f"Video generated successfully!")
        print(f"Video URL: {result['url']}")
        print(f"Resolution: {result['resolution']}")

    except Exception as e:
        print(f"Error: {e}")
```

## Frame-to-Video Mode

Models with the `-fl` suffix support frame-to-video functionality, converting static images into dynamic videos.

| Mode               | Images | Description                                                      |
| ------------------ | ------ | ---------------------------------------------------------------- |
| First Frame        | 1      | Use image as video start, AI generates the continuation          |
| First & Last Frame | 2      | Use first image as start, second as end, AI generates transition |

### Request Parameters

<Warning>
  Frame-to-video requests require `multipart/form-data` format (not JSON) because image files need to be uploaded.
</Warning>

<ParamField body="prompt" type="string" required>
  Video description. Describe how the scene should move (e.g., "camera slowly zooms in", "person walks forward")
</ParamField>

<ParamField body="model" type="string" required>
  Must use a model with `-fl` suffix, e.g., `veo-3.1-fl`, `veo-3.1-landscape-fl`
</ParamField>

<ParamField body="input_reference" type="file" required>
  Image file. Pass once for first-frame mode, twice for first-and-last-frame mode
</ParamField>

### First Frame Mode (Single Image)

Use one image as the video start, AI automatically generates the continuation.

<CodeGroup>
  ```bash cURL theme={null}
  curl --location --request POST 'https://api.apiyi.com/v1/videos' \
  --header 'Authorization: sk-your-api-key' \
  --form 'prompt="Bring this scene to life, camera slowly zooms in"' \
  --form 'model="veo-3.1-landscape-fl"' \
  --form 'input_reference=@"/path/to/image.jpg"'
  ```

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

  url = "https://api.apiyi.com/v1/videos"
  headers = {"Authorization": "sk-your-api-key"}
  data = {
      "prompt": "Bring this scene to life, camera slowly zooms in",
      "model": "veo-3.1-landscape-fl"
  }

  # Single image mode - first frame
  files = [
      ("input_reference", ("image.jpg", open("/path/to/image.jpg", "rb"), "image/jpeg"))
  ]

  response = requests.post(url, headers=headers, data=data, files=files)
  print(response.json())
  ```

  ```javascript JavaScript (Node.js) theme={null}
  const FormData = require('form-data');
  const fs = require('fs');

  const form = new FormData();
  form.append('prompt', 'Bring this scene to life, camera slowly zooms in');
  form.append('model', 'veo-3.1-landscape-fl');
  form.append('input_reference', fs.createReadStream('/path/to/image.jpg'));

  const response = await fetch('https://api.apiyi.com/v1/videos', {
    method: 'POST',
    headers: {
      'Authorization': 'sk-your-api-key',
      ...form.getHeaders()
    },
    body: form
  });

  const data = await response.json();
  console.log(data);
  ```
</CodeGroup>

### First & Last Frame Mode (Two Images)

Specify the start and end frames, AI generates the transition animation between them.

<CodeGroup>
  ```bash cURL theme={null}
  curl --location --request POST 'https://api.apiyi.com/v1/videos' \
  --header 'Authorization: sk-your-api-key' \
  --form 'prompt="Transition from day to night, camera stays still"' \
  --form 'model="veo-3.1-landscape-fl"' \
  --form 'input_reference=@"/path/to/first-frame.jpg"' \
  --form 'input_reference=@"/path/to/last-frame.jpg"'
  ```

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

  url = "https://api.apiyi.com/v1/videos"
  headers = {"Authorization": "sk-your-api-key"}
  data = {
      "prompt": "Transition from day to night, camera stays still",
      "model": "veo-3.1-landscape-fl"
  }

  # Two image mode - first and last frame
  files = [
      ("input_reference", ("first.jpg", open("/path/to/first-frame.jpg", "rb"), "image/jpeg")),
      ("input_reference", ("last.jpg", open("/path/to/last-frame.jpg", "rb"), "image/jpeg"))
  ]

  response = requests.post(url, headers=headers, data=data, files=files)
  print(response.json())
  ```

  ```javascript JavaScript (Node.js) theme={null}
  const FormData = require('form-data');
  const fs = require('fs');

  const form = new FormData();
  form.append('prompt', 'Transition from day to night, camera stays still');
  form.append('model', 'veo-3.1-landscape-fl');
  form.append('input_reference', fs.createReadStream('/path/to/first-frame.jpg'));
  form.append('input_reference', fs.createReadStream('/path/to/last-frame.jpg'));

  const response = await fetch('https://api.apiyi.com/v1/videos', {
    method: 'POST',
    headers: {
      'Authorization': 'sk-your-api-key',
      ...form.getHeaders()
    },
    body: form
  });

  const data = await response.json();
  console.log(data);
  ```
</CodeGroup>

### Response & Next Steps

The response format is the same as text-to-video. After receiving `video_id`, poll for results:

```json theme={null}
{
  "id": "video_xyz789",
  "object": "video",
  "created": 1762181811,
  "status": "queued",
  "model": "veo-3.1-landscape-fl"
}
```

For subsequent steps, refer to the [Complete Workflow](#complete-workflow) section above.

### Usage Recommendations

<CardGroup cols={2}>
  <Card title="First Frame Mode Use Cases" icon="play">
    * Bring static images to life
    * Product showcase animations
    * Generate dynamic video from portraits
  </Card>

  <Card title="First & Last Frame Use Cases" icon="arrow-right-left">
    * Scene transitions (day→night, seasonal changes)
    * Expression change animations
    * Object morphing transitions
  </Card>
</CardGroup>

<Tip>
  **Prompt Tips**: When using frame-to-video, describe "how the scene moves" rather than "what's in the scene". Examples: "camera slowly zooms in", "person turns head and smiles", "flower gradually blooms".
</Tip>

## Error Handling

| Error Code        | Description             | Solution                          |
| ----------------- | ----------------------- | --------------------------------- |
| `invalid_api_key` | Invalid API key         | Check if API key is correct       |
| `invalid_model`   | Model not found         | Use a supported model name        |
| `invalid_prompt`  | Invalid prompt          | Check prompt length and content   |
| `video_not_found` | Video task not found    | Verify video\_id is correct       |
| `video_not_ready` | Video not yet generated | Continue polling status           |
| `quota_exceeded`  | Quota exceeded          | Contact support to increase quota |

**Error Response Format**

```json theme={null}
{
  "error": {
    "code": "invalid_api_key",
    "message": "Invalid API key provided",
    "type": "authentication_error"
  }
}
```

## FAQ

<Accordion title="How do I choose the right model?">
  * **Best value**: Choose `-fast` series (\$0.15/video)
  * **Quality first**: Choose standard series (\$0.25/video)
  * **Image-to-video**: Choose `-fl` series
  * **Landscape content**: Choose `-landscape` series
</Accordion>

<Accordion title="How long does video generation take?">
  * **Fast models** (`-fast`): \~30-60 seconds
  * **Standard models**: \~1-2 minutes

  Recommended polling interval: 5-10 seconds.
</Accordion>

<Accordion title="How long is the video URL valid?">
  Video URLs are typically valid for 24 hours. Download and save promptly after generation.
</Accordion>

<Accordion title="Are there volume discounts?">
  For bulk usage, contact support for enterprise pricing: `feedback@apiyi.com`
</Accordion>

## Technical Specifications

| Item                      | Specification     |
| ------------------------- | ----------------- |
| Video Duration            | 8 seconds         |
| Portrait Resolution       | 720 x 1280 (9:16) |
| Landscape Resolution      | 1280 x 720 (16:9) |
| Audio Track               | Auto-included     |
| URL Validity              | 24 hours          |
| Recommended Poll Interval | 5-10 seconds      |
| Maximum Wait Time         | 10 minutes        |
