Skip to content
繁體中文
Generate Video

Generate Video Overview ​

Submit a video task, check progress, and download the result.

This tutorial uses the implemented OpenAI-compatible video routes with a Sora-type channel, including submission, polling, and download. Choose an enabled video model supporting this route; other channel types may use different fields and responses.

MethodPathPurpose
POST/v1/videosSubmit
GET/v1/videos/{id}Poll status
GET/v1/videos/{id}/contentDownload

API Calls in Multiple Languages ​

See language setup. Set API_KEY and replace model, file URL, and ID placeholders first. Each version displays the raw response to the same request.

These examples submit a task only. Keep the returned id for subsequent queries.

bash
curl --fail-with-body --silent --show-error --max-time 180 \
  --request POST \
  --url "https://api.tokatlas.ai/v1/videos" \
  --header "Authorization: Bearer $API_KEY" \
  --form model=YOUR_ENABLED_SORA_MODEL_ID \
  --form 'prompt=A ceramic cup on a table, steam rising, slow camera movement' \
  --form seconds=4 \
  --form size=720x1280
python
import os
import requests

headers = {
    'Authorization': 'Bearer ' + os.environ["API_KEY"],
}
files = {'model': (None, 'YOUR_ENABLED_SORA_MODEL_ID'),
 'prompt': (None, 'A ceramic cup on a table, steam rising, slow camera movement'),
 'seconds': (None, '4'),
 'size': (None, '720x1280')}
response = requests.request(
    'POST', 'https://api.tokatlas.ai/v1/videos', headers=headers,
    files=files,
    timeout=180,
)
response.raise_for_status()
print(response.text)
js
if (!process.env.API_KEY) throw new Error("Set API_KEY first.");
const form = new FormData();
form.append("model", "YOUR_ENABLED_SORA_MODEL_ID");
form.append("prompt", "A ceramic cup on a table, steam rising, slow camera movement");
form.append("seconds", "4");
form.append("size", "720x1280");
const response = await fetch("https://api.tokatlas.ai/v1/videos", {
  method: "POST",
  headers: {
    "Authorization": "Bearer " + process.env.API_KEY,
  },
  body: form,
  signal: AbortSignal.timeout(180_000),
});
if (!response.ok) {
  throw new Error(`HTTP ${response.status}: ${await response.text()}`);
}
console.log(await response.text());
java
import java.net.URI;
import java.net.http.*;
import java.time.Duration;

public class Example {
    public static void main(String[] args) throws Exception {
        String apiKey = System.getenv("API_KEY");
        if (apiKey == null || apiKey.isBlank()) {
            throw new IllegalArgumentException("Set API_KEY first.");
        }
        String boundary = "TokatlasExampleBoundary";
        String payload =
            "--TokatlasExampleBoundary\r\nContent-Disposition: form-data; name=\"model\"\r\n\r\nYOUR_ENABLED_SORA_MODEL_ID\r\n" +
            "--TokatlasExampleBoundary\r\nContent-Disposition: form-data; name=\"prompt\"\r\n\r\nA ceramic cup on a table, steam rising, slow camera movement\r\n" +
            "--TokatlasExampleBoundary\r\nContent-Disposition: form-data; name=\"seconds\"\r\n\r\n4\r\n" +
            "--TokatlasExampleBoundary\r\nContent-Disposition: form-data; name=\"size\"\r\n\r\n720x1280\r\n" +
            "--TokatlasExampleBoundary--\r\n";
        HttpClient client = HttpClient.newBuilder()
            .connectTimeout(Duration.ofSeconds(30)).build();
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://api.tokatlas.ai/v1/videos"))
            .timeout(Duration.ofSeconds(180))
            .header("Authorization", "Bearer " + apiKey)
            .header("Content-Type", "multipart/form-data; boundary=" + boundary)
            .method("POST", HttpRequest.BodyPublishers.ofString(payload))
            .build();
        HttpResponse<String> response = client.send(
            request, HttpResponse.BodyHandlers.ofString());
        if (response.statusCode() < 200 || response.statusCode() >= 300) {
            throw new IllegalStateException("HTTP " + response.statusCode() + ": "
                + response.body());
        }
        System.out.println(response.body());
    }
}

