HiAPI
Blogue

Tutorial

API de geração de vídeo a partir de referências HappyHorse 1.1: tutorial de curl + Python prontos para executar

Jun 26, 2026
API de geração de vídeo a partir de referências HappyHorse 1.1: tutorial de curl + Python prontos para executar

O HappyHorse 1.1 R2V converte até nove imagens de referência mais um prompt de texto em um vídeo curto, mantendo consistência de sujeito, vestuário e estilo de cena entre vários planos. No HiAPI, esse modelo é disponibilizado por meio de um endpoint unificado de tarefas assíncronas, com o ID de modelo happyhorse-1.1/reference-to-video. Este tutorial apresenta primeiro a requisição mínima que funciona e depois mostra a versão de nível de produção com callback.

Objetivo e pré-requisitos

Você realizará as seguintes operações:

  1. Fazer upload (ou já ter hospedado) de 1 a 9 imagens de referência em URLs HTTPS acessíveis pela API.
  2. Enviar via POST para https://api.hiapi.ai/v1/tasks uma tarefa que referencia essas URLs.
  3. Quando a tarefa atingir o estado success, ler o endereço final do MP4 em data.output[0].url.

Pré-requisitos:

  • Uma conta HiAPI e uma API Key que começa com sk-. Você pode obtê-la no console do HiAPI e consultar a documentação de autenticação para saber as regras de cabeçalho da requisição. A chave deve ser passada como Authorization: Bearer sk-... e nunca deve ficar no código do cliente.
  • Requisitos de hospedagem das imagens: cada imagem de referência deve estar em formato JPEG, PNG ou WEBP, com o lado mais curto ≥ 400px e ≤ 20MB. URLs de CDN pública são aceitas; URLs assinadas do S3 também são aceitas, desde que o executor da tarefa consiga acessá-las.

Requisição mínima funcional (curl)

Este é o menor corpo de requisição que o schema aceita. Ele usa a saída padrão de 1080p / 16:9 / 5 segundos, então você só precisa definir os campos prompt e reference_image.

curl -X POST https://api.hiapi.ai/v1/tasks \
  -H "Authorization: Bearer sk-YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "happyhorse-1.1/reference-to-video",
    "input": {
      "prompt": "O personagem da [Image 1] caminha em uma cena no estilo da [Image 2], filmagem cinematográfica",
      "reference_image": [
        "https://static.hiapi.ai/example/ref-1.jpg",
        "https://static.hiapi.ai/example/ref-2.jpg"
      ]
    }
  }'

A resposta é o envelope da tarefa; o único campo que você realmente precisa é data.taskId:

{
  "code": 200,
  "data": { "taskId": "task_xxx", "status": "queued" }
}

Faça polling até o estado terminal:

curl https://api.hiapi.ai/v1/tasks/task_xxx \
  -H "Authorization: Bearer sk-YOUR_KEY"

Quando data.status mudar para success, a URL do vídeo estará em data.output[0].url. Essa URL tem um timestamp expireAt, então baixe o arquivo imediatamente ou transfira-o para um armazenamento persistente — não faça hotlink direto desse endereço no seu site.

Parâmetros

O endpoint R2V valida a entrada estritamente; campos desconhecidos retornam 400 INVALID_REQUEST. Schema completo (validado em tempo real contra POST /v1/tasks):

Campo Tipo Obrigatório Valores permitidos Valor padrão
model string Sim happyhorse-1.1/reference-to-video (valor fixo) -
input.prompt string Sim Use [Image 1], [Image 2]…… para referenciar as imagens de referência, na mesma ordem de reference_image -
input.reference_image string[] Sim 1 a 9 URLs HTTPS, formato JPEG/PNG/WEBP, lado mais curto ≥ 400px, ≤ 20MB cada -
input.resolution enum Não 720p, 1080p 1080p
input.aspect_ratio enum Não 16:9, 9:16, 3:4, 4:3, 4:5, 5:4, 1:1, 9:21, 21:9 16:9
input.duration integer Não 3 a 15 (segundos) 5
callback.url string Não URL HTTPS que recebe o evento de estado terminal da tarefa -
callback.when enum Não final (atualmente o único valor) final

