HappyHorse
HappyHorse is a video generation model series covering four capabilities — text-to-video,
image-to-video, reference-to-video, and video editing. Unlike model families where the
mode is inferred from the materials you attach, HappyHorse names the capability in the model code
itself: -t2v, -i2v, -r2v, -video-edit.
https://llm.siraya.ai/v1/videos/generations
https://llm.siraya.ai/v1/videos/{video_id}
Synchronous by default, asynchronous on request
By default the request waits for the render and returns the finished video URL. A HappyHorse
render can take minutes, so async: true is recommended: you get an id back
immediately and poll GET /v1/videos/{video_id} every ~15 seconds until status is
completed or failed. Pass a callbackUrl to be notified instead of polling. See
Asynchronous generation.
Supported Models
| Model ID | Capability | Resolutions |
|---|---|---|
happyhorse-1.0-t2v |
Text-to-video | 720P, 1080P |
happyhorse-1.0-i2v |
Image-to-video (first frame) | 720P, 1080P |
happyhorse-1.0-r2v |
Reference-to-video (1–9 reference images) | 720P, 1080P |
happyhorse-1.0-video-edit |
Video editing (source video + up to 5 reference images) | 720P, 1080P |
happyhorse-1.1-t2v |
Text-to-video | 480P, 720P, 1080P |
happyhorse-1.1-i2v |
Image-to-video (first frame) | 480P, 720P, 1080P |
happyhorse-1.1-r2v |
Reference-to-video (1–9 reference images) | 480P, 720P, 1080P |
1.0 vs 1.1
The two versions differ in exactly two ways:
- 1.1 adds a 480P tier. 1.0 offers 720P and 1080P only.
- Video editing exists only in 1.0. There is no 1.1 video-edit model.
Everything else — seconds, aspect_ratio, seed, watermark, and the media containers — is
identical between versions for the same capability.
Capabilities by model
✅ supported · — not accepted.
| Capability | seconds |
resolution |
aspect_ratio |
seed |
watermark |
frame_images |
input_references |
audio_setting |
|---|---|---|---|---|---|---|---|---|
Text-to-video (-t2v) |
✅ | ✅ | ✅ | ✅ | ✅ | — | — | — |
Image-to-video (-i2v) |
✅ | ✅ | — | ✅ | ✅ | ✅ first frame only | — | — |
Reference-to-video (-r2v) |
✅ | ✅ | ✅ | ✅ | ✅ | — | ✅ 1–9 images | — |
Video editing (-video-edit) |
— | ✅ 720P/1080P | — | ✅ | ✅ | — | ✅ 1 video + ≤5 images | ✅ |
aspect_ratiois accepted on image-to-video and video editing but has no effect — the source material sets the frame.secondsis likewise not accepted on video editing.
Header
Bearer <API_KEY>).
Body
All HappyHorse parameters are sent flat, at the top level of the request body. There is no
extra_body passthrough for this family — every supported parameter has a top-level field.
happyhorse-1.0-t2v.
[Image 1], [Image 2] … to refer to entries in input_references —
see Reference-to-video.
3–15; a value outside the range returns a 400 at submit. duration is
not a supported field — use seconds. Not accepted on video editing — the output length follows the
source video instead.
480P, 720P, or 1080P. 480P is available on 1.1 models only; video editing is 720P/1080P.
Values are case-sensitive — 480p is rejected with a 400.
16:9, 9:16, 1:1, 4:3, 3:4, 4:5, 5:4, 9:21, 21:9. Applies to text-to-video
and reference-to-video only — on image-to-video and video editing the source material sets the
frame and this field has no effect.
0–2147483647. When omitted, a random seed is generated.
"watermark": false explicitly if you don't want one. See
Behavior worth knowing.
frame_type: "first_frame" — HappyHorse has no
last_frame support. See frame_images.
role and a url —
see input_references.
auto — the model decides the soundtrack from the prompt — or origin to keep
the source video's audio.
false, the request waits for generation to finish and returns the completed video URL. When true, it returns immediately with a video id you poll for status — recommended here, since HappyHorse renders can take minutes. See Asynchronous generation.
async is true. An http(s) URL the router will POST to once generation completes (or fails), so you don't have to poll.
curl https://llm.siraya.ai/v1/videos/generations \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <API_KEY>" \
-d '{
"model": "happyhorse-1.0-t2v",
"prompt": "A lighthouse on a cliff at sunset",
"seconds": 5,
"resolution": "720P",
"aspect_ratio": "16:9",
"watermark": false,
"seed": 42
}'
import requests
url = "https://llm.siraya.ai/v1/videos/generations"
headers = {
"Content-Type": "application/json",
"Authorization": "Bearer <API_KEY>"
}
data = {
"model": "happyhorse-1.0-t2v",
"prompt": "A lighthouse on a cliff at sunset",
"seconds": 5,
"resolution": "720P",
"aspect_ratio": "16:9",
"watermark": False,
"seed": 42
}
response = requests.post(url, headers=headers, json=data)
print(response.json())
Example Response
{
"created": 1760347750,
"data": [
{
"url": "https://resources.siraya.ai/v1/videos/video_8fK2mQxR7pLnVwZ3tYbC.../content"
}
],
"error": {}
}
With "async": true, the job is accepted immediately instead:
{
"id": "video_8fK2mQxR7pLnVwZ3tYbC...",
"object": "video",
"status": "processing",
"model": "happyhorse-1.0-t2v",
"seconds": 5,
"usage": { "completion_tokens": 0, "total_tokens": 0 }
}
Poll GET /v1/videos/{video_id} with that id until status is completed — see
Poll for status.
Media inputs
HappyHorse uses two different containers depending on whether the material is a hard constraint on the frame or soft guidance for the content.
frame_images
Pins the opening frame of an image-to-video render. Exactly one entry, and first_frame is the only
supported type — there is no last-frame control in this family.
| Field | Type | Description |
|---|---|---|
frame_type |
string | first_frame only. |
image_url |
string | Public URL or a data:image/jpeg;base64,... data URL. |
{
"model": "happyhorse-1.1-i2v",
"prompt": "The scene comes to life",
"frame_images": [
{ "frame_type": "first_frame", "image_url": "https://your-domain.com/cat.jpg" }
],
"seconds": 5,
"resolution": "720P"
}
aspect_ratio is ignored here — the frame you supply determines the output shape. Choose a first
frame already cropped to the ratio you want.
input_references
Soft-guidance material for reference-to-video and video editing. Each entry carries a role and a
url:
role |
Used by | Count |
|---|---|---|
reference_image |
reference-to-video | 1–9 |
video |
video editing — the source clip being edited | exactly 1, required |
reference_image |
video editing — extra style/subject material | 0–5, optional |
Reference image requirements: JPEG/JPG/PNG/WEBP, shortest side ≥ 400px, ≤ 20MB. Source video requirements (video editing): MP4 or MOV, 3–60 seconds, ≤ 100MB.
Reference-to-video
-r2v models build a video from 1–9 reference images. The prompt addresses them positionally
with [Image 1], [Image 2] … matching the order of the input_references array.
{
"model": "happyhorse-1.1-r2v",
"prompt": "[Image 1] stands in a sunny meadow holding the object from [Image 2]",
"input_references": [
{ "role": "reference_image", "url": "https://your-domain.com/character.jpg" },
{ "role": "reference_image", "url": "https://your-domain.com/prop.jpg" }
],
"resolution": "720P",
"aspect_ratio": "16:9"
}
Video editing
happyhorse-1.0-video-edit restyles or edits an existing clip. It takes one required video entry
and optionally up to five reference_image entries for style or subject guidance.
{
"model": "happyhorse-1.0-video-edit",
"prompt": "Change the style to a watercolor painting",
"input_references": [
{ "role": "video", "url": "https://your-domain.com/input.mp4" }
],
"audio_setting": "origin",
"resolution": "720P",
"watermark": false
}
secondsis not accepted. The output length follows the source video — kept as-is up to 15 seconds, truncated to the first 15 seconds if the source is longer. Thesecondsfield in the status response reports the real output length.aspect_ratiohas no effect — the source video sets the frame.audio_settingdecides the soundtrack:autolets the model score the clip from your prompt,originkeeps the source audio.
Poll for status
https://llm.siraya.ai/v1/videos/{video_id}
When you submit with async: true, call the status endpoint with the returned id until status
reads completed or failed. Poll roughly every 15 seconds and back off on 429. Passing a callbackUrl at submit time removes the need to
poll at all.
{
"id": "video_8fK2mQxR7pLnVwZ3tYbC...",
"object": "video",
"status": "completed",
"model": "happyhorse-1.0-t2v",
"seconds": 5,
"resolution": "720p",
"ratio": "16:9",
"output_url": "https://.../out.mp4?Expires=...&Signature=..."
}
Response object
| Field | Type | Present | Meaning |
|---|---|---|---|
id |
string | always | Opaque — pass it straight to GET /v1/videos/{video_id}; don't parse it |
object |
string | always | Always video |
status |
string | always | processing · completed · failed |
error |
object | on failure | { code, message } |
seconds |
integer | completed | Output duration, rounded to the nearest second |
resolution, ratio |
string | completed | Reported for the finished video; resolution is lowercase <height>p (e.g. 720p) even though requests use 720P |
output_url |
string | completed | Signed and expires after 24 hours — download promptly, don't cache it as a stable link |
Once status is completed you can also stream the raw bytes from
GET /v1/videos/{video_id}/content.
Behavior worth knowing
Watermarking is on by default. HappyHorse is the exception among the video families on this
platform: watermark defaults to true, so an output you never configured comes back watermarked.
Send "watermark": false on every request where that matters.
Resolution values are case-sensitive. 720P and 1080P are accepted; 720p and 480p return
a 400 (Input should be '1080P' or '720P'). This differs from the lowercase 720p used by most
other video models on the platform.
Results expire in 24 hours. Both the video id and the output_url stop working after a day.
Download and re-host anything you need to keep.
Errors
A request-time refusal is final — it means the parameters can't work together, not that the service is unavailable. Retrying the same body won't help.
{
"error": {
"code": "invalid_request_error",
"message": "Input should be '1080P' or '720P'",
"type": "invalid_request_error"
}
}
| Status | Meaning |
|---|---|
400 |
An invalid value or unsupported combination — a lowercase resolution, 480P on a 1.0 model, a seconds outside 3–15, or a reference image below the size floor |
404 |
Unrecognized model code |
200 with status: "processing" |
Accepted — keep polling |