Skip to content

Wan / HappyHorse: video generation API

Generate video with Wan 3.0 and HappyHorse 1.1: parameters, results, and caveats.

ItemValue
Base URLhttps://api.hop-base.com
Submit a taskPOST /api/v1/services/aigc/video-generation/video-synthesis
Query a taskGET /api/v1/tasks/{task_id}
Key groupWan: Wan 3.0 Official; HappyHorse: HappyHorse 1.1 Official

HopBase exposes Wan and HappyHorse video generation through the official API contract, on the original paths. Code written against the official SDK or HTTP API migrates by changing only the base URL and the key — the request body, the response shape, and the paths are unchanged.

Available models

Model IDDurationResolutionInputOfficial price
wan3.0-videointeger 2-30 s (default 5)480P / 720P / 1080Pfirst / last frame, or the reference group$0.0441from/ second
happyhorse-1.1-t2vinteger 3-15 s (default 5)480P / 720P / 1080Pprompt only$0.0662from/ second
happyhorse-1.1-i2vinteger 3-15 s (default 5)480P / 720P / 1080Pexactly 1 first_frame image$0.0662from/ second
happyhorse-1.1-r2vinteger 3-15 s (default 5)480P / 720P / 1080P1-9 reference_image images$0.0662from/ second

Wan 3.0 duration also accepts -1 for automatic. Its reference group takes up to 10 reference images, 5 reference videos, 5 reference audio tracks, 1 document, and 1 web link.

Use the exact IDs above. No aliases are registered; unknown model names are rejected. wan3.0-video-prime, happyhorse-1.1-video-edit, and the HappyHorse 1.0 line are not offered.

wan3.0-video and the happyhorse-1.1-* models are served by two separate groups — Wan 3.0 Official and HappyHorse 1.1 Official — so a key reaches a model only if it includes that model's group; one key can include both. Before sending paid work, confirm which models your key reaches with GET /v1/models.

Per-tier prices and full specs are on the model cards: wan3.0-video, happyhorse-1.1-t2v, happyhorse-1.1-i2v, happyhorse-1.1-r2v.

Request parameters

All requests use Authorization: Bearer sk-your-key and Content-Type: application/json. You do not need to send an async header; tasks are always asynchronous.

{
  "model": "wan3.0-video",
  "input": { "prompt": "...", "media": [{ "type": "first_frame", "url": "..." }] },
  "parameters": { "resolution": "1080P", "duration": 5 }
}
ParameterRequiredType & limitsDefaultDescription
modelRequiredone of the four IDs above—Exact ID
input.promptwan3.0-video: prompt or media
-t2v / -r2v: Required
-i2v: Optional
string; Wan 3.0 ≤20,000 characters, HappyHorse ≤5,000—See the note below
input.mediawan3.0-video: prompt or media
-i2v / -r2v: Required
-t2v: not accepted
array of { "type", "url" }; per-type limits below——
parametersOptionalobject, see below—May be omitted entirely

HappyHorse truncates Chinese prompts of 2,501-5,000 characters to about 2,500 without an error.

input.media

typeRequiredType & limitsDefaultDescription
first_frame-i2v: requiredimage; Wan 3.0 ≤1; -i2v exactly 1—Not accepted by -t2v / -r2v
last_frameOptionalimage; Wan 3.0 only, ≤1——
reference_image-r2v: requiredimage; Wan 3.0 ≤10; -r2v 1-9——
reference_videoOptionalWan 3.0 only; ≤5, ≤15 s combined—Billed on input duration; input + output ≤30 s
reference_audioOptionalWan 3.0 only; ≤5, ≤15 s combined——
fileOptionalWan 3.0 only; 1 document, ≤100 MB, ≤50 pages—Mutually exclusive with link
linkOptionalWan 3.0 only; 1 public web page URL, not data:——

file accepts docx / doc / xlsx / xls / pptx / ppt / pdf / txt / key / pages / numbers / md.

For Wan 3.0, first / last frame input and the reference group (reference_*, file, link) are mutually exclusive.

url accepts an absolute public http(s) URL or a data: URI (base64). oss:// is rejected — the object belongs to your own cloud account, which the gateway cannot access.

