Tutorial
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:
- Fazer upload (ou já ter hospedado) de 1 a 9 imagens de referência em URLs HTTPS acessíveis pela API.
- Enviar via POST para
https://api.hiapi.ai/v1/tasksuma tarefa que referencia essas URLs. - Quando a tarefa atingir o estado
success, ler o endereço final do MP4 emdata.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 comoAuthorization: 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_imagecomo array. Passar uma string diretamente retornareference_image: got string, want array. durationdeve ser um inteiro entre 3 e 15 (inclusive). Tanto999quanto1retornam mensagens de erro claras de limite.- Não existe
seed,negative_prompt,fps,cfg_scaleoustepsno 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
successefailcomo estados terminais — todos os outros (queued,processingetc.) 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:
- Processamento idempotente por
taskId. Tantosuccessquantofailsão estados terminais; as retentativas do HiAPI (ou duplicatas geradas pela sua própria camada de retentativa) reenviam o mesmotaskId. Deduplicate antes de disparar qualquer efeito colateral (cópia para S3, mensagem no Slack, evento de cobrança). - 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 emprocessingpor 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çalhoAuthorizationausente, malformado ou chave revogada. Emita novamente a chave pelo console em vez de corrigir o cabeçalho.400 INVALID_REQUESTcom caminho de campo — a mensagem de erro informa exatamente onde falhou, por exemploreference_image: maxItems: got 10, want 9ouaspect_ratio: value must be one of .... Valide a entrada antes do envio no worker, para falhar rapidamente localmente.task failed(terminal) — o polling/callback retornastatus: "fail", junto comerror.codeeerror.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 →-
Tutorial
Como chamar hailuo-2.3-fast/image-to-video pela API HiAPI: curl e Python na prática
A primeira requisição executável do nível rápido e econômico de geração de vídeo a partir de imagem — do ID do modelo ao download do MP4
-
Tutorial
Como chamar hailuo-2.3/text-to-video pela API HiAPI: curl, Python e exemplos de requisição executáveis
-
Tutorial
Usar Kandinsky 6.0 Pro text-to-video na HiAPI: tutorial de curl e Python
Demonstração prática com foco na requisição: montar a chamada de prompt para clipe de vídeo, enviar a requisição, consultar o status de conclusão e baixar o resultado