Video Generation
Generate videos from a text prompt using the OpenAI Videos API format. Generation is asynchronous: create a job, poll it until it completes, then download the MP4. All three endpoints use the same API key as the chat endpoints.
Base URL: https://api.techopenclaw.com — authentication uses the same Authorization: Bearer toc-..... header as the chat endpoints (details in API Reference).
Create a video generation job — one request produces one video (there is no n/count parameter; send parallel requests for several clips). Returns immediately with the initial job status (usually queued).
| Field | Type | Required | Description |
|---|---|---|---|
model | string | Yes | Video model name from the LLM Catalog. Replace seedance_2.5 in the examples with a video model available to your account. |
prompt | string | Yes | Text description of the video to generate. |
seconds | string | No | Clip duration. Each model accepts its own set of values (see the model table below); an unsupported value returns 400 with the accepted list. Omitted = the model default. |
size | string | No | Output frame size, 720x1280 by default. Every model takes the four OpenAI sizes; the Seedance models also take four extra ratios — see the frame size table. Unsupported values return 400 with the accepted list. |
aspect_ratio | string | No | Alternative to size: a ratio like 16:9 is translated to its size. Unknown or blank values are ignored (the default size applies). |
input_reference | array / file | No | Reference images for the model (first frame, style frames, …). Multiple images are accepted — in JSON pass an array of { "image_url": "https://…" } objects (https or data URL), in multipart repeat the input_reference part once per image. Order is kept. Each model caps how many it takes (10 for Seedance, 3 for Veo and Omni) — more returns 400. An empty or missing field means text-to-video. |
last_frame | object | No | End frame, as one { "image_url": … } object. Veo-3.1 only, and only together with exactly one input_reference image (the first frame). |
input_video | string | No | Source video URL (video-to-video), https only. Omni-Flash only; it also accepts up to 3 input_reference images alongside. |
resolution | string | No | 720p (default) or 1080p on Veo-3.1 (upscaled render). On models that do not offer it the value is ignored. |
curl https://api.techopenclaw.com/v1/videos \
-H "Authorization: Bearer toc-....." \
-H "Content-Type: application/json" \
-d '{
"model": "seedance_2.5",
"prompt": "A slow dolly shot across a neon-lit city street at night",
"seconds": "5",
"size": "1280x720",
"input_reference": [
{ "image_url": "https://example.com/first-frame.jpg" },
{ "image_url": "https://example.com/style-frame.jpg" }
]
}'curl https://api.techopenclaw.com/v1/videos \ -H "Authorization: Bearer toc-....." \ -F model="Veo-3.1" \ -F "prompt=A slow dolly shot across a neon-lit city street at night" \ -F seconds="8" \ -F size="1280x720" \ -F [email protected] \ -F [email protected]
{
"id": "video_abc123",
"object": "video",
"model": "seedance_2.5",
"status": "queued",
"progress": 0,
"created_at": 1790567494,
"completed_at": null,
"expires_at": null,
"prompt": "A slow dolly shot across a neon-lit city street at night",
"size": "1280x720",
"seconds": "5",
"remixed_from_video_id": null,
"error": null
}Works with the OpenAI SDK by pointing base_url at Techopenclaw:
from openai import OpenAI
client = OpenAI(api_key="toc-.....", base_url="https://api.techopenclaw.com/v1")
video = client.videos.create_and_poll(
model="seedance_2.5",
prompt="A slow dolly shot across a neon-lit city street at night",
seconds="5",
size="1280x720",
)
content = client.videos.download_content(video.id)
content.write_to_file("video.mp4")Get the current state of a job. Poll this endpoint until status is completed or failed; sleep between rounds following the openai-poll-after-ms response header (milliseconds, default 10000). Typical completion time is 1–10 minutes.
curl https://api.techopenclaw.com/v1/videos/video_abc123 \ -H "Authorization: Bearer toc-....."
{
"id": "video_abc123",
"object": "video",
"model": "seedance_2.5",
"status": "completed",
"progress": 100,
"created_at": 1790567494,
"completed_at": 1790567670,
"expires_at": null,
"prompt": "A slow dolly shot across a neon-lit city street at night",
"size": "1280x720",
"seconds": "5",
"remixed_from_video_id": null,
"error": null
}| Status | Meaning |
|---|---|
queued | Job accepted, waiting to start |
in_progress | Rendering |
completed | Done — download via the content endpoint |
failed | Job failed — see the error field |
Download the finished MP4. Only available while status is completed — earlier calls return 404 with a not-ready message. The response body is the raw video file (Content-Type: video/mp4), not JSON.
curl "https://api.techopenclaw.com/v1/videos/video_abc123/content?variant=video" \ -H "Authorization: Bearer toc-....." \ -o video.mp4
Models & limits
What each video model accepts — durations, frame ratios, how many reference images, and its extras:
| Model | seconds | Frame ratios | Max input images | Extras |
|---|---|---|---|---|
seedance_2.0_fast | 5, 10, 15 (default 5) | all six ratios | 10 | — |
seedance_2.5 | 5–30 (default 5) | all six ratios | 10 | — |
Veo-3.1 | 4, 6, 8 (default 8) | 9:16, 16:9 | 3 | last_frame, resolution 1080p |
Omni-Flash | 10 | 9:16, 16:9 | 3 | input_video (video-to-video) |
Frame sizes and their ratios (the Seedance models take all eight; Veo and Omni take the first four):
| size | Ratio | Models |
|---|---|---|
720x1280 | 9:16 (default) | all |
1280x720 | 16:9 | all |
1024x1792 | 9:16 | all |
1792x1024 | 16:9 | all |
1024x1024 | 1:1 | Seedance only |
768x1024 | 3:4 | Seedance only |
1024x768 | 4:3 | Seedance only |
1680x720 | 21:9 | Seedance only |
Billing
Video is priced per second: price = the model's credits-per-second rate × the requested seconds. Rates per model are shown in the Pricing Policy. Before a job starts, your available balance must cover its price plus the price of all your unfinished jobs.
| Outcome | Billing |
|---|---|
completed | Charged in full: credits-per-second of the model × seconds |
failed — prompt blocked by content moderation | Charged in full (the video was already rendered when the prompt was rejected) |
failed — system error, never started, or timed out | Not charged |
Error Codes
| Code | Meaning | Action |
|---|---|---|
400 | Malformed request, seconds/size not supported by the model, or more input_reference images than the model takes | Read error.message — it lists the accepted values |
401 | Invalid or missing API key | Check your toc- key |
402 | Not enough credits for the requested duration | Top up or shorten the clip. Unfinished jobs count toward your available balance |
404 | Unknown video id, or the video belongs to a different API key | Verify the id and the key used to create the job |
429 | Service is busy right now | Retry after the seconds given in Retry-After |
502 | Submission outcome unknown (possible duplicate job) | If x-should-retry: false is present, do NOT retry automatically — check by video id first |
503 | Model temporarily unavailable or no capacity | Retry shortly |