Types, counts, and combinations are checked at submission; file size, dimensions, codec, durations, and page counts are checked by the service during execution. The whole JSON body is capped at 128 MiB including base64, so use URLs for large media.

parameters

ParameterRequiredType & limitsDefaultDescription
resolutionOptional480P / 720P / 1080P1080PSelects the billing tier
durationOptionalinteger seconds; Wan 3.0: 2-30 or -1 (automatic); HappyHorse: 3-155—
ratioOptionalWan 3.0: adaptive / 16:9 / 4:3 / 1:1 / 3:4 / 9:16; HappyHorse -t2v / -r2v: 16:9 / 9:16 / 1:1 / 4:3 / 3:4 / 4:5 / 5:4 / 9:21 / 21:9; -i2v: not acceptedservice default-i2v output follows the input image
audioOptionalboolean; Wan 3.0 onlytrueAudio on or off costs the same
prompt_extendOptionalboolean; Wan 3.0 onlytruePrompt rewriting
watermarkOptionalbooleanWan 3.0: false; HappyHorse: trueSee the callout below
seedOptionalinteger 0-2147483647——

A parameter the model does not support returns 400 instead of being ignored. The gateway always sends resolution and duration (defaults above); other omitted fields are not sent, so the service defaults apply.

HappyHorse watermarks output by default.

The official default for HappyHorse watermark is true, which stamps a "Happy Horse" mark on the clip. HopBase does not override the official defaults, so send "watermark": false explicitly if you need a clean output.

Image-to-video example:

{
  "model": "happyhorse-1.1-i2v",
  "input": {
    "prompt": "The camera slowly pushes in, light drifting across the scene",
    "media": [{ "type": "first_frame", "url": "data:image/jpeg;base64,..." }]
  },
  "parameters": { "resolution": "720P", "duration": 5, "watermark": false }
}

Response

Submit response

A successful submit returns 200 with the HopBase task ID in the official response shape, in output.task_id (shaped like blt…):

{
  "output": {
    "task_id": "blt60x4115010d-d361-4bfc-b131-a382f1c400a5",
    "task_status": "PENDING"
  },
  "request_id": "..."
}

Task query fields

Poll GET /api/v1/tasks/{task_id}.

FieldDescription
output.task_idHopBase task ID, blt…
output.task_statusPENDING / RUNNING / SUCCEEDED / FAILED
output.video_urlOutput URL on success, valid for 24 hours
output.orig_promptPrompt echoed on success
output.code / output.messageOn failure: code is always TaskFailed; read message for the cause
modelModel ID the task used
usageMetering and charge on success; see "Billing"
usage.cost / usage.cost_usd / usage.currencyThe charge for this task: cost_usd is the USD amount actually deducted from your balance, cost is the same charge in CNY, and currency is always CNY

output.task_status moves PENDING → RUNNING → SUCCEEDED | FAILED (uppercase); only the last two are terminal, so treat any other value as still running. A typical clip completes in 1-5 minutes; long Wan 3.0 clips take longer. Poll at roughly 15-second intervals.

In progress

{
  "output": {
    "task_id": "blt60x4115010d-d361-4bfc-b131-a382f1c400a5",
    "task_status": "RUNNING"
  },
  "model": "wan3.0-video"
}

Succeeded

On success the response carries the output URL in output.video_url:

{
  "output": {
    "task_id": "blt60x4115010d-d361-4bfc-b131-a382f1c400a5",
    "task_status": "SUCCEEDED",
    "video_url": "https://api.hop-base.com/...",
    "orig_prompt": "..."
  },
  "usage": { "video_count": 1, "duration": 5, "SR": 1080, "output_video_duration": 5, "input_video_duration": 0, "ratio": "16:9", "billed_seconds": 5, "billing_bucket": "1080p", "cost": 4.08, "cost_usd": 0.6, "currency": "CNY" }
}

Failed

A failed task is never billed. code is always TaskFailed, so read output.message (async task failures):

{ "output": { "task_id": "blt60x...", "task_status": "FAILED", "code": "TaskFailed", "message": "..." } }

Minimal example

