> ## Documentation Index
> Fetch the complete documentation index at: https://docs.apimart.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Generación de Video HappyHorse 1.0

>  - Modelo de generación de video HappyHorse 1.0 de Alibaba Cloud Bailian (entrada unificada, enrutamiento automático de modelo único)
- Enrutamiento automático por parámetros: T2V (solo prompt) / I2V (first_frame_image) / R2V (image_urls) / EDIT (video_url)
- Soporta resoluciones 720P/1080P y cualquier duración entera de 3 a 15 segundos
- Facturado únicamente por resolución × duración (segundos), independientemente de la capacidad 

<RequestExample>
  ```bash cURL theme={null}
  curl --request POST \
    --url https://api.apimart.ai/v1/videos/generations \
    --header 'Authorization: Bearer <token>' \
    --header 'Content-Type: application/json' \
    --data '{
      "model": "happyhorse-1.0",
      "prompt": "A little girl walking down the road, cinematic feel",
      "resolution": "1080P",
      "size": "16:9",
      "duration": 5,
      "seed": 42
    }'
  ```

  ```python Python theme={null}
  import requests

  url = "https://api.apimart.ai/v1/videos/generations"

  payload = {
      "model": "happyhorse-1.0",
      "prompt": "A little girl walking down the road, cinematic feel",
      "resolution": "1080P",
      "size": "16:9",
      "duration": 5,
      "seed": 42
  }

  headers = {
      "Authorization": "Bearer <token>",
      "Content-Type": "application/json"
  }

  response = requests.post(url, json=payload, headers=headers)

  print(response.json())
  ```

  ```javascript JavaScript theme={null}
  const url = "https://api.apimart.ai/v1/videos/generations";

  const payload = {
    model: "happyhorse-1.0",
    prompt: "A little girl walking down the road, cinematic feel",
    resolution: "1080P",
    size: "16:9",
    duration: 5,
    seed: 42
  };

  const headers = {
    "Authorization": "Bearer <token>",
    "Content-Type": "application/json"
  };

  fetch(url, {
    method: "POST",
    headers: headers,
    body: JSON.stringify(payload)
  })
    .then(response => response.json())
    .then(data => console.log(data))
    .catch(error => console.error('Error:', error));
  ```

  ```go Go theme={null}
  package main

  import (
      "bytes"
      "encoding/json"
      "fmt"
      "io/ioutil"
      "net/http"
  )

  func main() {
      url := "https://api.apimart.ai/v1/videos/generations"

      payload := map[string]interface{}{
          "model":      "happyhorse-1.0",
          "prompt":     "A little girl walking down the road, cinematic feel",
          "resolution": "1080P",
          "size":       "16:9",
          "duration":   5,
          "seed":       42,
      }

      jsonData, _ := json.Marshal(payload)

      req, _ := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
      req.Header.Set("Authorization", "Bearer <token>")
      req.Header.Set("Content-Type", "application/json")

      client := &http.Client{}
      resp, err := client.Do(req)
      if err != nil {
          panic(err)
      }
      defer resp.Body.Close()

      body, _ := ioutil.ReadAll(resp.Body)
      fmt.Println(string(body))
  }
  ```

  ```java Java theme={null}
  import java.net.http.HttpClient;
  import java.net.http.HttpRequest;
  import java.net.http.HttpResponse;
  import java.net.URI;

  public class Main {
      public static void main(String[] args) throws Exception {
          String url = "https://api.apimart.ai/v1/videos/generations";

          String payload = """
          {
            "model": "happyhorse-1.0",
            "prompt": "A little girl walking down the road, cinematic feel",
            "resolution": "1080P",
            "size": "16:9",
            "duration": 5,
            "seed": 42
          }
          """;

          HttpClient client = HttpClient.newHttpClient();
          HttpRequest request = HttpRequest.newBuilder()
              .uri(URI.create(url))
              .header("Authorization", "Bearer <token>")
              .header("Content-Type", "application/json")
              .POST(HttpRequest.BodyPublishers.ofString(payload))
              .build();

          HttpResponse<String> response = client.send(request,
              HttpResponse.BodyHandlers.ofString());

          System.out.println(response.body());
      }
  }
  ```

  ```php PHP theme={null}
  <?php

  $url = "https://api.apimart.ai/v1/videos/generations";

  $payload = [
      "model" => "happyhorse-1.0",
      "prompt" => "A little girl walking down the road, cinematic feel",
      "resolution" => "1080P",
      "size" => "16:9",
      "duration" => 5,
      "seed" => 42
  ];

  $ch = curl_init($url);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  curl_setopt($ch, CURLOPT_POST, true);
  curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
  curl_setopt($ch, CURLOPT_HTTPHEADER, [
      "Authorization: Bearer <token>",
      "Content-Type: application/json"
  ]);

  $response = curl_exec($ch);
  curl_close($ch);

  echo $response;
  ?>
  ```

  ```ruby Ruby theme={null}
  require 'net/http'
  require 'json'
  require 'uri'

  url = URI("https://api.apimart.ai/v1/videos/generations")

  payload = {
    model: "happyhorse-1.0",
    prompt: "A little girl walking down the road, cinematic feel",
    resolution: "1080P",
    size: "16:9",
    duration: 5,
    seed: 42
  }

  http = Net::HTTP.new(url.host, url.port)
  http.use_ssl = true

  request = Net::HTTP::Post.new(url)
  request["Authorization"] = "Bearer <token>"
  request["Content-Type"] = "application/json"
  request.body = payload.to_json

  response = http.request(request)
  puts response.body
  ```

  ```swift Swift theme={null}
  import Foundation

  let url = URL(string: "https://api.apimart.ai/v1/videos/generations")!

  let payload: [String: Any] = [
      "model": "happyhorse-1.0",
      "prompt": "A little girl walking down the road, cinematic feel",
      "resolution": "1080P",
      "size": "16:9",
      "duration": 5,
      "seed": 42
  ]

  var request = URLRequest(url: url)
  request.httpMethod = "POST"
  request.setValue("Bearer <token>", forHTTPHeaderField: "Authorization")
  request.setValue("application/json", forHTTPHeaderField: "Content-Type")
  request.httpBody = try? JSONSerialization.data(withJSONObject: payload)

  let task = URLSession.shared.dataTask(with: request) { data, response, error in
      if let error = error {
          print("Error: \(error)")
          return
      }

      if let data = data, let responseString = String(data: data, encoding: .utf8) {
          print(responseString)
      }
  }

  task.resume()
  ```

  ```csharp C# theme={null}
  using System;
  using System.Net.Http;
  using System.Text;
  using System.Threading.Tasks;

  class Program
  {
      static async Task Main(string[] args)
      {
          var url = "https://api.apimart.ai/v1/videos/generations";

          var payload = @"{
              ""model"": ""happyhorse-1.0"",
              ""prompt"": ""A little girl walking down the road, cinematic feel"",
              ""resolution"": ""1080P"",
              ""size"": ""16:9"",
              ""duration"": 5,
              ""seed"": 42
          }";

          using var client = new HttpClient();
          client.DefaultRequestHeaders.Add("Authorization", "Bearer <token>");

          var content = new StringContent(payload, Encoding.UTF8, "application/json");
          var response = await client.PostAsync(url, content);
          var result = await response.Content.ReadAsStringAsync();

          Console.WriteLine(result);
      }
  }
  ```
