Tutorial
Como usar a API hailuo 2.3 image-to-video: curl, Python e um exemplo de requisição executável
Resumo
- O que é isto: uma receita executável para usar a API hailuo-2.3/image-to-video na hiapi — transformar uma imagem estática em um clipe curto de vídeo: criar a tarefa, fazer polling da tarefa, baixar o
.mp4. - Endpoint:
POST https://api.hiapi.ai/v1/tasks, com"model": "hailuo-2.3/image-to-video"no corpo da requisição, depois faça polling comGET /v1/tasks/<taskId>até a tarefa atingir um estado terminal. - Dois campos de entrada obrigatórios:
prompteimage_url— uma única URL pública apontando para o seu primeiro frame (não um array). durationé uma string, não um número:"6"ou"10". Enviar o inteiro6é rejeitado com 400.- O schema é estrito. Não existem os parâmetros
motion_strength,camera_movementouresolution— enviá-los é rejeitado com 400. O movimento é controlado inteiramente pelo prompt.
O que você vai construir e o que precisa ter pronto
Você tem uma imagem estática — foto de produto, render de personagem, fotografia — e quer que o hailuo-2.3 a anime, gerando um clipe de vídeo que começa exatamente neste frame. O hailuo-2.3 é conhecido por movimentos fisicamente convincentes (tecidos, cabelo, mecânica corporal), o que o torna ideal para dar vida a imagens estáticas.
Você precisa de duas coisas:
- Uma API key da hiapi. Crie uma no dashboard. A key tem o formato
sk-...e vai no cabeçalhoAuthorization: Bearer. - Uma URL de imagem do primeiro frame acessível publicamente. A API busca a imagem a partir de
image_url, portanto ela precisa estar acessível sem autenticação — links de S3/R2/CDN funcionam; caminhoslocalhostou URLs assinadas expiradas não funcionam.
Como todo modelo de geração na hiapi, este modelo usa a interface de tarefas assíncrona: você faz um POST de uma tarefa, recebe imediatamente um taskId e depois obtém o resultado por polling ou callback. Não há endpoint síncrono para vídeo — a renderização do clipe leva tempo e ultrapassaria o timeout típico de HTTP.
Schema da requisição
O schema de entrada completo de hailuo-2.3/image-to-video — verificado contra a API em produção:
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
prompt |
string | ✅ | Descreva o movimento, não a cena — a cena vem da sua imagem. |
image_url |
string | ✅ | Uma URL pública, ou seja, o primeiro frame. É singular — não image_urls. |
duration |
string | Opcional | "6" ou "10" (segundos). Precisa ser string. |
prompt_optimizer |
boolean | Opcional | Permite que a plataforma expanda seu prompt antes da geração. |
O schema é estrito: qualquer coisa que não esteja nesta tabela é rejeitada. Envie motion_strength, camera_movement ou resolution e você receberá exatamente isto:
{
"code": 400,
"error_code": "INVALID_REQUEST",
"message": "invalid input: <root>: additional properties 'resolution', 'camera_movement', 'motion_strength' not allowed"
}
Duas consequências valem ser lembradas:
- Não há botão de ajuste de movimento. A física de movimento do hailuo-2.3 é guiada apenas pelo prompt — veja a seção de escrita de prompts abaixo.
- Não há controle de resolução ou proporção de aspecto neste endpoint. Não construa UI para parâmetros que a API vai rejeitar com 400.
Exemplo mínimo executável: curl
Criar a tarefa:
curl -X POST https://api.hiapi.ai/v1/tasks \
-H "Authorization: Bearer sk-<your-key>" \
-H "Content-Type: application/json" \
-d '{
"model": "hailuo-2.3/image-to-video",
"input": {
"prompt": "The woman turns toward the camera and smiles; wind gently moves her hair; slow cinematic push-in",
"image_url": "https://your-cdn.example.com/first-frame.jpg",
"duration": "6",
"prompt_optimizer": true
}
}'
A resposta (trecho) contém o id da tarefa em data.taskId:
{ "data": { "taskId": "..." } }
Faça polling:
curl -s https://api.hiapi.ai/v1/tasks/<taskId> \
-H "Authorization: Bearer sk-<your-key>"
Durante a renderização do clipe, data.status reporta um estado não terminal. Quando ele mudar para "success", a URL do vídeo estará em data.output[0].url; se for "fail", data.error.code e data.error.message explicarão o motivo.
Baixe o arquivo imediatamente após o sucesso da tarefa. A URL de saída é assinada e tem data de expiração (expireAt) — armazene os bytes no seu próprio storage e nunca faça hotlink da URL da tarefa.
Exemplo mínimo executável: Python
Script completo com o mesmo fluxo — criar, fazer polling, baixar:
import time
import requests
API_BASE = "https://api.hiapi.ai/v1/tasks"
API_KEY = "sk-<your-key>"
HEADERS = {"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"}
def create_task() -> str:
payload = {
"model": "hailuo-2.3/image-to-video",
"input": {
"prompt": (
"The cat stretches, hops off the windowsill, and walks toward "
"the camera; soft afternoon light, subtle handheld camera sway"
),
"image_url": "https://your-cdn.example.com/first-frame.jpg",
"duration": "6", # string, not int: "6" or "10"
"prompt_optimizer": True,
},
}
r = requests.post(API_BASE, json=payload, headers=HEADERS, timeout=30)
task_id = (r.json().get("data") or {}).get("taskId")
if not task_id:
raise RuntimeError(f"create failed ({r.status_code}): {r.text[:300]}")
return task_id
def wait_task(task_id: str, timeout_s: int = 900, poll_s: int = 10) -> dict:
deadline = time.time() + timeout_s
while time.time() < deadline:
r = requests.get(f"{API_BASE}/{task_id}", headers=HEADERS, timeout=30)
task = r.json().get("data") or {}
status = task.get("status")
if status == "success":
return task
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} not finished after {timeout_s}s")
def main() -> None:
task_id = create_task()
print("task created:", task_id)
task = wait_task(task_id)
url = task["output"][0]["url"]
# The URL is signed and expires -- download immediately, store the bytes yourself.
video = requests.get(url, timeout=120).content
with open("hailuo-output.mp4", "wb") as f:
f.write(video)
print("saved hailuo-output.mp4")
if __name__ == "__main__":
main()
Tarefas de vídeo demoram bem mais que tarefas de imagem, então o script faz polling a cada 10 segundos e define um limite folgado de 15 minutos. Não faça polling com mais agressividade que a cada ~5 segundos — isso não deixa a renderização mais rápida.
Escrevendo prompts que controlam o movimento
Como não há nenhum parâmetro de movimento, o prompt é o seu único volante — e, para o hailuo-2.3, isso é suficiente. Três regras práticas:
- Descreva o movimento, não a aparência. O modelo já viu sua imagem.
"a beautiful woman in a red dress"desperdiça o prompt;"she turns her head slowly to the left as the camera pushes in"faz bom uso dele. - Descreva o movimento de câmera em linguagem simples.
"slow push-in","orbit right","handheld sway","static camera"— isso deve estar no texto do prompt, não em um campo (inexistente)camera_movement. - Na dúvida, deixe o
prompt_optimizerajudar. Com"prompt_optimizer": true, a plataforma expande prompts curtos antes da geração — um bom padrão para entradas curtas. Desligue-o quando quiser que uma formulação cuidadosamente elaborada seja seguida à risca.
Considerações para produção
Prefira callbacks ao polling. Anexe um callback ao criar a tarefa e a hiapi fará um POST do resultado terminal para o seu endpoint, assim você não precisa manter um loop aberto:
{
"model": "hailuo-2.3/image-to-video",
"input": { "...": "..." },
"callback": { "url": "https://your-app.example.com/hooks/hiapi", "when": "final" }
}
"when": "final" dispara uma única vez no estado terminal. Polling serve para scripts e notebooks; callbacks são a forma correta para servidores — a renderização de vídeo demora o suficiente para que um worker bloqueado em polling seja um custo real.
Trate explicitamente estes erros:
401com"code": "permission_denied"— há um problema com a key, ou essa key não tem permissão para usar este modelo. A mensagem contém umrequest_idque pode ser enviado ao suporte.400 INVALID_REQUEST— violação de schema. A mensagem é precisa, por exemploduration: got number, want stringouimage_url: missing required field "image_url". Registre-a exatamente como veio.402ao criar a tarefa — saldo insuficiente. Note que vídeo custa mais por tarefa que imagem, então uma key que ainda executa tarefas de imagem normalmente pode começar a falhar ao criar vídeos. Recarregue ou verifique o uso no dashboard.- A tarefa entra em
"fail"— falha no lado da geração; verifiquedata.error. Uma causa comum específica de image-to-video: não foi possível buscar aimage_url(bucket privado, URL assinada expirada).
Seja idempotente em relação a retentativas. Se seu processo morrer entre a criação e o download, uma reinicialização ingênua criará (e cobrará) uma segunda renderização. Persista o taskId imediatamente após o retorno de create_task e, ao reiniciar, continue o polling em vez de criar de novo.
Leituras relacionadas
- Página do modelo hailuo-2.3/image-to-video — playground, cobrança por segundo e schema em tempo real.
- hailuo-2.3-fast/image-to-video — tier de baixa latência, mesmo fluxo de tarefas.
- hailuo-2.3/text-to-video — sem imagem de origem, geração apenas a partir do prompt.
- Documentação da hiapi — explicação completa da interface de tarefas, incluindo callbacks.
- Preços — taxas atuais de vídeo por segundo para todos os modelos.
Perguntas frequentes
Quais durações o hailuo-2.3 image-to-video suporta?
Duas: "6" e "10" segundos, passadas como string. Qualquer outro valor — ou o mesmo valor escrito como número — retorna 400 INVALID_REQUEST.
Por que "duration": 6 falha, mas "duration": "6" funciona?
O schema define duration como um enum de strings. A API informa claramente: duration: got number, want string. Este é o 400 mais comum neste endpoint.
Posso definir a resolução ou a proporção de aspecto da saída?
Não. O endpoint não expõe os campos resolution ou aspect_ratio, e enviar um deles resulta em 400 — o schema rejeita propriedades desconhecidas em vez de ignorá-las.
Como controlar o movimento de câmera ou a intensidade do movimento?
Pelo prompt. Não há parâmetros camera_movement ou motion_strength — escreva instruções de câmera e movimento em linguagem simples ("slow push-in", "she walks toward the camera").
Posso enviar várias imagens de referência?
Não — image_url é uma única string, ou seja, o primeiro frame. Isso difere de alguns outros modelos na hiapi que recebem um array image_urls; o schema é definido por modelo, então sempre consulte a página do modelo.
Quais formatos de imagem o primeiro frame suporta? Formatos Web padrão (JPEG/PNG), servidos a partir de uma URL publicamente acessível. Se a plataforma não conseguir buscar a URL, a tarefa falhará após a criação — então valide que o link é publicamente acessível antes de enviar.
Qual é a diferença em relação ao hailuo-2.3/text-to-video?
O text-to-video inventa a cena apenas a partir do seu prompt; o image-to-video fixa o primeiro frame na sua imagem e anima a partir dele. O ciclo de vida da tarefa é idêntico — apenas o schema de entrada difere (aqui image_url é obrigatório, enquanto o text-to-video não tem campo de imagem).
Quanto custa? Vídeos são cobrados por segundo de saída, e as taxas mudam — consulte a página do modelo hailuo-2.3/image-to-video e a página de preços para os números atuais.
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