Skip to main content
POST
/
v1
/
videos
Text-to-video: submit a generation task from text prompt
curl --request POST \
  --url https://api.apiyi.com/v1/videos \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '
{
  "model": "veo-3.1-fast-generate-preview",
  "prompt": "A coastal lighthouse at dusk, slow push-in, waves lapping the rocks, distant seabirds, cinematic lighting, steady camera"
}
'
{
  "id": "task_xxxxxxxxxxxxxxxx",
  "task_id": "task_xxxxxxxxxxxxxxxx",
  "object": "video",
  "model": "veo-3.1-fast-generate-preview",
  "status": "queued",
  "progress": 0,
  "created_at": 1775025000,
  "completed_at": 1775025090
}

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.

The interactive Playground on the right supports live debugging. Fill in your API Key under Authorization (format Bearer sk-xxx), enter prompt, pick model / duration / metadata.resolution, then send. Default group works — no dedicated group switch needed.
Scope: This page covers “generate video from text only” — no input_reference, application/json body. To generate from a reference image, use the Image-to-Video endpoint (same endpoint + input_reference upload).
⚠️ Three most common pitfalls
  1. duration must be a string "4" / "6" / "8" — passing a number fails with parse_request_failed: cannot unmarshal number into Go struct field ... duration of type string
  2. Do not pass generateAudio — upstream returns INVALID_ARGUMENT. Encode audio intent (ambient, dialogue, BGM) in the prompt instead
  3. At 1080p / 4k, duration must be "8""4" / "6" will be rejected upstream
Three-step async flow — this page covers only Step 1 (submit)
  • Step 1 (this page): POST /v1/videos → returns task_id + status: "queued"
  • Step 2: GET /v1/videos/{task_id} poll until status: "completed"
  • Step 3: GET /v1/videos/{task_id}/content to download MP4
POST submit itself is sub-second and does not wait for generation. Full flow shown in the Python sample below.

Code Samples

Python (OpenAI SDK · low-level client.post)

{/* OpenAI SDK has no videos.create method; /v1/videos is a custom path, use low-level client.post() */}
from openai import OpenAI
import time

client = OpenAI(
    api_key="sk-your-api-key",
    base_url="https://api.apiyi.com/v1"
)

# Step 1: submit
resp = client.post(
    "/videos",
    body={
        "model": "veo-3.1-fast-generate-preview",
        "prompt": "A coastal lighthouse at dusk, slow push-in, waves lapping the rocks, distant seabirds, cinematic lighting, steady camera",
        "duration": "8",  # string, not number
        "size": "1280x720",
        "metadata": {
            "resolution": "720p",
            "aspectRatio": "16:9",
            "seed": 20260521,
            "negativePrompt": "blurry, watermark, distorted, low quality"
        }
    },
    cast_to=dict
)
task_id = resp["task_id"]
print(f"Task ID: {task_id}, status: {resp['status']}")

# Step 2: poll (up to 3 minutes for 720p/1080p)
deadline = time.time() + 180
while time.time() < deadline:
    s = client.get(f"/videos/{task_id}", cast_to=dict)
    print(f"Status: {s['status']}, progress: {s.get('progress', 0)}%")
    if s["status"] == "completed":
        break
    if s["status"] == "failed":
        raise RuntimeError(f"Generation failed: {s}")
    time.sleep(8)

# Step 3: download (retry to handle CDN sync delay right after completed)
import urllib.request, urllib.error
time.sleep(4)
for i in range(5):
    try:
        req = urllib.request.Request(
            f"https://api.apiyi.com/v1/videos/{task_id}/content",
            headers={"Authorization": "Bearer sk-your-api-key"}
        )
        with urllib.request.urlopen(req, timeout=180) as r, open("output.mp4", "wb") as f:
            while chunk := r.read(1 << 16):
                f.write(chunk)
        break
    except urllib.error.HTTPError:
        if i == 4:
            raise
        time.sleep(4)
print("Saved: output.mp4")

Python (requests)

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": "veo-3.1-fast-generate-preview",
        "prompt": "A coastal lighthouse at dusk, slow push-in, steady camera, ocean ambience and distant seabirds",
        "duration": "8",  # must be a string
        "size": "1280x720",
        "metadata": {
            "resolution": "720p",
            "aspectRatio": "16:9"
        }
    },
    timeout=30  # POST is just an enqueue; 30 sec is enough
).json()
task_id = resp["task_id"]
print(f"Task ID: {task_id}, status: {resp['status']}")

# Step 2: poll (3 min for 720p/1080p, 10 min for 4K)
deadline = time.time() + 180
while time.time() < deadline:
    s = requests.get(f"{BASE_URL}/videos/{task_id}", headers=HEADERS).json()
    print(f"Status: {s['status']}, progress: {s.get('progress', 0)}%")
    if s["status"] == "completed":
        break
    if s["status"] == "failed":
        raise RuntimeError(f"Generation failed: {s}")
    time.sleep(8)

