Video Generation
POST /v1/video/generations — generate videos from text or images
Video Generation
POST /v1/video/generations
Generate videos from a text prompt, reference images, or reference videos using supported video generation models.
SoxAI acts as a relay — the upstream provider's raw response is passed through verbatim to the client, with SoxAI's job management fields (id, status, url, etc.) injected directly into the response JSON. No wrapping, no filtering.
Create Video Job
Request
POST /v1/video/generations
Headers
| Header | Required | Description |
|---|---|---|
Authorization | Yes | Bearer sox-your-api-key |
Content-Type | Yes | application/json |
Request Body Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
model | string | Yes | Video model ID. See Supported Models below |
prompt | string | Yes | Text description of the desired video. Max ~2500 Chinese characters or ~5000 non-Chinese characters |
duration | integer | No | Video duration in seconds. Range: 3–15. Default: 5 |
resolution | string | No | Output resolution. Values: 480P, 720P, 1080P. Default varies by model |
aspect_ratio | string | No | Aspect ratio. Values: 16:9, 9:16, 1:1, 4:3, 3:4, 4:5, 5:4, 9:21, 21:9. Default: 16:9 |
generate_audio | boolean | No | Whether to generate audio for the video. Affects pricing for some models. Default: model-specific |
image_urls | string[] | No | Reference image URLs. Used for image-to-video (i2v) and reference-to-video (r2v) models. Supports 1–9 images depending on model |
image_url | string | No | Single reference image URL (legacy field, use image_urls instead). If image_urls is empty and this is set, it becomes image_urls[0] |
video_urls | string[] | No | Reference video URLs. Used for video editing models. Supports 1 video |
audio_urls | string[] | No | Reference audio URLs. Used for multimodal reference generation (Ark-native only) |
watermark | boolean | No | Whether to add watermark to the video. Default: true |
fps | integer | No | Frames per second. Rarely used, most models have fixed FPS |
callback_url | string | No | Webhook URL. SoxAI POSTs the final job result to this URL on completion or failure. Must be on SSRF allowlist |
Example: Text-to-Video (HappyHorse)
curl -X POST https://api.soxai.io/v1/video/generations \
-H "Authorization: Bearer $SOXAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "happyhorse-1.1-t2v",
"prompt": "一只小猫在草地上奔跑",
"duration": 5,
"resolution": "720P",
"aspect_ratio": "16:9",
"watermark": false
}'Example: Image-to-Video (Seedance with reference images)
curl -X POST https://api.soxai.io/v1/video/generations \
-H "Authorization: Bearer $SOXAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "doubao-seedance-2-0-260128",
"prompt": "图片1的角色在图片2的场景中奔跑",
"duration": 4,
"resolution": "480p",
"aspect_ratio": "16:9",
"generate_audio": true,
"image_urls": [
"https://example.com/character.png",
"https://example.com/scene.png"
],
"watermark": false
}'Response (201 Created)
Returns the upstream provider's raw submit response with SoxAI fields injected. The response shape depends on which provider the channel is configured to use.
SoxAI Injected Fields
These fields are always present, injected into the upstream JSON:
| Field | Type | Description |
|---|---|---|
id | string (UUID) | SoxAI job ID. Use this to poll status |
object | string | Always "video.generation" |
status | string | Job status: queued, processing, succeeded, failed, expired |
model | string | Model ID used for this job |
created_at | integer (unix timestamp) | Job creation time |
expires_at | integer (unix timestamp) | Job expiry time (7 days after creation). Video URL becomes unavailable after this |
url | string | SoxAI CDN URL for the generated video. Only present when status is succeeded |
duration_secs | float | Actual video duration in seconds. Only present when status is succeeded |
error | string | Error message. Only present when status is failed |
Example: Submit Response (HappyHorse via DashScope)
{
"id": "c1db669d-b412-4f7a-b700-2436c5d52810",
"object": "video.generation",
"status": "queued",
"model": "happyhorse-1.1-t2v",
"created_at": 1785643294,
"expires_at": 1786248094,
"output": {
"task_id": "113ba373-93be-4204-984a-d4a3440c9e0f",
"task_status": "PENDING"
},
"request_id": "ca8ca029-901c-999e-a8d7-d277bbfd4b19"
}
output,request_id— upstream DashScope fields, passed through as-is.
Example: Submit Response (Seedance via Volcengine Ark)
{
"id": "bcd16484-8630-4ee2-9ee3-f6fb9b864031",
"object": "video.generation",
"status": "queued",
"model": "doubao-seedance-2-0-260128",
"created_at": 1785639267,
"expires_at": 1786244067,
"id": "task_wklJT1SWIQNXSaZEkfPcEzdDPj8VO4W9",
"object": "video.generation.task",
"status": "pending"
}Note: upstream
idandstatusfields are overridden by SoxAI's injected values.
Get Video Job
GET /v1/video/generations/{job_id}
Poll the status of a video generation job. The video worker automatically polls the upstream provider and stores the result. You should poll every 5–15 seconds until status is succeeded or failed.
Request
| Path Parameter | Type | Required | Description |
|---|---|---|---|
job_id | string (UUID) | Yes | The job ID returned by POST /v1/video/generations |
Example
curl https://api.soxai.io/v1/video/generations/c1db669d-b412-4f7a-b700-2436c5d52810 \
-H "Authorization: Bearer $SOXAI_API_KEY"Response (200 OK)
Returns the upstream provider's raw poll response with SoxAI fields injected.
Status Values
| Status | Description |
|---|---|
queued | Job created, waiting for video worker to submit to upstream |
processing | Submitted to upstream, waiting for video generation to complete |
succeeded | Video generation complete, url field has the CDN link |
failed | Generation failed, error field has the error message |
expired | Job expired (7 days after creation), video URL no longer available |
Example: Processing Response
{
"id": "c1db669d-b412-4f7a-b700-2436c5d52810",
"object": "video.generation",
"status": "processing",
"model": "happyhorse-1.1-t2v",
"created_at": 1785643294,
"expires_at": 1786248094
}Example: Succeeded Response (Seedance)
All upstream fields (data, task_id, duration, video_url, usage, upstream_response, etc.) are passed through as-is:
{
"id": "c1db669d-b412-4f7a-b700-2436c5d52810",
"object": "video.generation",
"status": "succeeded",
"model": "doubao-seedance-2-0-260128",
"created_at": 1785643294,
"expires_at": 1786248094,
"url": "https://cdn.soxai.io/videos/1/c1db669d-b412-4f7a-b700-2436c5d52810.mp4",
"duration_secs": 4,
"data": [
{
"url": "https://upstream-cdn.example.com/video.mp4"
}
],
"task_id": "task_6yWpifeskOZOPL0VHooIC97qL3iUjgKY",
"duration": 4,
"tos_key": "ai-open-api/.../video.mp4",
"trace_id": "11c5fc2f6bb5e6c29000bbfc7a4088a3",
"video_url": "https://upstream-cdn.example.com/video.mp4",
"usage": {
"completion_tokens": 40594,
"total_tokens": 40594
},
"upstream_response": {
"code": 0,
"data": {
"status": "succeeded",
"duration": 4,
"generate_audio": true,
"framespersecond": "24",
"seed": "37400",
"video_url": "https://upstream-cdn.example.com/video.mp4",
"usage": {
"completion_tokens": 40594,
"total_tokens": 40594
}
},
"message": "",
"trace_id": "11c5fc2f6bb5e6c29000bbfc7a4088a3"
}
}Field Reference
SoxAI injected fields (always present):
| Field | Type | Present When | Description |
|---|---|---|---|
id | string | Always | SoxAI job UUID |
object | string | Always | "video.generation" |
status | string | Always | queued / processing / succeeded / failed / expired |
model | string | Always | Model ID |
created_at | integer | Always | Unix timestamp, job creation time |
expires_at | integer | Always | Unix timestamp, job expiry (7 days) |
url | string | succeeded | SoxAI CDN URL for the video file |
duration_secs | float | succeeded | Video duration in seconds |
error | string | failed | Error message |
Upstream passthrough fields (provider-specific, varies):
| Field | Provider | Description |
|---|---|---|
output.task_id | DashScope (HappyHorse) | Upstream task ID |
output.task_status | DashScope (HappyHorse) | Upstream task status (PENDING, RUNNING, SUCCEEDED, FAILED) |
output.video_url | DashScope (HappyHorse) | Upstream video URL |
usage.duration | DashScope (HappyHorse) | Video duration |
usage.SR | DashScope (HappyHorse) | Resolution height (720 = 720P) |
usage.ratio | DashScope (HappyHorse) | Aspect ratio |
usage.video_count | DashScope (HappyHorse) | Number of videos generated |
data[].url | Seedance (proxy/Ark) | Upstream video URL |
task_id | Seedance (proxy) | Upstream task ID |
video_url | Seedance (BytePlus/Ark) | Upstream video URL |
duration | Seedance | Video duration |
usage.completion_tokens | Seedance (Ark) | Token count for billing |
usage.total_tokens | Seedance (Ark) | Total token count |
upstream_response | Seedance (proxy) | Full upstream nested response |
generate_audio | Seedance (Ark) | Whether audio was generated |
framespersecond | Seedance (Ark) | Frame rate |
These fields come directly from the upstream provider. SoxAI does not modify, filter, or rename them. If the upstream changes their response format, the client sees the change immediately.
List Video Jobs
GET /v1/video/generations
List video generation jobs for the authenticated tenant, ordered by creation time (newest first).
Request
| Query Parameter | Type | Default | Description |
|---|---|---|---|
limit | integer | 20 | Number of jobs to return (max 100) |
offset | integer | 0 | Pagination offset |
Example
curl "https://api.soxai.io/v1/video/generations?limit=10&offset=0" \
-H "Authorization: Bearer $SOXAI_API_KEY"Response (200 OK)
{
"object": "list",
"data": [
{
"id": "c1db669d-b412-4f7a-b700-2436c5d52810",
"object": "video.generation",
"status": "succeeded",
"model": "happyhorse-1.1-t2v",
"created_at": 1785643294,
"expires_at": 1786248094,
"url": "https://cdn.soxai.io/videos/1/c1db669d-b412-4f7a-b700-2436c5d52810.mp4",
"duration_secs": 5
}
]
}Each item in data has the same shape as the Get Video Job response.
Delete Video Job
DELETE /v1/video/generations/{job_id}
Delete a video job and its associated video file. Only jobs belonging to the authenticated tenant can be deleted.
Request
curl -X DELETE "https://api.soxai.io/v1/video/generations/c1db669d-b412-4f7a-b700-2436c5d52810" \
-H "Authorization: Bearer $SOXAI_API_KEY"Response (200 OK)
{
"deleted": true
}Supported Models
Seedance (ByteDance)
| Model | Type | Max Duration | Resolutions |
|---|---|---|---|
doubao-seedance-2-0-260128 | Text/Image-to-Video | 15s | 480P, 720P, 1080P |
seedance-2 | Text/Image-to-Video | 15s | 480P, 720P, 1080P |
seedance-2-fast | Text/Image-to-Video | 15s | 480P, 720P, 1080P |
seedance-2-lite | Text/Image-to-Video | 15s | 480P, 720P, 1080P |
seedance-2-pro | Text/Image-to-Video | 15s | 480P, 720P, 1080P |
seedance-1-lite | Text/Image-to-Video | 10s | 480P, 720P |
seedance-1-pro | Text/Image-to-Video | 10s | 480P, 720P |
Access routes (configured as channel base_url):
| Route | Base URL | Billing | Notes |
|---|---|---|---|
| Volcengine Ark | https://ark.cn-beijing.volces.com | CNY | China mainland, multimodal references |
| BytePlus International | https://open.byteplusapi.com | USD | International |
| Third-party proxy | https://your-proxy.com | Varies | OpenAI-compatible format |
HappyHorse (Alibaba Bailian)
| Model | Type | Max Duration | Resolutions | Reference Images |
|---|---|---|---|---|
happyhorse-1.1-t2v | Text-to-Video | 15s | 480P, 720P, 1080P | — |
happyhorse-1.1-i2v | Image-to-Video | 15s | 480P, 720P, 1080P | 1 (first frame) |
happyhorse-1.1-r2v | Reference-to-Video | 15s | 480P, 720P, 1080P | 1–9 |
happyhorse-1.0-t2v | Text-to-Video | 15s | 480P, 720P, 1080P | — |
happyhorse-1.0-i2v | Image-to-Video | 15s | 480P, 720P, 1080P | 1 (first frame) |
happyhorse-1.0-r2v | Reference-to-Video | 15s | 480P, 720P, 1080P | 1–9 |
happyhorse-1.0-video-edit | Video Edit | 15s | 720P, 1080P | 0–5 reference images + 1 video |
Access route:
| Route | Base URL | Notes |
|---|---|---|
| DashScope API | https://dashscope.aliyuncs.com | SoxAI automatically sets X-DashScope-Async: enable header |
HappyHorse specific constraints:
| Constraint | Value |
|---|---|
| Image formats | JPEG, JPG, PNG, WEBP |
| Image min resolution | 300×300 pixels |
| Image aspect ratio range | 1:2.5 to 2.5:1 |
| Image max file size | 20 MB |
| Reference image min resolution (r2v) | 400px short side |
| Video formats (video-edit) | MP4, MOV (H.264 recommended) |
| Video max duration (video-edit) | 60s (output capped at 15s) |
| Video max file size (video-edit) | 100 MB |
| Video min FPS (video-edit) | 8 |
| Watermark | Adds "Happy Horse" watermark in bottom-right if watermark: true |
Billing
Video generation billing depends on the model's billing_type:
| Billing Type | Formula | Driven By |
|---|---|---|
token | completion_tokens × price_per_million | usage.completion_tokens from upstream |
per_second | actual_duration_seconds × price_per_second | duration / duration_secs from upstream |
per_request | Fixed price per generation | N/A |
If the upstream omits completion_tokens (token billing), SoxAI falls back to the pre-consumed estimate locked at job creation time.
Pricing tiers may vary by resolution and whether audio was generated. See the model catalog in the admin console for specific pricing.
Webhook Callbacks
If callback_url is provided in the request body, SoxAI POSTs the final job result to that URL when the job completes (succeeded or failed). The URL must be on the SSRF allowlist.
Callback Payload
{
"id": "c1db669d-b412-4f7a-b700-2436c5d52810",
"status": "succeeded",
"url": "https://cdn.soxai.io/videos/1/c1db669d-b412-4f7a-b700-2436c5d52810.mp4",
"duration_secs": 4
}The callback is sent only once, after the video worker has finished downloading and storing the video. If the callback URL is unreachable, SoxAI does not retry — use GET /v1/video/generations/{id} as a fallback.
Error Handling
400 Bad Request
{
"error": "invalid request body"
}401 Unauthorized
{
"error": "invalid api key"
}402 Payment Required
{
"error": "insufficient quota for this video generation"
}404 Not Found
{
"error": "video job not found"
}