</RequestExample>

<ResponseExample>
  ```json 200 theme={null}
  {
    "code": 200,
    "data": [
      {
        "status": "submitted",
        "task_id": "task_01J9HA7JPQ9A0Z6JZ3V8M9W6PZ"
      }
    ]
  }
  ```

  ```json 400 theme={null}
  {
    "error": {
      "code": 400,
      "message": "Invalid request parameters",
      "type": "invalid_request_error"
    }
  }
  ```

  ```json 401 theme={null}
  {
    "error": {
      "code": 401,
      "message": "Invalid authentication credentials",
      "type": "authentication_error"
    }
  }
  ```

  ```json 402 theme={null}
  {
    "error": {
      "code": 402,
      "message": "Insufficient balance. Please top up your account",
      "type": "payment_required"
    }
  }
  ```

  ```json 429 theme={null}
  {
    "error": {
      "code": 429,
      "message": "Rate limit exceeded. Please try again later",
      "type": "rate_limit_error"
    }
  }
  ```

  ```json 500 theme={null}
  {
    "error": {
      "code": 500,
      "message": "Internal server error. Please try again later",
      "type": "server_error"
    }
  }
  ```
</ResponseExample>

## Autorización

<ParamField header="Authorization" type="string" required>
  Todos los endpoints de la API requieren autenticación mediante Bearer Token

  Obtenga su API Key:

  Visite la [página de gestión de API Keys](https://apimart.ai/keys) para obtener su API Key

  Añádala al encabezado de la solicitud:

  ```
  Authorization: Bearer YOUR_API_KEY
  ```
</ParamField>

## Enrutamiento de modos

`happyhorse-1.0` es la entrada unificada para Text-to-Video / Image-to-Video / Reference-Image-to-Video / Video Edit. El backend determina automáticamente el modo según los parámetros entrantes. **Todos los modos se facturan con la misma regla (solo resolución × segundos)**:

| Campos que envía                                                                                | Se enruta a                    | Descripción del modo                                       |
| ----------------------------------------------------------------------------------------------- | ------------------------------ | ---------------------------------------------------------- |
| Solo `prompt`                                                                                   | Text-to-Video (T2V)            | Genera video únicamente a partir de texto                  |
| `prompt` + `first_frame_image`                                                                  | Image-to-Video (I2V)           | Anima desde una imagen del primer fotograma                |
| `prompt` + `image_urls` (1–9 imágenes)                                                          | Reference-Image-to-Video (R2V) | Genera una nueva escena a partir de imágenes de referencia |
| `prompt` + `video_url` (opcional `image_urls` 0–5 como referencias de estilo / `audio_setting`) | Video Edit (EDIT)              | Reescribe / re-estiliza un video fuente                    |

**Prioridad de enrutamiento** (de alta a baja): `video_url` > `first_frame_image` > `image_urls` > solo `prompt`.

**Reglas de exclusión mutua**: los tres campos de medios (`first_frame_image` / `image_urls` / `video_url`) son **mutuamente excluyentes por pares**. La única combinación válida es `video_url + image_urls` (modo EDIT + imágenes de referencia). Pasar dos campos mutuamente excluyentes devuelve 400 `mixed_media_not_allowed`.

## Parámetros de la solicitud

<ParamField body="model" type="string" required>
  Nombre del modelo de generación de video, fijado como `happyhorse-1.0`
</ParamField>

<ParamField body="prompt" type="string">
  Descripción del contenido del video, hasta 2500 caracteres; no puede contener tokens especiales

  * **Modos T2V / R2V / EDIT**: obligatorio
  * **Modo I2V**: opcional, pero recomendado para guiar el movimiento de cámara y las acciones

  Ejemplo: `"A little girl walking down the road, cinematic feel"`
</ParamField>

<ParamField body="first_frame_image" type="string">
  Imagen del primer fotograma, activa **I2V** (Image-to-Video). Admite URL o base64 (`data:image/<mime>;base64,<payload>`, el gateway la sube automáticamente a OSS)

  Mutuamente excluyente con `image_urls` / `video_url`

  <Note>
    **Requisitos de la imagen del primer fotograma:**

    * Formato: JPEG / JPG / PNG / BMP / WEBP
    * Lado corto: ≥ 300px
    * Proporción de aspecto: `1:2.5` a `2.5:1`
    * Tamaño del archivo: ≤ 10MB
  </Note>
</ParamField>

<ParamField body="image_urls" type="array<string>">
  Array de imágenes:

  * **Modo R2V** (solo se proporciona `image_urls`): 1–9 imágenes, usadas como referencias de sujeto/estilo para generar una nueva escena
  * **Modo EDIT** (proporcionado junto con `video_url`): 0–5 imágenes, usadas como referencia de estilo

  Admite URL o base64

  Mutuamente excluyente con `first_frame_image`; puede combinarse con `video_url`

  <Note>
    **Requisitos de las imágenes de referencia:**

    * Formato: JPEG / JPG / PNG / BMP / WEBP
    * Lado corto: ≥ 720p recomendado
    * Proporción de aspecto: corto / largo ≥ 0.4
    * Tamaño del archivo: ≤ 10MB
    * Cantidad: R2V debe ser 1–9; EDIT hasta 5
  </Note>
</ParamField>

<ParamField body="video_url" type="string">
  URL del video fuente, activa **EDIT** (Video Edit). **No se admite Base64** — proporcione un enlace directo HTTP/HTTPS

  Mutuamente excluyente con `first_frame_image`; puede combinarse con `image_urls` (≤ 5)

  <Note>
    **Requisitos del video fuente:**

    * Duración: 3–60 segundos (> 15s será truncado automáticamente por el upstream de 0 a 15s)
    * Resolución: mínimo 480p, lado corto ≥ 360
    * Proporción de aspecto: `1:8` a `8:1`
    * Formato: MP4 / MOV (H.264 recomendado)
    * Tasa de fotogramas: > 8 fps
    * Tamaño del archivo: ≤ 100MB
  </Note>

  <Warning>
    **En modo EDIT, la duración del video generado coincide con la del video fuente** (limitada a los 15s truncados cuando la fuente es más larga). El parámetro `duration` no tiene efecto aquí. Para controlar la duración de salida, recorte el video fuente a la duración deseada antes de subirlo.
  </Warning>
</ParamField>

<ParamField body="audio_setting" type="string" default="auto">
  Configuración de audio, **solo efectiva en modo EDIT** (debe pasar `video_url`)

  Opciones:

  * `auto` - Genera audio automáticamente (por defecto)
  * `origin` - Mantiene la pista de audio del video fuente

  <Warning>
    Pasar este campo fuera del modo EDIT devuelve 400 `audio_setting_only_for_edit`
  </Warning>
</ParamField>

<ParamField body="resolution" type="string" default="1080P">
  Resolución del video (afecta a la facturación)

  Opciones:

  * `720P` - Estándar
  * `1080P` - Alta definición (por defecto)
</ParamField>

<ParamField body="duration" type="integer" default="5">
  Duración del video en segundos (afecta a la facturación)

  Rango soportado: cualquier entero de `3` a `15`

  Por defecto: `5`

  <Warning>
    **No tiene efecto en modo EDIT (cuando se proporciona `video_url`)**: la duración del video generado coincide con la del video fuente (facturada por los 15s truncados cuando la fuente es más larga que 15s). Para controlar la duración de salida, recorte primero el video fuente.
  </Warning>
</ParamField>

<ParamField body="size" type="string" default="16:9">
  Proporción de aspecto

  Formatos soportados:

  * `16:9` - Paisaje panorámico (por defecto)
  * `9:16` - Retrato
  * `1:1` - Cuadrado
  * `4:3` - Paisaje
  * `3:4` - Retrato

  <Warning>
    **Ignorado en modos I2V / EDIT** — la proporción de aspecto de salida se determina automáticamente por el medio de entrada (imagen del primer fotograma / video fuente)
  </Warning>
</ParamField>

<ParamField body="watermark" type="boolean" default="false">
  Si se debe añadir una marca de agua al video generado

  * `true`: añadir marca de agua
  * `false`: no añadir marca de agua (por defecto)
</ParamField>

<ParamField body="seed" type="integer">
  Semilla aleatoria usada para controlar la aleatoriedad del contenido generado

  Rango de valores: `[0, 2147483647]`. Si se omite, se usa una semilla aleatoria.

  <Note>
    * Para solicitudes idénticas, el modelo genera resultados diferentes al recibir valores de seed diferentes (por ejemplo, al omitir el seed)
    * Para solicitudes idénticas, el modelo genera resultados similares al recibir el mismo valor de seed, pero no se garantiza consistencia exacta
  </Note>
</ParamField>

## Respuesta

<ResponseField name="code" type="integer">
  Código de estado de la respuesta, 200 en caso de éxito
</ResponseField>

<ResponseField name="data" type="array">
  Array de datos de la respuesta

  <Expandable title="Elementos del array">
    <ResponseField name="status" type="string">
      Estado de la tarea, `submitted` al enviarse inicialmente
    </ResponseField>

    <ResponseField name="task_id" type="string">
      Identificador único de la tarea para consultar el estado y los resultados
    </ResponseField>
  </Expandable>
</ResponseField>

## Casos de uso

### Caso 1: Texto a video T2V (Solicitud más simple)

```json theme={null}
{
  "model": "happyhorse-1.0",
  "prompt": "A little girl walking down the road, cinematic feel"
}
```

### Caso 2: Texto a video T2V (Parámetros completos)

```json theme={null}
{
  "model": "happyhorse-1.0",
  "prompt": "A coastal road at sunset, slow-motion camera push-in, cinematic feel",
  "resolution": "1080P",
  "size": "16:9",
  "duration": 8,
  "seed": 42
}
```

### Caso 3: Imagen a video I2V (first\_frame\_image)

```json theme={null}
{
  "model": "happyhorse-1.0",
  "prompt": "Bring the scene in the image to life",
  "first_frame_image": "https://example.com/first_frame.png",
  "resolution": "1080P",
  "duration": 5
}
```

### Caso 4: Referencia-Imagen a video R2V (múltiples referencias)

```json theme={null}
{
  "model": "happyhorse-1.0",
  "prompt": "The protagonist from image 1 runs through the scene from image 2, then picks up the prop from image 3. Keep a 3D cartoon style with smooth motion.",
  "image_urls": [
    "https://example.com/img_01.jpg",
    "https://example.com/img_02.png",
    "https://example.com/img_03.jpeg"
  ],
  "resolution": "1080P",
  "size": "16:9",
  "duration": 5
}
```

### Caso 5: Video Edit EDIT (mantener audio original + referencia de estilo)

```json theme={null}
{
  "model": "happyhorse-1.0",
  "prompt": "Convert the character in the video to a cartoon style, preserving the original motion",
  "video_url": "https://example.com/source.mp4",
  "image_urls": [
    "https://example.com/style_ref.jpg"
  ],
  "resolution": "1080P",
  "audio_setting": "origin",
  "seed": 42
}
```

### Caso 6: 720P para ahorrar costos

```json theme={null}
{
  "model": "happyhorse-1.0",
  "prompt": "Waves crashing on the beach at sunset",
  "resolution": "720P",
  "size": "16:9",
  "duration": 5
}
```

## Guía de selección de modo

| Requisito                                                                  | Enfoque recomendado                                                                                |
| -------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
| Generar video solo a partir de texto                                       | Pasar solo `prompt` (T2V)                                                                          |
| Hacer que una imagen "cobre vida" (usarla como primer fotograma)           | Pasar `first_frame_image` (I2V)                                                                    |
| Generar una nueva escena a partir de un conjunto de imágenes de referencia | Pasar `image_urls` (1–9, R2V)                                                                      |
| Reescribir / re-estilizar un video existente                               | Pasar `video_url` (EDIT), opcionalmente combinar con `image_urls` (0–5) como referencias de estilo |
| Ahorrar costos                                                             | Usar `resolution: "720P"`                                                                          |

## Consejos de uso

1. **Lógica de entrada unificada**: los campos de entrada determinan el modo. Tenga en cuenta que los tres campos de medios (`first_frame_image` / `image_urls` / `video_url`) son mutuamente excluyentes por pares
2. **`size` solo efectivo en T2V/R2V**: en los modos I2V / EDIT, `size` se ignora — la proporción de aspecto de salida está determinada por el medio de entrada
3. **Duración**: de 5 a 10 segundos es el punto óptimo. Demasiado corta causa movimiento entrecortado; demasiado larga aumenta significativamente el tiempo de procesamiento upstream
4. **Calidad de la imagen del primer fotograma**: clara, bien compuesta, sujeto centrado — mejora significativamente la salida I2V
5. **Redacción del prompt**: describa movimiento / cámara / atmósfera (por ejemplo, "slow push-in, cinematic, warm tones") para obtener mejores resultados que con descripciones puramente estáticas de la escena
6. **Video de entrada EDIT**: > 15 segundos será truncado automáticamente por el upstream de 0 a 15s. Si necesita otros segmentos, recorte el video usted mismo primero

<Note>
  **Consultar resultados de la tarea**

  La generación de video es una tarea asíncrona que devuelve un `task_id` al enviarse. Use el endpoint [Obtener estado de la tarea](/es/api-reference/tasks/status) para consultar el progreso y los resultados de la generación.
</Note>
