HappyHorse API Documentation
Complete guide to integrating the HappyHorse 1.0 Video Generation API into your applications.
Quick Start
curl -X POST 'https://happyhorse.app/api/generate' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"model": "happyhorse-1.0/video",
"prompt": "A cinematic shot of mountains at sunrise",
"mode": "pro",
"duration": 5,
"aspect_ratio": "16:9"
}'Authentication
All API requests require authentication using a Bearer token in the Authorization header.
Get Your API Key: You can get your API key from the API Keys page in your dashboard. → Get Your API Key
Authorization: Bearer YOUR_API_KEYHow Generate and Status Work
HappyHorse video tasks are asynchronous: create a task with POST /api/generate, keep the returned task_id, then check GET /api/status until it reaches a final state.
Create a task
Send your prompt, model, mode, duration, and optional images to /api/generate. A successful response returns task_id immediately.
Poll status
Use task_id with /api/status. While status is IN_PROGRESS, the video is still rendering and response stays null.
Read the result
When status becomes SUCCESS, use data.response.resultUrls[0] as the video URL. If it becomes FAILED, show error_message and stop polling.
Available Models
HappyHorse 1.0
High-quality AI video generation powered by HappyHorse
| Type | Description | Duration | Credits |
|---|---|---|---|
pro (text-to-video) | Pro quality text-to-video generation | 3-15s | 54/s (no audio) · 80/s (with audio) |
pro (image-to-video) | Pro quality image-to-video animation | 3-15s | 54/s (no audio) · 80/s (with audio) |
std (text-to-video) | Standard quality text-to-video | 3-15s | 40/s (no audio) · 60/s (with audio) |
std (image-to-video) | Standard quality image-to-video | 3-15s | 40/s (no audio) · 60/s (with audio) |
API Endpoints
Create a new video generation task. Requires model field set to 'happyhorse-1.0/video'.
Request Body
Model name, must be 'happyhorse-1.0/video'
Text description of the video to generate (max 2500 chars). Required unless multi_shots is true.
Quality mode: 'pro' or 'std' (default: std) Defaults to std.
Video duration in seconds (3-15). In multi-shot mode, the final duration is derived from multi_prompt; if you send duration explicitly, keep it equal to the summed shot duration. Defaults to 5 / sum(multi_prompt).
Output aspect ratio (16:9, 9:16, 1:1) Defaults to 16:9.
Array of image URLs for image-to-video. Single-shot: up to 2 URLs (first frame + last frame). Multi-shot: 1 URL (first frame only). When provided, aspect_ratio is auto-adapted.
Enable native audio generation (default: true). For multi-shot requests, send this field explicitly as true or false. Defaults to true.
Prompt adherence strength (0-1, default: 0.5). Higher values follow the prompt more closely. Defaults to 0.5.
Enable multi-shot mode with multiple prompts Defaults to false.
Array of 'prompt, duration' objects for multi-shot mode (max 5 shots, each prompt max 500 chars, each duration 1-12s)
Array of element objects. In your prompt, reference an element by writing @ immediately followed by that element's name value (e.g. if name is 'element_dog', include @element_dog in the prompt text). Each element: name (string), description (string), element_input_urls (2-4 image URLs). Max 3 elements per task.
Text to Video
{
"model": "happyhorse-1.0/video",
"prompt": "A majestic eagle soaring through clouds at sunset",
"mode": "pro",
"duration": 5,
"aspect_ratio": "16:9",
"sound": true
}Image to Video
{
"model": "happyhorse-1.0/video",
"prompt": "The character slowly turns and smiles",
"mode": "pro",
"image_urls": ["https://example.com/my-image.jpg"],
"duration": 5
}Multi-Shot Video
{
"model": "happyhorse-1.0/video",
"mode": "pro",
"multi_shots": true,
"sound": true,
"duration": 10,
"multi_prompt": [
{ "prompt": "A woman walks into a coffee shop", "duration": 3 },
{ "prompt": "She orders a latte and sits by the window", "duration": 4 },
{ "prompt": "She looks outside and smiles", "duration": 3 }
],
"aspect_ratio": "16:9"
}Responses
Task created successfully
{
"code": 200,
"message": "success",
"data": {
"task_id": "n92abc123hh10",
"status": "IN_PROGRESS"
}
}Check the status of a video generation task using the task_id returned from the generate endpoint.
Query Parameters
The unique task ID returned from the generate endpoint (prefixed with n92)
Example Request
curl -X GET 'https://happyhorse.app/api/status?task_id=n92abc123hh10' \
-H 'Authorization: Bearer YOUR_API_KEY'Tip: The response field contains a resultUrls array. Access data.response.resultUrls[0] to get the video URL.
// Extract video URL after the task is complete
const task = payload.data;
const videoUrl = task.status === "SUCCESS"
? task.response?.resultUrls?.[0]
: null;Responses
{
"code": 200,
"message": "success",
"data": {
"task_id": "n92abc123hh10",
"status": "SUCCESS",
"consumed_credits": 400,
"created_at": "2026-04-08T10:30:00Z",
"type": "text-to-video",
"request": {
"model": "happyhorse-1.0/video",
"prompt": "A majestic eagle soaring through clouds at sunset",
"mode": "pro",
"duration": 5,
"aspect_ratio": "16:9"
},
"response": {
"resultUrls": [
"https://cdn.example.com/videos/abc123.mp4"
]
},
"error_message": null
}
}Polling Best Practices
Video generation can take a little time, so treat status checks as a calm background job instead of a tight loop.
Start with a short delay
Wait about 8-10 seconds before the first status check, then poll every 10-20 seconds. Faster polling will not make the video finish sooner.
Stop on final states
SUCCESS and FAILED are terminal. Stop polling as soon as you see either status, and store the task_id for support or later lookup.
Use a client timeout
After 10-15 minutes, stop the active wait and let the user retry the status check manually. Do not leave a browser tab polling forever.
Back off on errors
If you receive 429 or a temporary network error, slow down before the next request. For 401 or 402, ask the user to fix the API key or credits.
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
async function waitForHappyHorseVideo(taskId) {
const deadline = Date.now() + 15 * 60 * 1000;
let delayMs = 8000;
while (Date.now() < deadline) {
const res = await fetch(
`${BASE_URL}/api/status?task_id=${encodeURIComponent(taskId)}`,
{ headers: { Authorization: `Bearer ${API_KEY}` } }
);
if (res.status === 429) {
await sleep(Math.min(delayMs * 2, 30000));
continue;
}
const payload = await res.json();
if (!res.ok) {
throw new Error(payload.message || `Status request failed: ${res.status}`);
}
const task = payload.data;
if (task.status === "SUCCESS") {
return task.response?.resultUrls?.[0];
}
if (task.status === "FAILED") {
throw new Error(task.error_message || "Generation failed");
}
await sleep(delayMs);
delayMs = Math.min(delayMs + 2000, 20000);
}
throw new Error("Timed out. Keep the task_id and check again later.");
}API Playground
Test the API directly from your browser. Replace YOUR_API_KEY with your actual API key.
Error Codes
| HTTP status | Code | Description |
|---|---|---|
| 400 Bad Request | INVALID_PROMPT | The prompt is invalid or empty |
| 400 Bad Request | INVALID_DURATION | Duration out of supported range (3-15s) |
| 400 Bad Request | INVALID_MODEL | Model must be 'happyhorse-1.0/video' |
| 401 Unauthorized | INVALID_API_KEY | API key is missing or invalid |
| 402 Payment Required | INSUFFICIENT_CREDITS | Not enough credits for this operation |
| 429 Too Many Requests | RATE_LIMITED | Too many requests, please slow down |
| 500 Internal Server Error | INTERNAL_ERROR | Server error, please try again later |