HappyHorse API Documentation

Complete guide to integrating the HappyHorse 1.0 Video Generation API into your applications.

API v1.0 Base URL: https://happyhorse.app

Quick Start

bash
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

http
Authorization: Bearer YOUR_API_KEY

How 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.

1

Create a task

Send your prompt, model, mode, duration, and optional images to /api/generate. A successful response returns task_id immediately.

2

Poll status

Use task_id with /api/status. While status is IN_PROGRESS, the video is still rendering and response stays null.

3

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

HappyHorse 1.0

High-quality AI video generation powered by HappyHorse

TypeDescriptionDurationCredits
pro (text-to-video)Pro quality text-to-video generation3-15s54/s (no audio) · 80/s (with audio)
pro (image-to-video)Pro quality image-to-video animation3-15s54/s (no audio) · 80/s (with audio)
std (text-to-video)Standard quality text-to-video3-15s40/s (no audio) · 60/s (with audio)
std (image-to-video)Standard quality image-to-video3-15s40/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

Body parametersJSON
model:string

Model name, must be 'happyhorse-1.0/video'

prompt:optional string

Text description of the video to generate (max 2500 chars). Required unless multi_shots is true.

mode:optional string

Quality mode: 'pro' or 'std' (default: std) Defaults to std.

duration:optional number

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).

aspect_ratio:optional string

Output aspect ratio (16:9, 9:16, 1:1) Defaults to 16:9.

image_urls:optional string[]

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.

sound:optional boolean

Enable native audio generation (default: true). For multi-shot requests, send this field explicitly as true or false. Defaults to true.

cfg_scale:optional number

Prompt adherence strength (0-1, default: 0.5). Higher values follow the prompt more closely. Defaults to 0.5.

multi_shots:optional boolean

Enable multi-shot mode with multiple prompts Defaults to false.

multi_prompt:optional array

Array of 'prompt, duration' objects for multi-shot mode (max 5 shots, each prompt max 500 chars, each duration 1-12s)

happyhorse_elements:optional array

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

json
{
  "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

json
{
  "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

json
{
  "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

Query Parameters
task_id:string

The unique task ID returned from the generate endpoint (prefixed with n92)

Example Request

bash
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.

javascript
// 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.

javascript
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.

API PlaygroundPOST

Error Codes

HTTP statusCodeDescription
400 Bad RequestINVALID_PROMPTThe prompt is invalid or empty
400 Bad RequestINVALID_DURATIONDuration out of supported range (3-15s)
400 Bad RequestINVALID_MODELModel must be 'happyhorse-1.0/video'
401 UnauthorizedINVALID_API_KEYAPI key is missing or invalid
402 Payment RequiredINSUFFICIENT_CREDITSNot enough credits for this operation
429 Too Many RequestsRATE_LIMITEDToo many requests, please slow down
500 Internal Server ErrorINTERNAL_ERRORServer error, please try again later