BASE=https://api.hop-base.com
# 1. Submit
curl "$BASE/api/v1/services/aigc/video-generation/video-synthesis" \
  -H "Authorization: Bearer sk-your-key" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "wan3.0-video",
    "input": {
      "prompt": "Aerial shot of snow mountains above clouds at sunrise"
    },
    "parameters": {
      "resolution": "1080P",
      "duration": 5,
      "ratio": "16:9"
    }
  }'

# 2. Poll with the returned output.task_id, about every 15 seconds
curl "$BASE/api/v1/tasks/blt-your-task-id" \
  -H "Authorization: Bearer sk-your-key"

Notes

An invalid parameter fails ten minutes later.

The official API accepts almost any parameter at submit time: an invalid resolution still returns a task ID, then fails roughly ten minutes later. HopBase decodes the body strictly and rejects such requests immediately with 400 instead.

  • Unknown fields and a second JSON value are rejected before the request goes out. This is deliberate.
  • Reference media is downloaded on the official side, not by HopBase. Image hosts that block datacenter IPs (many free image CDNs do) cause the task to fail minutes after submission, so prefer data: embeds or your own object storage / CDN.
  • Poll GET /api/v1/tasks/{task_id}. Note this path has no /v1 in front of /api, unlike the video endpoints on other pages. There is no list endpoint; keep track of task IDs on your side.
  • A task still unfinished 2 hours after creation is failed automatically, and these tasks cannot be cancelled.
  • output.video_url is a signed URL on api.hop-base.com, valid for 24 hours from the moment the task completes.
  • Querying again returns the same URL without extending it, and an expired URL returns 410, so download results within that window.
  • usage.ratio echoes the produced aspect ratio and may be raw dimensions (e.g. 1632:937) rather than one of the request enum values.

Common errors and fixes

Request errors return {"code":…,"message":…}.

StatuscodeCause / what to do
400InvalidParameterInvalid field or value; fix the field named in message
400DataInspectionFailedContent moderation; rewrite the prompt or replace the media
400InvalidFile / InvalidURLMedia file or URL unusable; replace it
413InvalidParameterRequest body too large; use URLs, not data:
404InvalidParameterMalformed task ID or task not found
429Throttling...Rate limited; back off and retry

The 413 and 404 message texts:

request body exceeds the 128 MiB limit (received N bytes)
invalid task ID format
task not found            # also returned for another user's key

Any other field (OpenAI's size / n / image, Wan 2.x's img_url / negative_prompt / shot_type, extra keys in media) returns 400:

400 {"code":"InvalidParameter","message":"request body does not match the JSON contract: json: unknown field \"size\""}

When a task fails, act on output.message:

messageWhat to do
invalid request parameters: ...
... parameters or reference media were rejected ...
Fix it and resubmit
a reference media file could not be downloaded ...Use a data: URI or reachable hosting
... declined by content moderation ...
... blocked by content moderation ...
... may infringe intellectual property rights ...
Rewrite the prompt or replace the media
video generation failed ...
... stopped before completion ...
... finished without a usable video ...
Resubmit; contact support if it repeats
... did not finish within 2 hours ...Resubmit; contact support with the task ID if it repeats

Billing

Submitting a task checks available balance minus the estimates for in-flight tasks minus this request's own estimate; if that falls short you get a 402 with code insufficient_balance. The estimate is released automatically once the task ends. See Video overview for the details shared by every video family. A failed task is never billed.

Both series are billed by video duration in seconds, at a per-second unit price that varies by resolution tier — not by token:

charge = usage.billed_seconds × the rate for the usage.billing_bucket tier

usage.SR reports the generated video's resolution tier (480 / 720 / 1080) and is what selects it. The two series charge different rates at the same tier.

They differ in what the billable duration covers:

  • wan3.0-video is billed on the input video as well as the output — billable duration = input video duration + output video duration — so usage.input_video_duration is added to the billed seconds; it reads 0.0 when the request carries no video input.
  • The happyhorse-1.1-* models are billed on output only, and for reference-to-video the official documentation fixes input_video_duration at 0.

Once the task finishes, the query's top-level usage also carries cost_usd (the USD amount actually deducted from your balance for this task, 0 for an uncharged failure), cost (the same charge in CNY), and currency (always CNY).

Official list prices are on the pricing page. Your own rate is the one shown in the signed-in model catalog, and each request's actual charge is in your usage record.

Next steps