Query the Task ​

Replace YOUR_TASK_ID with the submission response’s id. Poll at intervals until status is completed or failed; do not submit new tasks while waiting.

bash
curl --fail-with-body --silent --show-error --max-time 180 \
  --request GET \
  --url "https://api.tokatlas.ai/v1/videos/YOUR_TASK_ID" \
  --header "Authorization: Bearer $API_KEY"
python
import os
import requests

headers = {
    'Authorization': 'Bearer ' + os.environ["API_KEY"],
}
response = requests.request(
    'GET', 'https://api.tokatlas.ai/v1/videos/YOUR_TASK_ID', headers=headers,
    timeout=180,
)
response.raise_for_status()
print(response.text)
js
if (!process.env.API_KEY) throw new Error("Set API_KEY first.");
const response = await fetch("https://api.tokatlas.ai/v1/videos/YOUR_TASK_ID", {
  method: "GET",
  headers: {
    "Authorization": "Bearer " + process.env.API_KEY,
  },
  signal: AbortSignal.timeout(180_000),
});
if (!response.ok) {
  throw new Error(`HTTP ${response.status}: ${await response.text()}`);
}
console.log(await response.text());
java
import java.net.URI;
import java.net.http.*;
import java.time.Duration;

public class Example {
    public static void main(String[] args) throws Exception {
        String apiKey = System.getenv("API_KEY");
        if (apiKey == null || apiKey.isBlank()) {
            throw new IllegalArgumentException("Set API_KEY first.");
        }
        HttpClient client = HttpClient.newBuilder()
            .connectTimeout(Duration.ofSeconds(30)).build();
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://api.tokatlas.ai/v1/videos/YOUR_TASK_ID"))
            .timeout(Duration.ofSeconds(180))
            .header("Authorization", "Bearer " + apiKey)
            .method("GET", HttpRequest.BodyPublishers.noBody())
            .build();
        HttpResponse<String> response = client.send(
            request, HttpResponse.BodyHandlers.ofString());
        if (response.statusCode() < 200 || response.statusCode() >= 300) {
            throw new IllegalStateException("HTTP " + response.statusCode() + ": "
                + response.body());
        }
        System.out.println(response.body());
    }
}

Download the Result ​

Run only when the status is completed. Use the same task ID; success saves video.mp4.

bash
curl --fail-with-body --silent --show-error --max-time 180 \
  --request GET \
  --url "https://api.tokatlas.ai/v1/videos/YOUR_TASK_ID/content" \
  --header "Authorization: Bearer $API_KEY" \
  --output video.mp4
python
import os
from pathlib import Path
import requests

headers = {
    'Authorization': 'Bearer ' + os.environ["API_KEY"],
}
response = requests.request(
    'GET', 'https://api.tokatlas.ai/v1/videos/YOUR_TASK_ID/content', headers=headers,
    timeout=180,
)
response.raise_for_status()
Path('video.mp4').write_bytes(response.content)
print("Saved video.mp4")
js
import { writeFile } from "node:fs/promises";

if (!process.env.API_KEY) throw new Error("Set API_KEY first.");
const response = await fetch("https://api.tokatlas.ai/v1/videos/YOUR_TASK_ID/content", {
  method: "GET",
  headers: {
    "Authorization": "Bearer " + process.env.API_KEY,
  },
  signal: AbortSignal.timeout(180_000),
});
if (!response.ok) {
  throw new Error(`HTTP ${response.status}: ${await response.text()}`);
}
await writeFile("video.mp4", Buffer.from(await response.arrayBuffer()));
console.log("Saved video.mp4");
java
import java.net.URI;
import java.net.http.*;
import java.time.Duration;
import java.nio.file.*;

