Videos
Use the asynchronous video APIs for seedance and happyhorse: submit a task, save the task ID, poll for status, and store the output promptly after success.
Supported video series
| Series | Model | Best at | Protocol style |
|---|---|---|---|
| seedance | seedance-2-0, seedance-2-0-fast, seedance-2-0-mini | Multimodal references (image + video + audio), audio-enabled video, video editing & extension, up to 4K | Multimodal content array |
| happyhorse | happyhorse-1.1-t2v, happyhorse-1.1-i2v, happyhorse-1.1-r2v, happyhorse-1.0-video-edit | Physically realistic, smooth motion; generation with multiple reference images; partial video replacement editing | input + parameters |
Common conventions
- Auth: same as the other APIs — send the header
Authorization: Bearer sk-***. - Async flow: submit the task → get the task ID → poll the status at a fixed interval → take the video address once succeeded → download and store it immediately.
- Polling interval: video generation usually takes 1 ~ 5 minutes; poll about every 15 seconds, never below 5 seconds.
- Do not resubmit: after a successful submission, save the task ID and poll. Resubmitting creates a new task and is billed again.
- Output validity: video links from both series are kept for only 24 hours and cleared automatically after that.
Timeouts and long connectionsDo not wait for the result over a long-lived connection. Set the client HTTP timeout for the submit-request duration (a few seconds), and track generation progress via the polling API.
Limits follow upstream documentationThe size, dimension, count and duration limits on this page come from each upstream vendor's documentation and may change as they update it. For the current values, refer to each vendor's official documentation.
seedance series
The Volcano Engine Ark Doubao Seedance 2.0 series supports multimodal image, video, audio and text input, as well as video generation, editing and extension.
Supported models
Use one of the following model IDs in the model field.
| Model ID | Positioning |
|---|---|
seedance-2-0 | Flagship; pursues the highest generation quality; the only one with 4K |
seedance-2-0-fast | Faster output, balancing quality and speed |
seedance-2-0-mini | Cost-first, ideal for high-volume test shots |
Public model IDsUse only the three public model IDs above in requests. Internal endpoint IDs and upstream-normalized names in responses are not request parameters. A successful response may show an upstream name in model; keep using the IDs above for later requests.
Capability matrix
| Capability | Input combination | Description |
|---|---|---|
| Text-to-video | Text | Generates one video from a prompt only. |
| Image-to-video | First-frame image + text (optional) | Generates forward from the given image as the first frame. |
| Image-to-video · first & last frames | First-frame image + last-frame image + text (optional) | Fills in the motion between the two images. |
| Multimodal reference-to-video | Images 0~9 + videos 0~3 + audio 0~3 + text (optional) | Combines reference materials into a brand-new video. Audio alone is not allowed — at least one reference video or image is required. |
| Edit video | Video + image + text | E.g. "Replace the perfume in the gift box in the video with the face cream from image 1, keeping the camera work unchanged". |
| Extend video | Video to extend + text | Appends content after the existing video. |
| Audio-enabled video | — | With generate_audio: true, automatically generates voice, sound effects and background music (mono) synchronized with the picture. |
| Web search | Text-only input | tools: [{"type": "web_search"}] lets the model look up information before generating. Available only with text-only input. |
| Return last frame | — | return_last_frame: true — use the last frame of the previous clip as the first frame of the next to chain a continuous long video. |
Restrictions on real-person portrait materialThe Seedance 2.0 series does not accept reference images or videos containing real human faces. Scenarios involving portraits must use the licensed-material solutions provided upstream (same-account output trust, preset virtual avatars, licensed real-person material); please confirm a viable path with us via the contact page first.
Submit a generation task
Request body fields
| Parameter | Type | Required | Description |
|---|---|---|---|
model | string | Required | tokgate.io model ID; see the model table above. |
content | array<object> | Required | Array of input materials; see "content elements" below. |
resolution | string | Optional | Resolution, default 720p. Options: 480p / 720p / 1080p / 4k. 1080p is not supported on fast and mini; 4k is supported only on seedance-2-0. |
ratio | string | Optional | Aspect ratio, default adaptive. Options: 21:9 / 16:9 / 4:3 / 1:1 / 3:4 / 9:16 / adaptive. |
duration | integer | Optional | Duration in seconds; default 5, range 4 ~ 15; you can also pass -1 to let the model decide. The actual duration is the duration returned by the query API — duration directly affects billing. |
generate_audio | boolean | Optional | Default true — output carries synchronized audio. Set false for a silent video. Put dialogue inside double quotes to improve audio quality. |
output_format | string | Optional | Output format; currently supported and defaulted to mp4. |
watermark | boolean | Optional | Default false. When true, an AI-generated watermark is shown in the lower-right corner. |
return_last_frame | boolean | Optional | Default false. When true, the query API returns the last-frame image of the video (same dimensions as the video, no watermark). |
tools | array<object> | Optional | Tool configuration; currently supports [{"type": "web_search"}]. Available only in text-only input scenarios. |
callback_url | string | Optional | Callback URL (POST) invoked on task status changes. The callback body matches the query API response. Failed deliveries are retried 3 times. |
execution_expires_after | integer | Optional | Task timeout threshold in seconds; default 172800 (48 hours), range 3600 ~ 259200. Timed-out tasks are marked expired. |
priority | integer | Optional | Queue priority 0 ~ 9, default 0; larger values queue earlier. Equal priorities are first-in-first-out. It only affects queue order and does not interrupt running tasks. |
safety_identifier | string | Optional | Unique end-user identifier, up to 64 characters. Passing a hash of the user ID is recommended to avoid exposing personal data. |
Fields not supported by the Seedance 2.0 seriesseed, frames, camera_fixed, service_tier (online inference only) and draft (draft mode) are not yet supported on the 2.0 series; they are ignored or rejected with an error.
content elements
type | Structure | role | Description |
|---|---|---|---|
text | {"type":"text","text":"..."} | — | The prompt. You can reference the following materials as "image 1", "video 1", "audio 1" inside it. |
image_url | {"type":"image_url","image_url":{"url":"..."},"role":"reference_image"} | reference_image | Reference images, 0 ~ 9. Public URLs or Base64 data URIs are supported. |
video_url | {"type":"video_url","video_url":{"url":"..."},"role":"reference_video"} | reference_video | Reference videos, 0 ~ 3. Used for editing, extension or style reference. |
audio_url | {"type":"audio_url","audio_url":{"url":"..."},"role":"reference_audio"} | reference_audio | Reference audio, 0 ~ 3. Cannot be the only material passed. |
Output spec differences
| Item | seedance-2-0 | seedance-2-0-fast | seedance-2-0-mini |
|---|---|---|---|
| Resolution | 480p / 720p / 1080p / 4k (10-bit depth) | 480p / 720p | 480p / 720p |
| Aspect ratio | 21:9 / 16:9 / 4:3 / 1:1 / 3:4 / 9:16 / adaptive | ||
| Duration | 4 ~ 15 seconds (or -1 to let the model decide) | ||
| Output format | MP4 (4K is H.265-encoded) | ||
4K playback compatibility4K videos use H.265 + 10-bit encoding, which a few browsers and players do not support. If playback misbehaves, switch to VLC, MPV, QuickTime Player or similar, or drop to 1080p.
Submit response
{
"id": "cgt-2026xxxx-xxxx"
}
The returned id is the task ID used for later queries. Task records are kept for 7 days (from creation time) and cleared automatically after that.
Task ID and polling contractA successful POST returns a top-level id; use that same value as {id} in the GET path. Keep polling on queued / running; read content.video_url on succeeded; stop on the other terminal states. Do not switch to task_id, and the success status is not success.
Query task status
Set the path parameter id directly to the same-named id returned by the submit API.
{
"id": "cgt-2026xxxx-xxxx",
"model": "doubao-seedance-2-0-260128",
"status": "succeeded",
"content": {
"video_url": "https://.../result.mp4"
},
"usage": {
"completion_tokens": 108900,
"total_tokens": 108900
},
"created_at": 1779348818,
"updated_at": 1779348874,
"resolution": "720p",
"ratio": "16:9",
"duration": 5,
"framespersecond": 24,
"generate_audio": true,
"output_format": "mp4",
"service_tier": "default",
"execution_expires_after": 172800,
"priority": 0
}
| Field | Description |
|---|---|
model | The upstream-normalized model name in the response, for result records only. Keep using the public model IDs above in later requests. |
status | Task status; see the enum below. |
content.video_url | Video address, returned only when succeeded. Valid for 24 hours. |
error | Error details on failure; this field may be absent from a successful response. |
resolution / ratio / duration / framespersecond | The actual generation parameters. With adaptive or duration: -1, read the values the model finally chose here. |
usage | Token usage of this task, for reconciliation. |
created_at / updated_at | Creation time and status update time (Unix seconds). |
generate_audio / output_format | The actual audio switch and output format. output_format is currently mp4, and content.video_url points to the downloadable MP4 file. |
| status | Meaning | Action |
|---|---|---|
queued | Submitted, queuing | Keep polling |
running | Generating | Keep polling |
succeeded | Succeeded | Take content.video_url and store it immediately |
failed | Failed | Read error to locate the cause, fix the prompt or materials, then retry |
cancelled | Cancelled (only queued tasks can be cancelled) | Resubmit if you need the result |
expired | Exceeded execution_expires_after without completing; terminated | Resubmit; raise the timeout threshold if necessary |
Complete examples
# Step 1: Submit the task
curl -X POST https://api.tokgate.io/api/v3/contents/generations/tasks \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk-***" \
-d '{
"model": "seedance-2-0",
"content": [
{ "type": "text", "text": "A coastline in the early morning, waves lapping against the reefs, seagulls circling low, the camera pushing in from far to near" }
],
"resolution": "720p",
"ratio": "16:9",
"duration": 5,
"generate_audio": true,
"output_format": "mp4",
"watermark": false
}'
# Response: { "id": "cgt-2026xxxx-xxxx" }
# Step 2: Poll the task status (recommended every 15 seconds)
curl https://api.tokgate.io/api/v3/contents/generations/tasks/cgt-2026xxxx-xxxx \
-H "Authorization: Bearer sk-***"
# Step 3: Download content.video_url once status becomes succeeded
curl -o coastline.mp4 "https://.../result.mp4"
curl -X POST https://api.tokgate.io/api/v3/contents/generations/tasks \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk-***" \
-d '{
"model": "seedance-2-0-fast",
"content": [
{ "type": "text", "text": "Use image 1 as the first frame; the product rotates slowly to show details, soft studio lighting" },
{
"type": "image_url",
"image_url": { "url": "https://your-cdn.example.com/product.jpg" },
"role": "reference_image"
}
],
"resolution": "720p",
"ratio": "adaptive",
"duration": 5
}'
{
"model": "seedance-2-0",
"content": [
{
"type": "text",
"text": "A first-person-perspective fruit-tea commercial: the first frame is image 1 — a hand picks an apple covered in morning dew; then a quick cut to a shaker cup being shaken; the last frame freezes on image 2. Use the compositional rhythm of video 1 throughout, with audio 1 as the background music"
},
{ "type": "image_url", "image_url": { "url": "https://your-cdn.example.com/pic1.jpg" }, "role": "reference_image" },
{ "type": "image_url", "image_url": { "url": "https://your-cdn.example.com/pic2.jpg" }, "role": "reference_image" },
{ "type": "video_url", "video_url": { "url": "https://your-cdn.example.com/ref.mp4" }, "role": "reference_video" },
{ "type": "audio_url", "audio_url": { "url": "https://your-cdn.example.com/bgm.mp3" }, "role": "reference_audio" }
],
"generate_audio": true,
"ratio": "16:9",
"duration": 11,
"watermark": false,
"return_last_frame": true
}
{
"model": "seedance-2-0",
"content": [
{ "type": "text", "text": "Shoot a nature-documentary shot introducing the physical characteristics of the glass frog; it must match real biological traits" }
],
"tools": [{ "type": "web_search" }],
"ratio": "16:9",
"duration": 11,
"watermark": false
}
happyhorse series
The HappyHorse video generation series supports text-to-video, first-frame image-to-video, multi-image reference-to-video and video editing.
Supported models
| Capability | Model (model) | Input | Key parameter rules |
|---|---|---|---|
| Text-to-video (T2V) | happyhorse-1.1-t2v | Pass only the required input.prompt; do not send input.media | ratio is supported; the verified combination on this page is 720P, 16:9 and 5 seconds |
| Image-to-video · first-frame based (I2V) | happyhorse-1.1-i2v | Exactly one first_frame in input.media; prompt is optional | Do not pass ratio; the output aspect ratio follows the first frame |
| Reference-to-video (R2V) | happyhorse-1.1-r2v | 1 ~ 9 reference_image items in input.media + required prompt | Reference images in the prompt as [Image N] in array order |
| Video editing | happyhorse-1.0-video-edit | Exactly one video in input.media, plus 0 ~ 5 reference_image items; prompt is required | The minimal request needs only resolution; do not pass ratio or duration |
Use the complete model IDUse only the four complete IDs in the table above. Do not construct model names from version numbers. Before going live, you can call GET /v1/models to confirm that these models are visible to the current API key.
Submit a generation task
Request headers
| Header | Required | Description |
|---|---|---|
Authorization | Required | Bearer sk-*** |
Content-Type | Required | application/json |
X-DashScope-Async | Required | Fixed at enable. Missing this header returns an error (saying synchronous calls are not supported). |
Request body fields
| Parameter | Type | Required | Description |
|---|---|---|---|
model | string | Required | Model name; see the list above. |
input.prompt | string | Depends on capability | The prompt. Required for T2V / R2V / video editing; optional for I2V. The provider applies character-counting limits under its own rules (up to 5,000 or 2,500 characters, depending on how the input is counted); overflow is truncated automatically. |
input.media | array<object> | Depends on capability | Material list; elements contain type and url. Not needed for T2V. |
input.media[].type | string | Required | first_frame (I2V first frame, exactly 1) / reference_image (reference image) / video (video to edit, exactly 1). |
input.media[].url | string | Required | Material address. Images support public URLs or Base64 (data:{MIME};base64,{data}); videos must be publicly accessible URLs. |
parameters.resolution | string | Optional | Resolution tier 480P / 720P / 1080P, default 1080P. Video editing supports only 720P / 1080P. Note the tier uses an uppercase P. |
parameters.ratio | string | Optional | Used only by T2V / R2V. Default: 16:9. Options: 16:9 / 9:16 / 1:1 / 4:3 / 3:4 / 4:5 / 5:4 / 9:21 / 21:9. Do not send it for I2V or video editing: I2V follows the first frame, while video editing follows the input video. |
parameters.duration | integer | Optional | The verified T2V / I2V / R2V requests on this page all use 5 seconds. I2V / R2V accept integers from 3 to 15 and default to 5. Video editing does not support this parameter; its output follows the input video and is capped at 15 seconds. |
parameters.watermark | boolean | Optional | Default true — adds a fixed "Happy Horse" watermark in the lower-right corner. You must explicitly pass false if you do not want a watermark. |
parameters.seed | integer | Optional | Random seed, [0, 2147483647]. Fixing it makes results more reproducible, though the probabilistic model does not guarantee identical output. |
parameters.audio_setting | string | Optional | Video editing only. auto (default, model-controlled) or origin (keeps the original sound of the input video). |
The model must match the input structureT2V sends only input.prompt; I2V, R2V and video editing must send input.media as shown above. Field required: input.media usually means the selected model and request-body shape do not match. Do not work around it by adding irrelevant media to T2V.
Watermark on by defaultThe watermark of happyhorse defaults to true, the opposite of seedance (default false). For commercial use, explicitly pass false.
Material limits
| Material | Limits |
|---|---|
First-frame image (first_frame) | Formats JPEG / JPG / PNG / WEBP; both width and height at least 300 px; aspect ratio 1:2.5 ~ 2.5:1; up to 20 MB; exactly 1 image. |
Reference images (reference_image for R2V) | 1 ~ 9 images; same formats as above; short side at least 400 px — clear images of 720P or better recommended; up to 20 MB. Avoid images that are too small, blurry or over-compressed. |
Reference images (reference_image for video editing) | 0 ~ 5 images; both width and height at least 300 px; aspect ratio 1:2.5 ~ 2.5:1; up to 20 MB. |
Video to edit (video) | Formats MP4 / MOV (H.264 recommended); duration 3 ~ 60 seconds; long side up to 4096 px, short side at least 360 px; aspect ratio 1:2.5 ~ 2.5:1; up to 100 MB; frame rate above 8 fps; must be a publicly accessible URL. |
Output duration of video editingOutput is 3 ~ 15 seconds. When the input video is no longer than 15 seconds, the output duration matches the input; beyond 15 seconds, the system automatically takes the first 15 seconds, so the maximum output is 15 seconds.
Reference syntax for R2V
For reference-to-video, use [Image 1], [Image 2] in the prompt to refer to the images at the corresponding positions in the media array (order matches the array), and clearly name the specific object in each reference image. Example: "The woman in the red qipao from [Image 1] gently raises her hand to unfold the folding fan from [Image 2]".
Submit response
{
"output": {
"task_status": "PENDING",
"task_id": "0385dc79-5ff8-4d82-bcb6-xxxxxx"
},
"request_id": "4909100c-7b5a-9f92-bfe5-xxxxxx"
}
Save output.task_id — queryable for 24 hours. Do not create the task again; just poll directly.
Query task status
{
"request_id": "99243b47-ec5f-9413-9993-xxxxxx",
"output": {
"task_id": "4673458e-28be-4a05-bf2a-xxxxxx",
"task_status": "SUCCEEDED",
"submit_time": "2026-04-20 17:55:17.075",
"scheduled_time": "2026-04-20 17:55:17.129",
"end_time": "2026-04-20 17:56:36.658",
"orig_prompt": "A miniature city built from cardboard and bottle caps comes alive at night",
"video_url": "https://.../result.mp4?Expires=..."
},
"usage": {
"duration": 5,
"input_video_duration": 0,
"output_video_duration": 5,
"video_count": 1,
"SR": 720,
"ratio": "16:9"
}
}
{
"request_id": "e5d70b02-ebd3-98ce-9fe8-xxxxxx",
"output": {
"task_id": "86ecf553-d340-4e21-af6e-xxxxxx",
"task_status": "FAILED",
"code": "InvalidParameter",
"message": "The parameter is invalid."
}
}
{
"request_id": "a4de7c32-7057-9f82-8581-xxxxxx",
"output": {
"task_id": "502a00b1-19d9-4839-a82f-xxxxxx",
"task_status": "UNKNOWN"
}
}
| Field | Description |
|---|---|
output.task_status | Task status; see the enum below. |
output.video_url | Video address, returned only on SUCCEEDED. Format MP4 (H.264), valid for 24 hours. |
output.orig_prompt | The original input prompt. |
output.submit_time / scheduled_time / end_time | Submission / execution-start / completion time, in YYYY-MM-DD HH:mm:ss.SSS format. |
usage.duration | Total billed video duration (seconds). Video editing is generally billed using the combined input and output video durations; use the value returned by the API as the source of truth. |
usage.input_video_duration / output_video_duration | Input / output video duration (seconds). |
usage.SR / usage.ratio | The actual output resolution tier and aspect ratio. |
usage.video_count | Number of output videos, fixed at 1. |
request_id | Unique request identifier; provide it when troubleshooting. |
| task_status | Meaning | Action |
|---|---|---|
PENDING | Queuing | Keep polling |
RUNNING | Processing | Keep polling |
SUCCEEDED | Succeeded | Take video_url and store it immediately |
FAILED | Failed | Read code / message to locate the cause |
CANCELED | Cancelled | Resubmit if you need the result |
UNKNOWN | The task does not exist, or the task_id has passed its 24-hour validity | Check the task_id; if expired, the only option is to resubmit |
Keep polling while output.task_status is PENDING or RUNNING. SUCCEEDED, FAILED, CANCELED and UNKNOWN are all terminal states. Even when the query returns HTTP 200, always inspect output.task_status. The default query limit is 20 RPS; polling every 15 seconds is recommended.
Complete examples
# Step 1: Create the task only once
curl -X POST https://api.tokgate.io/api/v1/services/aigc/video-generation/video-synthesis \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk-***" \
-H "X-DashScope-Async: enable" \
-d '{
"model": "happyhorse-1.1-t2v",
"input": {
"prompt": "A miniature city built from cardboard and bottle caps comes alive at night. A cardboard train slowly passes by, with small lights illuminating the way."
},
"parameters": {
"resolution": "720P",
"ratio": "16:9",
"duration": 5
}
}'
# Save output.task_id from the response; do not POST again
# Step 2: Query the same task_id every 15 seconds
curl https://api.tokgate.io/api/v1/tasks/0385dc79-5ff8-4d82-bcb6-xxxxxx \
-H "Authorization: Bearer sk-***"
# Step 3: Download output.video_url only when output.task_status is SUCCEEDED
curl -o happyhorse-result.mp4 "https://.../result.mp4?Expires=..."
curl -X POST https://api.tokgate.io/api/v1/services/aigc/video-generation/video-synthesis \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk-***" \
-H "X-DashScope-Async: enable" \
-d '{
"model": "happyhorse-1.1-i2v",
"input": {
"prompt": "A cat running across grass as sunlight filters through the blades",
"media": [
{ "type": "first_frame", "url": "https://your-cdn.example.com/cat.png" }
]
},
"parameters": {
"resolution": "720P",
"duration": 5
}
}'
curl -X POST https://api.tokgate.io/api/v1/services/aigc/video-generation/video-synthesis \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk-***" \
-H "X-DashScope-Async: enable" \
-d '{
"model": "happyhorse-1.1-r2v",
"input": {
"prompt": "The woman in the red qipao from [Image 1] gently raises her hand to unfold the fan from [Image 2], ending with a close-up of her face",
"media": [
{ "type": "reference_image", "url": "https://your-cdn.example.com/girl.jpg" },
{ "type": "reference_image", "url": "https://your-cdn.example.com/fan.jpg" }
]
},
"parameters": {
"resolution": "720P",
"ratio": "16:9",
"duration": 5
}
}'
curl -X POST https://api.tokgate.io/api/v1/services/aigc/video-generation/video-synthesis \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk-***" \
-H "X-DashScope-Async: enable" \
-d '{
"model": "happyhorse-1.0-video-edit",
"input": {
"prompt": "Put the character in the video into the striped sweater from the image; keep everything else unchanged",
"media": [
{ "type": "video", "url": "https://your-cdn.example.com/source.mp4" },
{ "type": "reference_image", "url": "https://your-cdn.example.com/sweater.webp" }
]
},
"parameters": {
"resolution": "720P"
}
}'
Complete Python flow (create only once)
First put the key in the TOKGATE_API_KEY environment variable. The script below POSTs exactly once, then keeps querying the same task_id. It stops on any terminal state and, on success, verifies the MP4 container header before saving the file. To call another HappyHorse model, replace only payload with the matching request body above.
import json
import os
import time
from pathlib import Path
from urllib.request import Request, urlopen
base_url = "https://api.tokgate.io"
api_key = os.environ["TOKGATE_API_KEY"]
payload = {
"model": "happyhorse-1.1-t2v",
"input": {
"prompt": "A miniature city built from cardboard and bottle caps comes alive at night"
},
"parameters": {
"resolution": "720P",
"ratio": "16:9",
"duration": 5
}
}
# Create the task only once
create_request = Request(
f"{base_url}/api/v1/services/aigc/video-generation/video-synthesis",
data=json.dumps(payload, ensure_ascii=False).encode("utf-8"),
headers={
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
"X-DashScope-Async": "enable",
},
method="POST",
)
with urlopen(create_request, timeout=30) as response:
created = json.load(response)
task_id = created["output"]["task_id"]
print(f"task_id={task_id}")
# Wait up to 30 minutes; continue on PENDING / RUNNING and stop on any terminal state
terminal_statuses = {"SUCCEEDED", "FAILED", "CANCELED", "UNKNOWN"}
for _ in range(120):
time.sleep(15)
query_request = Request(
f"{base_url}/api/v1/tasks/{task_id}",
headers={"Authorization": f"Bearer {api_key}"},
)
with urlopen(query_request, timeout=30) as response:
result = json.load(response)
output = result["output"]
status = output["task_status"]
print(f"task_status={status}")
if status in terminal_statuses:
break
else:
raise TimeoutError("The task did not reach a terminal state within 30 minutes")
if status != "SUCCEEDED":
raise RuntimeError(
"{}: {} {}".format(status, output.get("code", ""), output.get("message", ""))
)
# Download immediately after success; the result URL is retained for only 24 hours
with urlopen(output["video_url"], timeout=180) as response:
video = response.read()
if len(video) < 12 or video[4:8] != b"ftyp":
raise RuntimeError("The response is not a valid MP4 file")
Path("happyhorse-result.mp4").write_bytes(video)
print(f"saved happyhorse-result.mp4 ({len(video)} bytes)")
Series comparison
| Item | seedance | happyhorse |
|---|---|---|
| Submit / query paths | POST /api/v3/contents/generations/tasksGET /api/v3/contents/generations/tasks/{id} | POST /api/v1/services/aigc/video-generation/video-synthesisGET /api/v1/tasks/{task_id} |
| Async request header | No extra header needed | Required X-DashScope-Async: enable |
| Material field | content[] (type + role) | input.media[] (type + url) |
| Control-parameter location | Top level of the request body | Inside the parameters object |
| Task ID field | id | output.task_id |
| Status field and values | status: lowercase (succeeded…) | output.task_status: uppercase (SUCCEEDED…) |
| Video address field | content.video_url | output.video_url |
| Resolution notation | Lowercase 720p, default 720p | Uppercase 720P, default 1080P |
| Watermark default | false | true |
| Task-record retention | 7 days | 24 hours |
| Audio generation | generate_audio (on by default) | Video editing can use audio_setting to keep the original sound |
Error handling
- 4xx at submission: usually an illegal parameter combination (e.g. passing
1080pto fast / mini, multiple first frames tohappyhorse-1.1-i2v, ordurationto video editing). Check against the relevant parameter table and retry. For status-code semantics see Error codes. - Missing async header: calling happyhorse without
X-DashScope-Async: enablefails immediately with an error saying synchronous calls are not supported. - Task failure: common causes are content failing moderation, materials violating the limits, upstream generation failure or timeout. Fix the prompt, replace the materials, or lower the resolution and duration, then retry.
- Material inaccessible: when passing URLs, make sure they are publicly reachable, not hotlink-protected and require no auth; the video to edit does not support Base64 — it must be an accessible URL.
- Output expiry: video links expire after 24 hours. In production, always download and re-store them in your own object storage immediately after the task succeeds.
- A missing task may still return HTTP 200: clients must inspect
status/output.task_statusand error fields instead of treating the HTTP status alone as success.