Algumas regras de validação pouco intuitivas que vale conhecer antes que causem problemas:

  • Mesmo com apenas uma URL de imagem de referência, passe reference_image como array. Passar uma string diretamente retorna reference_image: got string, want array.
  • duration deve ser um inteiro entre 3 e 15 (inclusive). Tanto 999 quanto 1 retornam mensagens de erro claras de limite.
  • Não existe seed, negative_prompt, fps, cfg_scale ou steps no nível superior. A API rejeita propriedades desconhecidas.

Para o preço completo por combinação de resolução e duração, consulte a página de preços do HiAPI — o custo varia tanto com resolution quanto com duration.

Python: ponta a ponta + polling

O bloco de código abaixo é um gerador autocontido que envia a tarefa, faz polling até o estado terminal e baixa o vídeo. Ele é deliberadamente sem dependências (usa apenas requests), para que você possa copiá-lo para qualquer serviço. Para uma descrição mais completa do ciclo de vida das requisições, consulte o guia de início rápido.

import os
import time
import requests

API = "https://api.hiapi.ai/v1/tasks"
HEADERS = {"Authorization": f"Bearer {os.environ['HIAPI_API_KEY']}"}


def submit_r2v(prompt: str, reference_images: list[str], duration: int = 5,
               resolution: str = "1080p", aspect_ratio: str = "16:9") -> str:
    if not 1 <= len(reference_images) <= 9:
        raise ValueError("reference_image must contain 1-9 URLs")
    payload = {
        "model": "happyhorse-1.1/reference-to-video",
        "input": {
            "prompt": prompt,
            "reference_image": reference_images,
            "resolution": resolution,
            "aspect_ratio": aspect_ratio,
            "duration": duration,
        },
    }
    r = requests.post(API, json=payload, headers=HEADERS, timeout=60)
    r.raise_for_status()
    data = r.json()
    if data.get("code") != 200:
        raise RuntimeError(f"create failed: {data}")
    return data["data"]["taskId"]


def wait_for_video(task_id: str, timeout_s: int = 600, poll_s: int = 5) -> str:
    deadline = time.time() + timeout_s
    while time.time() < deadline:
        r = requests.get(f"{API}/{task_id}", headers=HEADERS, timeout=30)
        r.raise_for_status()
        task = r.json()["data"]
        status = task["status"]
        if status == "success":
            return task["output"][0]["url"]
        if status == "fail":
            err = task.get("error") or {}
            raise RuntimeError(f"task failed: {err.get('code')} {err.get('message')}")
        time.sleep(poll_s)
    raise TimeoutError(f"task {task_id} did not finish in {timeout_s}s")


def download(url: str, path: str) -> None:
    with requests.get(url, stream=True, timeout=300) as r:
        r.raise_for_status()
        with open(path, "wb") as f:
            for chunk in r.iter_content(1 << 16):
                f.write(chunk)


if __name__ == "__main__":
    task_id = submit_r2v(
        prompt="Mantenha o design do sujeito da [Image 1], caminhando na atmosfera da [Image 2]",
        reference_images=[
            "https://static.hiapi.ai/example/char.jpg",
            "https://static.hiapi.ai/example/mood.jpg",
        ],
        duration=5,
        resolution="720p",
        aspect_ratio="9:16",
    )
    print("submitted", task_id)
    video_url = wait_for_video(task_id)
    print("ready", video_url)
    download(video_url, "out.mp4")

Este código faz deliberadamente o seguinte:

  • Lê a chave da variável de ambiente HIAPI_API_KEY, para que ela nunca entre no git nem nos logs do contêiner.
  • Usa r.raise_for_status() para capturar erros de transporte antes de analisar o JSON.
  • Considera apenas success e fail como estados terminais — todos os outros (queued, processing etc.) mantêm o loop em execução.
  • Baixa imediatamente a URL de saída. As URLs de saída são endereços assinados e expiram; se você precisar do arquivo mais tarde, transfira-o para armazenamento persistente ou copie-o para o seu próprio bucket.

Produção: migrar do polling para webhook

