curl --request GET \
--url 'https://toapis.com/v1/videos/generations/task_01K9S419324DREZFBWNSVXYR6H' \
--header 'Authorization: Bearer <token>'
import requests
import time
API_BASE = 'https://toapis.com'
API_KEY = 'sk-xxxxxxxxxxxxxxxxxxxxxx'
headers = {
'Authorization': f'Bearer {API_KEY}'
}
def get_video_status(task_id):
response = requests.get(f'{API_BASE}/v1/videos/generations/{task_id}', headers=headers)
return response.json()
def wait_for_video(task_id, max_attempts=60, interval=10):
for _ in range(max_attempts):
result = get_video_status(task_id)
status = result.get('status')
progress = result.get('progress', 0)
print(f"Status: {status}, Progress: {progress}%")
if status == 'completed':
return result
elif status == 'failed':
raise Exception(f"Task failed: {result}")
time.sleep(interval)
raise Exception("Task timeout")
# Usage example
task_id = "task_01K9S419324DREZFBWNSVXYR6H"
result = wait_for_video(task_id)
print(f"Video URL: {result['result']['data'][0]['url']}")
const API_BASE = 'https://toapis.com';
const API_KEY = 'sk-xxxxxxxxxxxxxxxxxxxxxx';
async function getVideoStatus(taskId) {
const response = await fetch(`${API_BASE}/v1/videos/generations/${taskId}`, {
headers: {
'Authorization': `Bearer ${API_KEY}`
}
});
return response.json();
}
async function waitForVideo(taskId, maxAttempts = 60, interval = 10000) {
for (let i = 0; i < maxAttempts; i++) {
const result = await getVideoStatus(taskId);
const status = result.status;
const progress = result.progress || 0;
console.log(`Status: ${status}, Progress: ${progress}%`);
if (status === 'completed') {
return result;
} else if (status === 'failed') {
throw new Error(`Task failed: ${JSON.stringify(result)}`);
}
await new Promise(r => setTimeout(r, interval));
}
throw new Error('Task timeout');
}
// Usage example
const taskId = 'task_01K9S419324DREZFBWNSVXYR6H';
waitForVideo(taskId).then(result => {
console.log('Video URL:', result.result.data[0].url);
console.log('Format:', result.result.data[0].format);
});
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"time"
)
func getVideoStatus(taskId string) (map[string]interface{}, error) {
url := fmt.Sprintf("https://toapis.com/v1/videos/generations/%s", taskId)
req, _ := http.NewRequest("GET", url, nil)
req.Header.Set("Authorization", "Bearer <token>")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
var result map[string]interface{}
json.Unmarshal(body, &result)
return result, nil
}
func main() {
taskId := "task_01K9S419324DREZFBWNSVXYR6H"
for i := 0; i < 60; i++ {
result, _ := getVideoStatus(taskId)
status := result["status"].(string)
progress := int(result["progress"].(float64))
fmt.Printf("Status: %s, Progress: %d%%\n", status, progress)
if status == "completed" {
fmt.Println("Video generation completed!")
resultData := result["result"].(map[string]interface{})
dataArray := resultData["data"].([]interface{})
videoData := dataArray[0].(map[string]interface{})
fmt.Println("Video URL:", videoData["url"])
break
}
time.Sleep(10 * time.Second)
}
}
{
"id": "video_7497f4d5-3a88-44c7-923a-967fa7d941a0",
"object": "generation.task",
"model": "sora-2",
"status": "queued",
"progress": 0,
"created_at": 1768380222
}
{
"id": "video_7497f4d5-3a88-44c7-923a-967fa7d941a0",
"object": "generation.task",
"model": "sora-2",
"status": "in_progress",
"progress": 65,
"created_at": 1768380222
}
{
"id": "tsk_vid_01KRDG4S50XXXXXXXXXXXXXXX",
"object": "generation.task",
"model": "seedance-2",
"status": "completed",
"progress": 100,
"created_at": 1778569600,
"finished_at": 1778569900,
"completed_at": 1778569900,
"expires_at": 1778656300,
"result": {
"type": "video",
"data": [
{
"url": "https://files.toapis.com/videos/tsk_vid_01KRDG4S50XXXXXXXXXXXXXXX/********_********.mp4",
"format": "mp4"
}
]
},
"billing": {
"status": "settled",
"credits": "100",
"cost_usd": "0.5"
}
}
{
"id": "video_7497f4d5-3a88-44c7-923a-967fa7d941a0",
"object": "generation.task",
"model": "sora-2",
"status": "failed",
"progress": 0,
"created_at": 1768380222,
"error": {
"code": "generation_failed",
"message": "Generation failed: Content violates the content policy"
}
}
{
"error": {
"code": 404,
"message": "Task not found",
"type": "not_found_error"
}
}
{
"error": {
"code": 401,
"message": "Authentication failed, please check your API key",
"type": "authentication_error"
}
}
{
"id": "task_seedance_example",
"object": "generation.task",
"model": "seedance-2-global",
"client_business_id": "order_123",
"status": "completed",
"progress": 100,
"created_at": 1792400000,
"updated_at": 1792400061,
"finished_at": 1792400060,
"completed_at": 1792400060,
"expires_at": 1792486460,
"result": {
"type": "video",
"data": [
{
"url": "https://cdn.example/result.mp4",
"fallback_url": "https://files.toapis.com/videos/task_seedance_example/result.mp4",
"format": "mp4",
"last_frame_url": "https://cdn.example/last-frame.jpg",
"fallback_last_frame_url": "https://files.toapis.com/videos/task_seedance_example/last-frame.jpg"
}
]
},
"usage": {
"charge_type": "per_token",
"billed_tokens": 87300,
"completion_tokens": 87300,
"total_tokens": 87300
},
"billing": {
"status": "settled",
"credits": "87.3",
"cost_usd": "0.4365"
},
"generation": {
"duration": 5,
"resolution": "720p",
"ratio": "16:9",
"generate_audio": true,
"usage": {
"completion_tokens": 87300,
"total_tokens": 87300
}
}
}
Task Management
Get Video Task Status
Query video generation task status and results
GET
/
v1
/
videos
/
generations
/
{task_id}
curl --request GET \
--url 'https://toapis.com/v1/videos/generations/task_01K9S419324DREZFBWNSVXYR6H' \
--header 'Authorization: Bearer <token>'
import requests
import time
API_BASE = 'https://toapis.com'
API_KEY = 'sk-xxxxxxxxxxxxxxxxxxxxxx'
headers = {
'Authorization': f'Bearer {API_KEY}'
}
def get_video_status(task_id):
response = requests.get(f'{API_BASE}/v1/videos/generations/{task_id}', headers=headers)
return response.json()
def wait_for_video(task_id, max_attempts=60, interval=10):
for _ in range(max_attempts):
result = get_video_status(task_id)
status = result.get('status')
progress = result.get('progress', 0)
print(f"Status: {status}, Progress: {progress}%")
if status == 'completed':
return result
elif status == 'failed':
raise Exception(f"Task failed: {result}")
time.sleep(interval)
raise Exception("Task timeout")
# Usage example
task_id = "task_01K9S419324DREZFBWNSVXYR6H"
result = wait_for_video(task_id)
print(f"Video URL: {result['result']['data'][0]['url']}")
const API_BASE = 'https://toapis.com';
const API_KEY = 'sk-xxxxxxxxxxxxxxxxxxxxxx';
async function getVideoStatus(taskId) {
const response = await fetch(`${API_BASE}/v1/videos/generations/${taskId}`, {
headers: {
'Authorization': `Bearer ${API_KEY}`
}
});
return response.json();
}
async function waitForVideo(taskId, maxAttempts = 60, interval = 10000) {
for (let i = 0; i < maxAttempts; i++) {
const result = await getVideoStatus(taskId);
const status = result.status;
const progress = result.progress || 0;
console.log(`Status: ${status}, Progress: ${progress}%`);
if (status === 'completed') {
return result;
} else if (status === 'failed') {
throw new Error(`Task failed: ${JSON.stringify(result)}`);
}
await new Promise(r => setTimeout(r, interval));
}
throw new Error('Task timeout');
}
// Usage example
const taskId = 'task_01K9S419324DREZFBWNSVXYR6H';
waitForVideo(taskId).then(result => {
console.log('Video URL:', result.result.data[0].url);
console.log('Format:', result.result.data[0].format);
});
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"time"
)
func getVideoStatus(taskId string) (map[string]interface{}, error) {
url := fmt.Sprintf("https://toapis.com/v1/videos/generations/%s", taskId)
req, _ := http.NewRequest("GET", url, nil)
req.Header.Set("Authorization", "Bearer <token>")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
var result map[string]interface{}
json.Unmarshal(body, &result)
return result, nil
}
func main() {
taskId := "task_01K9S419324DREZFBWNSVXYR6H"
for i := 0; i < 60; i++ {
result, _ := getVideoStatus(taskId)
status := result["status"].(string)
progress := int(result["progress"].(float64))
fmt.Printf("Status: %s, Progress: %d%%\n", status, progress)
if status == "completed" {
fmt.Println("Video generation completed!")
resultData := result["result"].(map[string]interface{})
dataArray := resultData["data"].([]interface{})
videoData := dataArray[0].(map[string]interface{})
fmt.Println("Video URL:", videoData["url"])
break
}
time.Sleep(10 * time.Second)
}
}
{
"id": "video_7497f4d5-3a88-44c7-923a-967fa7d941a0",
"object": "generation.task",
"model": "sora-2",
"status": "queued",
"progress": 0,
"created_at": 1768380222
}
{
"id": "video_7497f4d5-3a88-44c7-923a-967fa7d941a0",
"object": "generation.task",
"model": "sora-2",
"status": "in_progress",
"progress": 65,
"created_at": 1768380222
}
{
"id": "tsk_vid_01KRDG4S50XXXXXXXXXXXXXXX",
"object": "generation.task",
"model": "seedance-2",
"status": "completed",
"progress": 100,
"created_at": 1778569600,
"finished_at": 1778569900,
"completed_at": 1778569900,
"expires_at": 1778656300,
"result": {
"type": "video",
"data": [
{
"url": "https://files.toapis.com/videos/tsk_vid_01KRDG4S50XXXXXXXXXXXXXXX/********_********.mp4",
"format": "mp4"
}
]
},
"billing": {
"status": "settled",
"credits": "100",
"cost_usd": "0.5"
}
}
{
"id": "video_7497f4d5-3a88-44c7-923a-967fa7d941a0",
"object": "generation.task",
"model": "sora-2",
"status": "failed",
"progress": 0,
"created_at": 1768380222,
"error": {
"code": "generation_failed",
"message": "Generation failed: Content violates the content policy"
}
}
{
"error": {
"code": 404,
"message": "Task not found",
"type": "not_found_error"
}
}
{
"error": {
"code": 401,
"message": "Authentication failed, please check your API key",
"type": "authentication_error"
}
}
{
"id": "task_seedance_example",
"object": "generation.task",
"model": "seedance-2-global",
"client_business_id": "order_123",
"status": "completed",
"progress": 100,
"created_at": 1792400000,
"updated_at": 1792400061,
"finished_at": 1792400060,
"completed_at": 1792400060,
"expires_at": 1792486460,
"result": {
"type": "video",
"data": [
{
"url": "https://cdn.example/result.mp4",
"fallback_url": "https://files.toapis.com/videos/task_seedance_example/result.mp4",
"format": "mp4",
"last_frame_url": "https://cdn.example/last-frame.jpg",
"fallback_last_frame_url": "https://files.toapis.com/videos/task_seedance_example/last-frame.jpg"
}
]
},
"usage": {
"charge_type": "per_token",
"billed_tokens": 87300,
"completion_tokens": 87300,
"total_tokens": 87300
},
"billing": {
"status": "settled",
"credits": "87.3",
"cost_usd": "0.4365"
},
"generation": {
"duration": 5,
"resolution": "720p",
"ratio": "16:9",
"generate_audio": true,
"usage": {
"completion_tokens": 87300,
"total_tokens": 87300
}
}
}
Note for users in mainland China: Please use
https://toapis.cn as the API endpoint (Base URL). Replace https://toapis.com with https://toapis.cn in the examples in this document.- Query async video generation task execution status and results
- Real-time status updates and progress tracking
- Get generated videos when task completes
- Multi-language support (zh/en/ko/ja)
Seedance video tasks that require asset review use this same status endpoint.
queued may include asset preparation and review; receiving a task does not mean video generation has started. Preparation defaults to 20 minutes per round, excluding subsequent video generation. Inspect error.message on failure.GET /v1/tasks/{task_id}, using the same usage and billing definitions. This page describes responses from the generation-task query path shown above.
Examples illustrate the fields, not current model prices or a fixed billing mode. Use the fields and amounts returned for the actual task.
Seedance original and fallback URLs
For Seedance tasks with original URL delivery enabled,result.data[].url preserves the original upstream video URL. Use url first and switch to fallback_url on the same result item only if the primary URL cannot be accessed. The platform does not switch URLs automatically based on the client region.
Fallback fields are optional and are returned only when a copy is available. They may be omitted when original URL delivery is disabled, a copy is unavailable, or a historical task has no copy. Omission does not mean generation failed. For the last frame, use last_frame_url first and fallback_last_frame_url only if it cannot be accessed. A fallback does not extend the original URL validity period. Download promptly if you need long-term storage.
Authorizations
string
required
All endpoints require Bearer Token authenticationGet your API Key:Visit the API Key Management Page to get your API KeyAdd it to the request header:
Authorization: Bearer YOUR_API_KEY
Path Parameters
string
required
Task ID returned by the video generation API
curl --request GET \
--url 'https://toapis.com/v1/videos/generations/task_01K9S419324DREZFBWNSVXYR6H' \
--header 'Authorization: Bearer <token>'
import requests
import time
API_BASE = 'https://toapis.com'
API_KEY = 'sk-xxxxxxxxxxxxxxxxxxxxxx'
headers = {
'Authorization': f'Bearer {API_KEY}'
}
def get_video_status(task_id):
response = requests.get(f'{API_BASE}/v1/videos/generations/{task_id}', headers=headers)
return response.json()
def wait_for_video(task_id, max_attempts=60, interval=10):
for _ in range(max_attempts):
result = get_video_status(task_id)
status = result.get('status')
progress = result.get('progress', 0)
print(f"Status: {status}, Progress: {progress}%")
if status == 'completed':
return result
elif status == 'failed':
raise Exception(f"Task failed: {result}")
time.sleep(interval)
raise Exception("Task timeout")
# Usage example
task_id = "task_01K9S419324DREZFBWNSVXYR6H"
result = wait_for_video(task_id)
print(f"Video URL: {result['result']['data'][0]['url']}")
const API_BASE = 'https://toapis.com';
const API_KEY = 'sk-xxxxxxxxxxxxxxxxxxxxxx';
async function getVideoStatus(taskId) {
const response = await fetch(`${API_BASE}/v1/videos/generations/${taskId}`, {
headers: {
'Authorization': `Bearer ${API_KEY}`
}
});
return response.json();
}
async function waitForVideo(taskId, maxAttempts = 60, interval = 10000) {
for (let i = 0; i < maxAttempts; i++) {
const result = await getVideoStatus(taskId);
const status = result.status;
const progress = result.progress || 0;
console.log(`Status: ${status}, Progress: ${progress}%`);
if (status === 'completed') {
return result;
} else if (status === 'failed') {
throw new Error(`Task failed: ${JSON.stringify(result)}`);
}
await new Promise(r => setTimeout(r, interval));
}
throw new Error('Task timeout');
}
// Usage example
const taskId = 'task_01K9S419324DREZFBWNSVXYR6H';
waitForVideo(taskId).then(result => {
console.log('Video URL:', result.result.data[0].url);
console.log('Format:', result.result.data[0].format);
});
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"time"
)
func getVideoStatus(taskId string) (map[string]interface{}, error) {
url := fmt.Sprintf("https://toapis.com/v1/videos/generations/%s", taskId)
req, _ := http.NewRequest("GET", url, nil)
req.Header.Set("Authorization", "Bearer <token>")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
var result map[string]interface{}
json.Unmarshal(body, &result)
return result, nil
}
func main() {
taskId := "task_01K9S419324DREZFBWNSVXYR6H"
for i := 0; i < 60; i++ {
result, _ := getVideoStatus(taskId)
status := result["status"].(string)
progress := int(result["progress"].(float64))
fmt.Printf("Status: %s, Progress: %d%%\n", status, progress)
if status == "completed" {
fmt.Println("Video generation completed!")
resultData := result["result"].(map[string]interface{})
dataArray := resultData["data"].([]interface{})
videoData := dataArray[0].(map[string]interface{})
fmt.Println("Video URL:", videoData["url"])
break
}
time.Sleep(10 * time.Second)
}
}
{
"id": "video_7497f4d5-3a88-44c7-923a-967fa7d941a0",
"object": "generation.task",
"model": "sora-2",
"status": "queued",
"progress": 0,
"created_at": 1768380222
}
{
"id": "video_7497f4d5-3a88-44c7-923a-967fa7d941a0",
"object": "generation.task",
"model": "sora-2",
"status": "in_progress",
"progress": 65,
"created_at": 1768380222
}
{
"id": "tsk_vid_01KRDG4S50XXXXXXXXXXXXXXX",
"object": "generation.task",
"model": "seedance-2",
"status": "completed",
"progress": 100,
"created_at": 1778569600,
"finished_at": 1778569900,
"completed_at": 1778569900,
"expires_at": 1778656300,
"result": {
"type": "video",
"data": [
{
"url": "https://files.toapis.com/videos/tsk_vid_01KRDG4S50XXXXXXXXXXXXXXX/********_********.mp4",
"format": "mp4"
}
]
},
"billing": {
"status": "settled",
"credits": "100",
"cost_usd": "0.5"
}
}
{
"id": "video_7497f4d5-3a88-44c7-923a-967fa7d941a0",
"object": "generation.task",
"model": "sora-2",
"status": "failed",
"progress": 0,
"created_at": 1768380222,
"error": {
"code": "generation_failed",
"message": "Generation failed: Content violates the content policy"
}
}
{
"error": {
"code": 404,
"message": "Task not found",
"type": "not_found_error"
}
}
{
"error": {
"code": 401,
"message": "Authentication failed, please check your API key",
"type": "authentication_error"
}
}
{
"id": "task_seedance_example",
"object": "generation.task",
"model": "seedance-2-global",
"client_business_id": "order_123",
"status": "completed",
"progress": 100,
"created_at": 1792400000,
"updated_at": 1792400061,
"finished_at": 1792400060,
"completed_at": 1792400060,
"expires_at": 1792486460,
"result": {
"type": "video",
"data": [
{
"url": "https://cdn.example/result.mp4",
"fallback_url": "https://files.toapis.com/videos/task_seedance_example/result.mp4",
"format": "mp4",
"last_frame_url": "https://cdn.example/last-frame.jpg",
"fallback_last_frame_url": "https://files.toapis.com/videos/task_seedance_example/last-frame.jpg"
}
]
},
"usage": {
"charge_type": "per_token",
"billed_tokens": 87300,
"completion_tokens": 87300,
"total_tokens": 87300
},
"billing": {
"status": "settled",
"credits": "87.3",
"cost_usd": "0.4365"
},
"generation": {
"duration": 5,
"resolution": "720p",
"ratio": "16:9",
"generate_audio": true,
"usage": {
"completion_tokens": 87300,
"total_tokens": 87300
}
}
}
Response
string
Unique task identifier
string
Optional customer business ID, returned only when
client_business_id was supplied at task creation.string
Object type, always
generation.taskstring
Task status
queued- Queued for processingin_progress- Processingcompleted- Successfully completedfailed- Failed
integer
Task progress percentage (0-100)
string
Video generation model used
integer
Optional platform task update time as a Unix timestamp, currently returned for Seedance Global.
generation.updated_at is the upstream update time.integer
Task creation time (Unix timestamp)
integer
Task finish time as a Unix timestamp. This query path returns it for successful or failed tasks when a finish time has been saved.
integer
Task completion time (Unix timestamp, only returned when completed)
integer
Video URL expiration time (Unix timestamp, only returned on completion)
object
Task result (only returned on success)
Show Properties
Show Properties
string
Result type, always
videoarray
Video data array
Show Array item
Show Array item
string
Primary video URL. Seedance tasks with original URL delivery enabled preserve the original upstream URL. Use this URL first.
string
Optional backup video URL, returned only when a copy is available. Use only if the primary URL cannot be accessed. It does not replace
url.string
Video format (for example
mp4)string
URL of the generated video’s last-frame image. Present only when the task was created with
return_last_frame: true and the model returned a frame.string
Optional backup last-frame URL, returned when a frame copy is available. Use only if
last_frame_url cannot be accessed.object
Optional Seedance generation metadata. Only saved, publicly available fields are returned; defaults are not filled in. These parameters describe generation and do not replace the billing quantities in top-level
usage.| Field | Type | Description |
|---|---|---|
created_at, updated_at | number | Upstream task creation and update times, as Unix timestamps; distinct from platform timestamps at the top level |
seed | number | Random seed |
resolution | string | Resolution |
ratio | string | Aspect ratio |
duration | number | Generated duration in seconds |
framespersecond | number | Frames per second |
service_tier | string | Service tier |
execution_expires_after | number | Execution validity period in seconds |
generate_audio | boolean | Whether audio is generated |
draft | boolean | Whether this is a draft |
priority | number | Scheduling priority |
output_format | string | Output format |
generation.usage is an existing compatibility field that may contain completion_tokens and total_tokens. It may be absent or differ from final billed quantities. Do not add it to top-level usage; use top-level usage for billing quantities.object
Optional billing quantities, video-token details, and tool counts. See Task usage fields for units and omission rules.
| Field | Type | Values |
|---|---|---|
charge_type | string | Billing mode: per_request, per_second, or per_token |
requests | integer | Billed request count, returned for per_request. The effective Midjourney video batch_size is counted |
billed_tokens | integer | Tokens used for pricing, returned for per_token |
output_seconds | integer | Billed output video duration in whole seconds, returned for per_second |
input_seconds | integer | Billed input video duration in whole seconds; omitted when zero |
input_images | integer | Billed input images after the free allowance; omitted when zero |
input_tokens, output_tokens, prompt_tokens, completion_tokens, total_tokens | integer | Tokens reported by the video model, as provided; per-token settlement can fill total_tokens |
tool_usage | object | Tool counts: web_search, image_search, grounding_queries, returned when used |
charge_type through input_images are billing quantities and are returned only together with billing.status=settled. Fixed-price videos return the quantities confirmed at submission; videos settled by actual duration or tokens return the final settled quantities. Fields that are not returned are omitted, not filled with zeros.object
Optional platform billing status and confirmed charge. Billing status is independent of generation status;
Task Webhook
completed does not guarantee settlement. A missing object does not mean the task was free. See Pricing and Actual Charges.Show Properties
Show Properties
string
pending: generating, awaiting settlement, or refund not yet confirmed; amounts are omitted. settled: the final charge is confirmed, including fixed-price videos charged once at submission, and the actual amount is returned. refunded: refunded, or confirmed to have no net charge after failure; both amounts are "0".string
ToAPIs Credits actually charged, as a decimal string. Returned only for
settled or refunded.string
USD amount of the same charge, as a decimal string. Returned under the same conditions as
credits; includes discounts and multipliers and is not the provider cost.data.billing uses the same confirmation rules and values.object
Error information (only returned on failure)Error codes and messages are normally strings. Seedance Global preserves the original upstream JSON values in
error.code and error.message, including numbers or null. Missing upstream fields are not replaced with platform defaults.Task Status Reference
| Status | Description | Is Final | Recommended Action |
|---|---|---|---|
queued | Task submitted, waiting in queue | ❌ | Wait 5-10 seconds, then query again |
in_progress | Task is processing | ❌ | Wait 10-15 seconds, then query again |
completed | Task completed successfully | ✅ | Get video from result.data[0].url |
failed | Task processing failed | ✅ | Check error info |
Polling Strategy
Initial wait: 5 seconds
Polling interval: 10 seconds
Max wait: 600 seconds (10 minutes)
Typical time: 1-5 minutes
Python Polling Example
import time
import random
import requests
def poll_video_task(task_id, api_key, max_wait=600):
"""Poll video generation task until completion or timeout"""
start_time = time.time()
interval = 10 # 10 second interval
# Initial 5 second wait
time.sleep(5)
while time.time() - start_time < max_wait:
response = requests.get(
f'https://toapis.com/v1/videos/generations/{task_id}',
headers={'Authorization': f'Bearer {api_key}'}
)
if response.status_code == 429:
retry_after = int(response.headers.get('Retry-After', interval))
time.sleep(retry_after + random.uniform(0, 1))
interval = min(interval * 2, 120)
continue
response.raise_for_status()
data = response.json()
print(f"Status: {data['status']}, Progress: {data.get('progress', 0)}%")
if data['status'] == 'completed':
return {
'url': data['result']['data'][0]['url'],
'format': data['result']['data'][0].get('format'),
'expires_at': data.get('expires_at')
}
elif data['status'] == 'failed':
raise Exception(f"Generation failed: {data['error']['message']}")
time.sleep(interval + random.uniform(0, 1))
raise TimeoutError("Task timeout")
Resource Expiration
Generated video URLs are valid for 24 hours
- Please download and save videos within the validity period
expires_atfield indicates video expiration time (Unix timestamp)- Expired videos cannot be accessed; to regenerate, submit a new task
- Thumbnails expire simultaneously with videos
Errors and failure reasons
See the error reference for request errors, task failures, specific causes, and recovery guidance. A successful query still requires checking task status; HTTP status is not a business error code.Performance Tips
Video generation takes longer, recommendations:
- Use the ToAPIs Task Webhook: Prefer Webhooks and keep polling as fallback
- Set a Reasonable Polling Interval: At least 5–10 seconds with jitter; on 429, honor
Retry-Afterand back off exponentially - Set Timeout: Long videos may take 5-10 minutes, set reasonable timeout
- Download Promptly: Videos expire after 24 hours, save to your storage immediately