# Step 3: download with retry
time.sleep(4)
for i in range(5):
    try:
        with requests.get(
            f"{BASE_URL}/videos/{task_id}/content",
            headers=HEADERS, stream=True, timeout=180
        ) 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)
        break
    except requests.HTTPError:
        if i == 4:
            raise
        time.sleep(4)
print("Saved: output.mp4")

cURL

{/* Step 1: submit (note duration is the string "8", not number 8) */}
RESP=$(curl -sS -X POST "https://api.apiyi.com/v1/videos" \
  -H "Authorization: Bearer sk-your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "veo-3.1-fast-generate-preview",
    "prompt": "A coastal lighthouse at dusk, slow push-in, ocean ambience, cinematic lighting",
    "duration": "8",
    "size": "1280x720",
    "metadata": {"resolution": "720p", "aspectRatio": "16:9"}
  }')
TASK_ID=$(echo "$RESP" | python3 -c 'import sys,json;print(json.load(sys.stdin)["task_id"])')
echo "task_id=$TASK_ID"

{/* Step 2: poll every 8 sec */}
while :; do
  S=$(curl -sS -H "Authorization: Bearer sk-your-api-key" "https://api.apiyi.com/v1/videos/$TASK_ID")
  ST=$(echo "$S" | python3 -c 'import sys,json;print(json.load(sys.stdin)["status"])')
  echo "status=$ST"
  [ "$ST" = "completed" ] && break
  [ "$ST" = "failed" ] && { echo "$S"; exit 1; }
  sleep 8
done

{/* Step 3: download (--retry covers occasional 400 right after status=completed) */}
sleep 4
curl -sSL --retry 3 --retry-delay 4 \
  -H "Authorization: Bearer sk-your-api-key" \
  "https://api.apiyi.com/v1/videos/$TASK_ID/content" \
  -o output.mp4
ls -lh output.mp4

Node.js (native fetch)

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: 'veo-3.1-fast-generate-preview',
        prompt: 'A golden retriever running on a sandy beach, slow motion, golden hour, cinematic quality',
        duration: '8',  // must be string
        size: '1280x720',
        metadata: { resolution: '720p', aspectRatio: '16:9' }
    })
});
const { task_id } = await submitResp.json();
console.log(`Task ID: ${task_id}`);

// Step 2: poll
let status = 'queued';
while (status !== 'completed' && status !== 'failed') {
    await new Promise(r => setTimeout(r, 8000));
    const s = await (await fetch(`${BASE_URL}/videos/${task_id}`, {
        headers: { 'Authorization': `Bearer ${API_KEY}` }
    })).json();
    status = s.status;
    console.log(`Status: ${status}, progress: ${s.progress ?? 0}%`);
}

if (status === 'failed') throw new Error('Generation failed');

// Step 3: download (retry up to 3 times, 4-second gaps)
await new Promise(r => setTimeout(r, 4000));
let buffer;
for (let i = 0; i < 4; i++) {
    try {
        const resp = await fetch(`${BASE_URL}/videos/${task_id}/content`, {
            headers: { 'Authorization': `Bearer ${API_KEY}` }
        });
        if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
        buffer = Buffer.from(await resp.arrayBuffer());
        break;
    } catch (e) {
        if (i === 3) throw e;
        await new Promise(r => setTimeout(r, 4000));
    }
}
fs.writeFileSync('output.mp4', buffer);
console.log('Saved: output.mp4');

Browser JavaScript

{/* Demo only; route through a backend proxy in production to avoid Key leakage; large video downloads are also unsuitable for direct browser handling */}
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: 'veo-3.1-fast-generate-preview',
        prompt: 'Watercolor northern lights over snowy mountains, gentle motion',
        duration: '4',
        size: '720x1280',
        metadata: { resolution: '720p', aspectRatio: '9:16' }
    })
});
const { task_id } = await submitResp.json();
console.log('Task ID:', task_id);

{/* After polling completes, route the /content endpoint through your backend proxy for download */}

Parameter Reference

ParamTypeRequiredDefaultDescription
modelstringYesveo-3.1-fast-generate-preview ($0.3/req) or veo-3.1-generate-preview ($1.2/req)
promptstringYesVideo description; describe scene, action, camera, lighting, audio intent (do not pass generateAudio)
durationstringNo"8"Video length, string enum: "4" / "6" / "8". 1080p/4k must be "8"
sizestringNo1280x720Output pixels, e.g. 1280x720 / 1920x1080 / 3840x2160; lower precedence than metadata.resolution
metadata.resolutionstringNo720p720p / 1080p / 4k; higher precedence than size
metadata.aspectRatiostringNo16:916:9 (landscape) or 9:16 (portrait)
metadata.seedintNoRandom seed. Fixed seed clusters outputs in style (but cannot byte-reproduce)
metadata.negativePromptstringNoNegative prompt; recommended "blurry, watermark, distorted, low quality"
Do not pass the generateAudio field! Veo 3.1 is natively audio-enabled; passing this parameter returns INVALID_ARGUMENT. To control audio, write the intent into your prompt: "waves, distant seabirds, low wind sounds".
Parameter precedence:
  • Duration: metadata.durationSeconds > duration > seconds > 8
  • Resolution: metadata.resolution > size > 720p
  • Aspect: explicit metadata.aspectRatio > inferred from size > 16:9