Fazer polling a cada 5 segundos é aceitável para um notebook ou para um worker de baixo tráfego. Para um serviço real, registre um callback ao criar a tarefa e deixe o HiAPI enviar o evento de estado terminal ao seu endpoint — assim seu worker pode ficar ocioso e você deixa de pagar por requisições GET vazias. Para o schema completo de requisição, consulte Criar tarefa, e para o polling de fallback, consulte Obter detalhes da tarefa.

payload = {
    "model": "happyhorse-1.1/reference-to-video",
    "input": {
        "prompt": "O personagem da [Image 1] caminha pela rua da cidade da [Image 2]",
        "reference_image": [
            "https://cdn.example.com/char.jpg",
            "https://cdn.example.com/street.jpg",
        ],
        "resolution": "1080p",
        "aspect_ratio": "16:9",
        "duration": 8,
    },
    "callback": {
        "url": "https://your-domain.com/hiapi/callback",
        "when": "final",
    },
}

O endpoint de recebimento precisa de duas coisas:

  1. Processamento idempotente por taskId. Tanto success quanto fail são estados terminais; as retentativas do HiAPI (ou duplicatas geradas pela sua própria camada de retentativa) reenviam o mesmo taskId. Deduplicate antes de disparar qualquer efeito colateral (cópia para S3, mensagem no Slack, evento de cobrança).
  2. Polling de verificação de fallback. Se o seu endpoint cair, o callback será perdido. Mantenha uma tarefa leve de verificação que chame GET /v1/tasks/<id> para todas as tarefas que estejam em processing por mais tempo que o pior caso de geração.

Erros que você realmente encontrará

Vale tratar explicitamente os seguintes; os demais são 5xx temporários, que devem ser retentados com backoff pelo SDK ou pelo seu cliente HTTP.

  • 401 permission_denied — cabeçalho Authorization ausente, malformado ou chave revogada. Emita novamente a chave pelo console em vez de corrigir o cabeçalho.
  • 400 INVALID_REQUEST com caminho de campo — a mensagem de erro informa exatamente onde falhou, por exemplo reference_image: maxItems: got 10, want 9 ou aspect_ratio: value must be one of .... Valide a entrada antes do envio no worker, para falhar rapidamente localmente.
  • task failed (terminal) — o polling/callback retorna status: "fail", junto com error.code e error.message. Causas comuns: URL de imagem de referência inacessível ao worker, moderação NSFW acionada ou lado mais curto da imagem abaixo do limite de 400px.

Perguntas frequentes

É possível referenciar mais de 9 imagens? Não. O schema limita reference_image a no máximo 9. Se precisar de mais variações, combine fora da plataforma uma imagem de referência de maior densidade (uma grade ou prancha de storyboard) e use-a como entrada única.

É obrigatório usar marcadores [Image N] no prompt? Esses marcadores são a forma de apontar para uma imagem de referência específica. Se o prompt nunca indicar imagens, também é válido, mas o modelo misturará livremente todas as imagens de referência — o que normalmente não é o que você quer em cenários de consistência entre vários planos.

Por que recebo 503 nsfw_moderation_unavailable? Trata-se de um erro temporário da plataforma causado pelo serviço de moderação, não relacionado à sua requisição. Tente novamente após um breve backoff.

Devo usar 720p ou 1080p? 1080p é o padrão e tem melhor qualidade, mas custa mais e renderiza mais devagar. Para cortes verticais para redes sociais, em que o player já faz downsampling, 720p com aspect_ratio: 9:16 costuma ser o equilíbrio mais adequado.

É possível passar seed para tornar a saída reprodutível? Atualmente não. A API trata seed como propriedade desconhecida e a rejeita. Se precisar de reprodutibilidade, fixe exatamente prompt, reference_image, aspect_ratio, resolution e duration, e aceite alguma variação entre execuções.

Onde ficam armazenadas as URLs de saída e por quanto tempo valem? São URLs assinadas com TTL curto; a resposta inclui expireAt. Após o sucesso da tarefa, baixe imediatamente ou transfira para armazenamento persistente.

Artigos relacionados

Ver todos os artigos →

Comece a criar com artefactos de IA persistentes

Obtenha a sua chave de API e chame modelos de imagem, vídeo e áudio através de uma única API baseada em tasks.

Ler a documentação da API