HappyHorse Video Generation
Use EasyRouter's unified API to call Alibaba Cloud's HappyHorse 1.0 video generation model family — text-to-video, image-to-video, reference-to-video, and video editing.
HappyHorse is a video generation model family from Alibaba Cloud Bailian, featuring physically realistic visuals and smooth motion. EasyRouter wraps the upstream DashScope async task API as a unified /v1/video/generations async task endpoint, covering four capabilities: text-to-video, image-to-video, reference-to-video, and video editing.
HappyHorse uses an async task-based API: submit a request, receive a task_id, poll for status, then download the video from data.result_url.
1. Supported Models
| Model | Capability | Required Media Input | Description |
|---|---|---|---|
happyhorse-1.0-t2v | Text-to-Video | — | Generate video from pure text prompt |
happyhorse-1.0-i2v | Image-to-Video | 1 first-frame image | Generate video starting from a first-frame image |
happyhorse-1.0-r2v | Reference-to-Video | 1–9 reference images | Multi-image reference generation, prompt uses [Image N] to reference |
happyhorse-1.0-video-edit | Video Editing | 1 video + 0–5 reference images | Style transfer, local replacement on existing videos |
All 4 models share the same endpoint /v1/video/generations, distinguished only by the model field. Response structures are identical across models.
2. Common Endpoints
2.1 Submit Task
POST /v1/video/generationsRequest Headers
| Header | Required | Description |
|---|---|---|
Authorization | ✓ | Bearer sk-YourApiKey |
Content-Type | ✓ | application/json |
Request Body Fields
| Field | Type | Required | Description |
|---|---|---|---|
model | string | ✓ | HappyHorse model name (see table above) |
prompt | string | ✓ | Text prompt. Max 5000 non-Chinese chars or 2500 Chinese chars; excess is auto-truncated |
size | string | — | Resolution tier. 720P / 1080P (default 1080P) |
duration | int | — | Video duration in seconds, range 3~15, default 5. Ignored by video-edit (duration follows input video, max 15s) |
input_reference | string | model-dependent | i2v only: first-frame image URL (publicly accessible) |
metadata | object | model-dependent | HappyHorse-specific parameters container, see Section 3 |
Response
{
"id": "task_9dxfuz8h5gxBvdIqvyoVnZr3DVJxzI1o",
"task_id": "task_9dxfuz8h5gxBvdIqvyoVnZr3DVJxzI1o",
"object": "video",
"model": "happyhorse-1.0-t2v",
"status": "queued",
"progress": 0,
"created_at": 1778250615
}id / task_id is the EasyRouter public ID (with task_ prefix), used for subsequent queries. It is not the upstream DashScope task_id — always use the value returned here.
2.2 Query Task Status
GET /v1/video/generations/{task_id}The response uses EasyRouter's unified {code, message, data} wrapper format. data contains full task details, billing info, channel info, and the raw upstream response.
Response (Queued / NOT_START)
{
"code": "success",
"message": "",
"data": {
"id": 23,
"task_id": "task_9dxfuz8h5gxBvdIqvyoVnZr3DVJxzI1o",
"platform": "17",
"user_id": 3,
"group": "default",
"channel_id": 18,
"quota": 178500,
"action": "textGenerate",
"status": "NOT_START",
"fail_reason": "",
"submit_time": 1778250615,
"start_time": 0,
"finish_time": 0,
"created_at": 1778250615,
"updated_at": 1778250615,
"progress": "0%",
"properties": {
"input": "",
"upstream_model_name": "happyhorse-1.0-t2v",
"origin_model_name": "happyhorse-1.0-t2v"
},
"data": {
"output": {
"task_id": "89c3124e-c1eb-4017-a4db-8b81536dfd22",
"task_status": "PENDING"
},
"request_id": "85a9f836-4bbe-9b95-bbfd-f3ee1309cebb"
}
}
}Response (In Progress / IN_PROGRESS)
{
"code": "success",
"message": "",
"data": {
"id": 23,
"task_id": "task_9dxfuz8h5gxBvdIqvyoVnZr3DVJxzI1o",
"status": "IN_PROGRESS",
"progress": "30%",
"submit_time": 1778250615,
"start_time": 1778250620,
"finish_time": 0,
"properties": {
"upstream_model_name": "happyhorse-1.0-t2v",
"origin_model_name": "happyhorse-1.0-t2v"
},
"data": {
"output": { "task_status": "RUNNING" }
}
}
}Response (Completed / SUCCESS)
{
"code": "success",
"message": "",
"data": {
"id": 18,
"task_id": "task_SEf9uAzULfIdexfoVEcrpuhNNmQvJVfZ",
"platform": "17",
"user_id": 3,
"group": "default",
"channel_id": 18,
"quota": 297500,
"action": "textGenerate",
"status": "SUCCESS",
"fail_reason": "",
"result_url": "https://dashscope-463f.oss-accelerate.aliyuncs.com/.../xxx.mp4?Expires=...&Signature=...",
"submit_time": 1778249722,
"start_time": 1778249737,
"finish_time": 1778249834,
"created_at": 1778249722,
"updated_at": 1778249834,
"progress": "100%",
"properties": {
"input": "",
"upstream_model_name": "happyhorse-1.0-r2v",
"origin_model_name": "happyhorse-1.0-r2v"
},
"data": {
"output": {
"task_id": "54b32dcb-6887-4839-bd1b-3bf73efef38c",
"end_time": "2026-05-08 22:16:57.506",
"video_url": "https://dashscope-463f.oss-accelerate.aliyuncs.com/.../xxx.mp4",
"orig_prompt": "...",
"submit_time": "2026-05-08 22:15:22.401",
"task_status": "SUCCEEDED"
},
"usage": {
"SR": 720,
"ratio": "16:9",
"duration": 5,
"video_count": 1,
"input_video_duration": 0,
"output_video_duration": 5
},
"request_id": "9f888f70-8519-90b7-a540-a69a31e03181"
}
}
}Response (Failed / FAILURE)
{
"code": "success",
"message": "",
"data": {
"id": 25,
"task_id": "task_xxx",
"status": "FAILURE",
"fail_reason": "InvalidParameter: ...",
"progress": "100%",
"data": {
"output": { "task_status": "FAILED" }
}
}
}Key Fields
| Field | Type | Description |
|---|---|---|
code | string | Response code, "success" on success |
message | string | Response message |
data.id | int | Internal auto-increment task ID |
data.task_id | string | EasyRouter public task ID (with task_ prefix) |
data.status | string | Task status: NOT_START / QUEUED / IN_PROGRESS / SUCCESS / FAILURE |
data.result_url | string | Video URL (non-empty on SUCCESS, upstream OSS signed link) |
data.fail_reason | string | Failure reason (non-empty only on FAILURE) |
data.quota | int | Raw quota value billed for this task |
data.progress | string | Progress string, e.g. "30%" / "100%" |
data.submit_time / start_time / finish_time | int64 | Submit / start / finish Unix timestamps (seconds) |
data.created_at / updated_at | int64 | Record create / last update time |
data.properties.upstream_model_name | string | Actual model name sent to upstream DashScope |
data.properties.origin_model_name | string | Model name requested by the client |
data.data | object | Raw upstream DashScope response (output.video_url, usage, request_id) |
Status Enum
| Status | DashScope Equivalent | Description |
|---|---|---|
NOT_START | PENDING | Created, not yet processing (at submission moment) |
QUEUED | PENDING | Queued upstream |
IN_PROGRESS | RUNNING | Generating |
SUCCESS | SUCCEEDED | Completed, result_url is the video URL |
FAILURE | FAILED / CANCELED / UNKNOWN | Generation failed, fail_reason contains the reason |
The video URL (data.result_url) is an upstream OSS signed link. The Expires= parameter is typically valid for 24 hours — download or persist it to your own storage promptly.
3. HappyHorse-Specific Parameters (metadata)
These fields are passed via the metadata object in the request body. EasyRouter forwards them to upstream DashScope.
3.1 Video Control
| Field | Type | Applicable Models | Description | Default |
|---|---|---|---|---|
ratio | string | t2v / r2v | Aspect ratio: 16:9 / 9:16 / 1:1 / 4:3 / 3:4 | 16:9 |
watermark | bool | All | Whether to add watermark ("Happy Horse" in bottom-right) | true |
seed | int | All | Random seed, range 0 ~ 2147483647, auto-generated if not specified | — |
audio_setting | string | video-edit | Audio control: auto (model-decided) / origin (preserve original) | auto |
i2v does not support ratio: the output aspect ratio automatically follows the input first-frame image.
3.2 Multi-Media Input (input.media)
r2v and video-edit require media input via the metadata.input.media array:
{
"metadata": {
"input": {
"media": [
{ "type": "reference_image", "url": "https://.../img1.jpg" },
{ "type": "reference_image", "url": "https://.../img2.jpg" },
{ "type": "video", "url": "https://.../video.mp4" }
]
}
}
}type | Purpose | Quantity Limit |
|---|---|---|
reference_image | Reference image (r2v and video-edit) | r2v: 1–9; video-edit: 0–5 |
video | Source video (video-edit only) | video-edit: exactly 1 |
first_frame | First-frame image (i2v only, alternative to the top-level input_reference) | i2v: exactly 1 |
r2v prompt convention: In the prompt, reference images using [Image 1], [Image 2] in array order. Example: "The woman in [Image 1] wearing red qipao raises her hand to unfold the fan in [Image 2]."
3.3 Media Asset Limits
| Type | Format | Resolution | File Size | Other |
|---|---|---|---|---|
| Image (i2v first frame) | JPEG / JPG / PNG / WEBP | W & H ≥ 300 px | ≤ 10 MB | Aspect ratio 1:2.5 ~ 2.5:1 |
| Image (r2v reference) | JPEG / JPG / PNG / WEBP | Short edge ≥ 400 px (720P+ recommended) | ≤ 10 MB | Avoid blurry / heavily compressed images |
| Image (video-edit reference) | JPEG / JPG / PNG / WEBP | W & H ≥ 300 px | ≤ 10 MB | Aspect ratio 1:2.5 ~ 2.5:1 |
| Video (video-edit input) | MP4 / MOV (H.264 recommended) | Long edge ≤ 2160, short ≥ 320 | ≤ 100 MB | 3–60s, >8 fps, aspect ratio 1:2.5 ~ 2.5:1 |
video-edit output duration rule: If input video ≤15s, output duration matches input. If input >15s, the system auto-crops the first 15s, so output is max 15s.
4. Full Examples
# Step 1: Submit task
curl -X POST https://easyrouter.io/v1/video/generations \
-H "Authorization: Bearer sk-YourApiKey" \
-H "Content-Type: application/json" \
-d '{
"model": "happyhorse-1.0-t2v",
"prompt": "A cat napping in sunlight, fur gently swaying in the breeze",
"size": "720P",
"duration": 5,
"metadata": {
"ratio": "16:9",
"seed": 42,
"watermark": false
}
}'
# Response (OpenAI flat format):
# {
# "id": "task_9dxfuz8h5gxBvdIqvyoVnZr3DVJxzI1o",
# "task_id": "task_9dxfuz8h5gxBvdIqvyoVnZr3DVJxzI1o",
# "status": "queued", ...
# }
# Step 2: Poll (recommended interval: 10–15s)
curl https://easyrouter.io/v1/video/generations/task_9dxfuz8h5gxBvdIqvyoVnZr3DVJxzI1o \
-H "Authorization: Bearer sk-YourApiKey"
# Step 3: Once data.status == "SUCCESS", download from data.result_url
curl -o cat.mp4 "https://dashscope-463f.oss-accelerate.aliyuncs.com/.../xxx.mp4?Expires=..."# i2v: pass first-frame image via input_reference
curl -X POST https://easyrouter.io/v1/video/generations \
-H "Authorization: Bearer sk-YourApiKey" \
-H "Content-Type: application/json" \
-d '{
"model": "happyhorse-1.0-i2v",
"prompt": "Camera slowly pushes in, the scene comes alive",
"size": "720P",
"duration": 5,
"input_reference": "https://example.com/girl.jpg"
}'i2v's aspect ratio follows input_reference; metadata.ratio is not supported.
# r2v: up to 9 reference images, use [Image 1], [Image 2] in prompt
curl -X POST https://easyrouter.io/v1/video/generations \
-H "Authorization: Bearer sk-YourApiKey" \
-H "Content-Type: application/json" \
-d '{
"model": "happyhorse-1.0-r2v",
"prompt": "The woman in [Image 1] wearing red qipao raises her hand to unfold the fan in [Image 2], while the tassel earrings from [Image 3] sway gently with her head movement",
"size": "720P",
"duration": 5,
"metadata": {
"ratio": "16:9",
"input": {
"media": [
{ "type": "reference_image", "url": "https://example.com/girl.jpg" },
{ "type": "reference_image", "url": "https://example.com/folding-fan.jpg" },
{ "type": "reference_image", "url": "https://example.com/earrings.jpg" }
]
}
}
}'# video-edit: 1 video + up to 5 reference images
curl -X POST https://easyrouter.io/v1/video/generations \
-H "Authorization: Bearer sk-YourApiKey" \
-H "Content-Type: application/json" \
-d '{
"model": "happyhorse-1.0-video-edit",
"prompt": "Make the horse-headed character in the video wear the striped sweater from the image",
"size": "720P",
"metadata": {
"audio_setting": "origin",
"input": {
"media": [
{ "type": "video", "url": "https://example.com/source.mp4" },
{ "type": "reference_image", "url": "https://example.com/sweater.webp" }
]
}
}
}'video-edit ignores duration; output follows input video length (max 15s).
5. Polling Guidelines
| Item | Recommendation |
|---|---|
| Polling interval | 10–15 seconds, never under 5s to avoid rate limiting |
| Terminal states | data.status == "SUCCESS" or data.status == "FAILURE" |
| Total timeout | 5 minutes for t2v/i2v/r2v; 8–10 minutes for video-edit |
| Typical duration | t2v/i2v/r2v: 100–150s; video-edit: 180–250s |
| Video URL validity | Approx. 24 hours; download or persist immediately |
Simple Polling Script (Bash)
#!/bin/bash
TID="task_9dxfuz8h5gxBvdIqvyoVnZr3DVJxzI1o"
KEY="sk-YourApiKey"
while true; do
RESP=$(curl -sS "https://easyrouter.io/v1/video/generations/$TID" -H "Authorization: Bearer $KEY")
echo "$RESP"
STATUS=$(echo "$RESP" | python3 -c "import sys,json;print(json.load(sys.stdin)['data'].get('status',''))")
case "$STATUS" in
SUCCESS|FAILURE) break ;;
esac
sleep 15
done6. Error Handling
6.1 HTTP 400 Parameter Errors (pre-submit validation, no billing)
EasyRouter validates required fields before submitting to upstream:
| Scenario | Response |
|---|---|
Missing prompt | {"code":"invalid_request","message":"prompt is required","data":null} |
| Unknown model | {"code":"invalid_request","message":"unknown model: ..."} |
6.2 HTTP 401 / 403 Auth Errors
401 Unauthorized: API Key invalid or expired403 Forbidden: API Key has no access to this model (check token's model allowlist)
6.3 HTTP 402 Insufficient Balance
Returns insufficient user quota. Top up at the EasyRouter console.
6.4 Task FAILURE Status
Task was accepted but upstream generation failed (data.status == "FAILURE"). Check data.fail_reason:
| Reason | Suggested Action |
|---|---|
| Content moderation | Adjust prompt to avoid sensitive content |
i2v missing input_reference | Image-to-video requires a first-frame image |
| r2v with 0 reference images | Provide at least 1 reference_image |
| video-edit missing video | media must contain exactly 1 type: video element |
| Media URL unreachable | Ensure URL is publicly accessible and not expired |
| Media file violates specs | See Section 3.3 for asset limits |
| Illegal parameter combo (unsupported resolution, etc.) | Follow Section 3 for valid parameters |
When a task enters FAILURE, EasyRouter automatically refunds the billed quota. Check refund records in the logs page.
7. OpenAI-Compatible Endpoints (Optional)
In addition to the primary /v1/video/generations endpoint, HappyHorse is also compatible with the OpenAI-style /v1/videos endpoint, making it easy to integrate with OpenAI SDKs / clients. The two endpoint sets are fully equivalent (same backend task); choose based on your client integration approach.
7.1 Submit (OpenAI-Compatible)
POST /v1/videosRequest body fields are identical to /v1/video/generations. Response is also the OpenAI flat format.
7.2 Query (OpenAI-Compatible)
GET /v1/videos/{task_id}Returns the OpenAI standard flat structure:
{
"id": "task_hI0pZ1xklxOsDWK2wYyK30DT3QQUjM7A",
"object": "video",
"model": "happyhorse-1.0-t2v",
"status": "completed",
"progress": 100,
"created_at": 1778249702,
"completed_at": 1778249795,
"metadata": {
"url": "https://dashscope-463f.oss-accelerate.aliyuncs.com/.../xxx.mp4?Expires=...&Signature=..."
}
}OpenAI Status Mapping
| OpenAI Status | Unified Format Status |
|---|---|
queued | NOT_START / QUEUED |
in_progress | IN_PROGRESS |
completed | SUCCESS |
failed | FAILURE |
You can mix the two endpoints: submitting via POST /v1/video/generations and querying via GET /v1/videos/{id} works fine — both share the same task_id. But we recommend staying consistent within a single business flow for easier debugging.