Response Format

Step 1 - immediately after submission

{
  "id": "task_xxxxxxxxxxxxxxxx",
  "task_id": "task_xxxxxxxxxxxxxxxx",
  "object": "video",
  "model": "veo-3.1-fast-generate-preview",
  "status": "queued",
  "progress": 0,
  "created_at": 1775025000
}

Step 2 - polling response (in progress)

{
  "id": "task_xxxxxxxxxxxxxxxx",
  "task_id": "task_xxxxxxxxxxxxxxxx",
  "object": "video",
  "model": "veo-3.1-fast-generate-preview",
  "status": "in_progress",
  "progress": 50,
  "created_at": 1775025000
}

Step 2 - polling response (completed)

{
  "id": "task_xxxxxxxxxxxxxxxx",
  "task_id": "task_xxxxxxxxxxxxxxxx",
  "object": "video",
  "model": "veo-3.1-fast-generate-preview",
  "status": "completed",
  "progress": 100,
  "created_at": 1775025000,
  "completed_at": 1775025090
}
⚠️ Response field gotchas
  • id and task_id are both returned with the same value; downstream should standardize on task_id (compatible with the existing Reverse channel)
  • No CDN / public URL is returned — no video_url / data.url in the response; the video can only be retrieved as an MP4 binary stream via GET /v1/videos/{task_id}/content (requires auth header). Frontends cannot hit this endpoint directly — download server-side and re-host on your own OSS / CDN
  • progress is coarse-grained — jumps only between 0 / 50 / 100, do not use for percentage bars
  • status: "failed" may not include a detailed error field; usually content review or parameter errors. Just retry or adjust the prompt
  • /content returns 400 occasionally right after status flips to completed; retry after 4 seconds (all code samples above have this baked in)
This endpoint is an async task entry. Billing happens when the task reaches completed, charged per request by model name (fast $0.3 / standard $1.2, see Pricing). POST submission, polling, and download themselves are not billed; failed tasks are also not billed.

Authorizations

Authorization
string
header
required

API Key from APIYI console (Default group + Pay-per-request or Pay-as-you-go Priority Token; pure Pay-as-you-go not supported)

Body

application/json
model
enum<string>
default:veo-3.1-fast-generate-preview
required

Model ID (per-request billing, duration / resolution do not affect price):

  • veo-3.1-fast-generate-preview — $0.3/request, top pick for iteration / batch generation
  • veo-3.1-generate-preview — $1.2/request, for final delivery / 4K scenarios
Available options:
veo-3.1-fast-generate-preview,
veo-3.1-generate-preview
prompt
string
required

Video generation prompt; describe in detail: scene + subject + action + camera + lighting + style.

Audio intent also goes in the prompt (e.g. "waves, distant seabirds, low wind sounds"). Do not pass generateAudio — upstream rejects with INVALID_ARGUMENT.

Example:

"A coastal lighthouse at dusk, slow push-in, waves lapping the rocks, distant seabirds, cinematic lighting, steady camera"

duration
enum<string>
default:8

Video length, string enum (not number):

  • "4" — 4 sec, 720p only
  • "6" — 6 sec, 720p only
  • "8" — 8 sec (default), required at 1080p / 4k

Passing a number (8) returns parse_request_failed: cannot unmarshal number into Go struct field ... duration of type string.

Available options:
4,
6,
8
size
enum<string>
default:1280x720

Output pixel dimensions; lower precedence than metadata.resolution:

  • 1280x720 / 720x1280 — 720p (default)
  • 1920x1080 / 1080x1920 — 1080p (duration must be "8")
  • 3840x2160 / 2160x3840 — 4k (duration must be "8", 4–6× slower render)
Available options:
1280x720,
720x1280,
1920x1080,
1080x1920,
3840x2160,
2160x3840
metadata
object

Wrapper for fine-grained generation parameters. Higher precedence than the top-level size etc.:

  • Duration resolution order: metadata.durationSeconds > duration > seconds > 8
  • Resolution resolution order: metadata.resolution > size > 720p

Response

Task submitted; returns task_id and queued status

id
string

Task ID (matches task_id; downstream should standardize on task_id)

Example:

"task_xxxxxxxxxxxxxxxx"

task_id
string

Task ID for subsequent polling and download

Example:

"task_xxxxxxxxxxxxxxxx"

object
string

Object type, fixed to video

Example:

"video"

model
string

Model ID used for this task

Example:

"veo-3.1-fast-generate-preview"

status
enum<string>

Task status:

  • queued — submitted, awaiting processing
  • in_progress — generating
  • completed — done, downloadable (/v1/videos/{task_id}/content)
  • failed — failed (not billed), retry possible
Available options:
queued,
in_progress,
completed,
failed
Example:

"queued"

progress
integer

Generation progress (coarse-grained, jumps only between 0 / 50 / 100, do not use for percentage bars)

Example:

0

created_at
integer

Task creation Unix timestamp (seconds)

Example:

1775025000

completed_at
integer

Task completion Unix timestamp (seconds); only present for completed status

Example:

1775025090