public class Example {
    public static void main(String[] args) throws Exception {
        String apiKey = System.getenv("API_KEY");
        if (apiKey == null || apiKey.isBlank()) {
            throw new IllegalArgumentException("Set API_KEY first.");
        }
        HttpClient client = HttpClient.newBuilder()
            .connectTimeout(Duration.ofSeconds(30)).build();
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://api.tokatlas.ai/v1/videos/YOUR_TASK_ID/content"))
            .timeout(Duration.ofSeconds(180))
            .header("Authorization", "Bearer " + apiKey)
            .method("GET", HttpRequest.BodyPublishers.noBody())
            .build();
        HttpResponse<byte[]> response = client.send(
            request, HttpResponse.BodyHandlers.ofByteArray());
        if (response.statusCode() < 200 || response.statusCode() >= 300) {
            throw new IllegalStateException("HTTP " + response.statusCode() + ": "
                + new String(response.body(), java.nio.charset.StandardCharsets.UTF_8));
        }
        Files.write(Path.of("video.mp4"), response.body());
        System.out.println("Saved video.mp4");
    }
}

1. Prepare ​

Install Python 3 and set your key and enabled Sora-compatible model ID. The example uses 4 seconds and 720×1280; select a model supporting those settings.

bash
python3 -m pip install requests
export API_KEY='YOUR_TOKATLAS_API_KEY'
export VIDEO_MODEL_ID='YOUR_ENABLED_SORA_MODEL_ID'

2. Submit, Wait, and Download ​

Save as generate_video.py and run python3 generate_video.py. It saves video-task.txt immediately; running again resumes polling that task. Rename the ID file to preserve it before starting a new task. Avoid repeated submissions while an outcome is unknown.

python
import os
import time
from pathlib import Path
import requests

base = os.environ.get("API_BASE_URL", "https://api.tokatlas.ai").rstrip("/")
headers = {"Authorization": f"Bearer {os.environ['API_KEY']}"}
task_file = Path("video-task.txt")
if task_file.exists():
    task_id = task_file.read_text().strip()
else:
    response = requests.post(
        base + "/v1/videos", headers=headers,
        files={"model": (None, os.environ["VIDEO_MODEL_ID"]),
               "prompt": (None, "A ceramic cup on a table, steam rising, slow camera movement"),
               "seconds": (None, "4"), "size": (None, "720x1280")},
        timeout=120,
    )
    response.raise_for_status()
    body = response.json()
    task_id = body["id"]
    task_file.write_text(task_id)
print("Task:", task_id)
for attempt in range(120):
    response = requests.get(base + f"/v1/videos/{task_id}", headers=headers, timeout=60)
    response.raise_for_status()
    task = response.json()
    status = task.get("status")
    print(status, task.get("progress", ""))
    if status == "completed":
        with requests.get(base + f"/v1/videos/{task_id}/content",
                          headers=headers, stream=True, timeout=120) as download:
            download.raise_for_status()
            with Path("video.mp4").open("wb") as output:
                for chunk in download.iter_content(1024 * 1024):
                    output.write(chunk)
        print("Saved video.mp4")
        break
    if status == "failed":
        raise RuntimeError(task.get("error") or task)
    if status not in {"queued", "in_progress"}:
        raise RuntimeError(f"Unexpected task response: {task}")
    time.sleep(5)
else:
    raise TimeoutError("Task still pending; run again to resume polling the saved ID")

3. Interpret the Result ​

queued and in_progress are pending states. completed triggers download to video.mp4; inspect error when failed. The polling limit stops local waiting, not the remote task. Run again later to resume polling.

Troubleshooting ​

  • 400: check model, seconds, and pixel dimensions. Do not use an image aspect ratio such as 1:1 as video size.
  • 401 / 403: check the key and access to the video model.
  • Task not found: use the returned public id and a key belonging to the submitting account.
  • Download failure: check for completed; retain the task ID and error body.
  • Timeout or 5xx: resume querying a saved ID. If submission returned no ID, check request records before resubmitting.

See File Analysis for image input and Tool Calling for chat integration.