EasyRouterEasyRouter
User GuideAPI DocsConnect Agent Tools
AI Model APIVideos

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

ModelCapabilityRequired Media InputDescription
happyhorse-1.0-t2vText-to-VideoGenerate video from pure text prompt
happyhorse-1.0-i2vImage-to-Video1 first-frame imageGenerate video starting from a first-frame image
happyhorse-1.0-r2vReference-to-Video1–9 reference imagesMulti-image reference generation, prompt uses [Image N] to reference
happyhorse-1.0-video-editVideo Editing1 video + 0–5 reference imagesStyle 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/generations

Request Headers

HeaderRequiredDescription
AuthorizationBearer sk-YourApiKey
Content-Typeapplication/json

Request Body Fields

FieldTypeRequiredDescription
modelstringHappyHorse model name (see table above)
promptstringText prompt. Max 5000 non-Chinese chars or 2500 Chinese chars; excess is auto-truncated
sizestringResolution tier. 720P / 1080P (default 1080P)
durationintVideo duration in seconds, range 3~15, default 5. Ignored by video-edit (duration follows input video, max 15s)
input_referencestringmodel-dependenti2v only: first-frame image URL (publicly accessible)
metadataobjectmodel-dependentHappyHorse-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

FieldTypeDescription
codestringResponse code, "success" on success
messagestringResponse message
data.idintInternal auto-increment task ID
data.task_idstringEasyRouter public task ID (with task_ prefix)
data.statusstringTask status: NOT_START / QUEUED / IN_PROGRESS / SUCCESS / FAILURE
data.result_urlstringVideo URL (non-empty on SUCCESS, upstream OSS signed link)
data.fail_reasonstringFailure reason (non-empty only on FAILURE)
data.quotaintRaw quota value billed for this task
data.progressstringProgress string, e.g. "30%" / "100%"
data.submit_time / start_time / finish_timeint64Submit / start / finish Unix timestamps (seconds)
data.created_at / updated_atint64Record create / last update time
data.properties.upstream_model_namestringActual model name sent to upstream DashScope
data.properties.origin_model_namestringModel name requested by the client
data.dataobjectRaw upstream DashScope response (output.video_url, usage, request_id)

Status Enum

StatusDashScope EquivalentDescription
NOT_STARTPENDINGCreated, not yet processing (at submission moment)
QUEUEDPENDINGQueued upstream
IN_PROGRESSRUNNINGGenerating
SUCCESSSUCCEEDEDCompleted, result_url is the video URL
FAILUREFAILED / CANCELED / UNKNOWNGeneration 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

FieldTypeApplicable ModelsDescriptionDefault
ratiostringt2v / r2vAspect ratio: 16:9 / 9:16 / 1:1 / 4:3 / 3:416:9
watermarkboolAllWhether to add watermark ("Happy Horse" in bottom-right)true
seedintAllRandom seed, range 0 ~ 2147483647, auto-generated if not specified
audio_settingstringvideo-editAudio 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" }
      ]
    }
  }
}
typePurposeQuantity Limit
reference_imageReference image (r2v and video-edit)r2v: 1–9; video-edit: 0–5
videoSource video (video-edit only)video-edit: exactly 1
first_frameFirst-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

TypeFormatResolutionFile SizeOther
Image (i2v first frame)JPEG / JPG / PNG / WEBPW & H ≥ 300 px≤ 10 MBAspect ratio 1:2.5 ~ 2.5:1
Image (r2v reference)JPEG / JPG / PNG / WEBPShort edge ≥ 400 px (720P+ recommended)≤ 10 MBAvoid blurry / heavily compressed images
Image (video-edit reference)JPEG / JPG / PNG / WEBPW & H ≥ 300 px≤ 10 MBAspect ratio 1:2.5 ~ 2.5:1
Video (video-edit input)MP4 / MOV (H.264 recommended)Long edge ≤ 2160, short ≥ 320≤ 100 MB3–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

ItemRecommendation
Polling interval10–15 seconds, never under 5s to avoid rate limiting
Terminal statesdata.status == "SUCCESS" or data.status == "FAILURE"
Total timeout5 minutes for t2v/i2v/r2v; 8–10 minutes for video-edit
Typical durationt2v/i2v/r2v: 100–150s; video-edit: 180–250s
Video URL validityApprox. 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
done

6. Error Handling

6.1 HTTP 400 Parameter Errors (pre-submit validation, no billing)

EasyRouter validates required fields before submitting to upstream:

ScenarioResponse
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 expired
  • 403 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:

ReasonSuggested Action
Content moderationAdjust prompt to avoid sensitive content
i2v missing input_referenceImage-to-video requires a first-frame image
r2v with 0 reference imagesProvide at least 1 reference_image
video-edit missing videomedia must contain exactly 1 type: video element
Media URL unreachableEnsure URL is publicly accessible and not expired
Media file violates specsSee 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/videos

Request 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 StatusUnified Format Status
queuedNOT_START / QUEUED
in_progressIN_PROGRESS
completedSUCCESS
failedFAILURE

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.


8. FAQ