Tutorial
Tutorial de kandinsky-6.0-pro/image-to-video na HiAPI: curl + Python
Faça uma requisição de imagem para vídeo funcional na HiAPI: do upload do primeiro quadro ao polling assíncrono e ao download final
Visão geral do fluxo image-to-video na HiAPI
Cada chamada de imagem para vídeo na HiAPI segue o mesmo ciclo de vida em três fases. Entender isso desde o início faz o restante do tutorial parecer passo a passo, em vez de algo misterioso.
Fase 1: Enviar a tarefa
Você envia uma requisição POST para o endpoint kandinsky-6.0-pro/image-to-video, anexando a imagem do primeiro quadro e um prompt curto que descreve o movimento esperado. A resposta não retorna o fluxo de bytes do vídeo; em vez disso, aceita a tarefa e retorna um identificador de tarefa — todas as operações seguintes dependem desse identificador.
Fase 2: Consultar o status (polling)
A geração de vídeo leva tempo, então o endpoint é assíncrono. Você chama a rota de status com o identificador da tarefa, e ela informa o estado atual da tarefa: na fila, em processamento, sucesso ou falha. Você repete a verificação em intervalos curtos até o status atingir um estado terminal.
Fase 3: Obter a saída
Assim que a tarefa reporta sucesso, o payload de status traz a localização do vídeo finalizado. Você o baixa e grava em disco. Esse é todo o contrato.
Por que esse formato importa
A separação entre envio e polling não é acidental. Isso significa que seu cliente HTTP não precisa manter o socket aberto durante toda a renderização, e também que você pode persistir o identificador da tarefa, sair e retomar depois. Em caso de erro, o campo de status informa onde ocorreu o erro, o que é bem mais útil do que um timeout opaco.
Memorize as três fases: enviar, consultar, baixar. Os exemplos em curl e Python abaixo são apenas duas formas de escrever a mesma sequência.
Preparar o primeiro quadro e as credenciais da API
Na maioria das primeiras chamadas, só há dois pontos de falha: a chave e a imagem. Resolva ambos agora e o restante do tutorial fluirá bem.
Definir a chave de API como variável de ambiente
Não cole a chave em arquivos de código-fonte. Exporte-a no shell para que curl e Python possam obtê-la sem codificar nada diretamente:
export HIAPI_API_KEY="your_api_key_here"
No Windows PowerShell, o equivalente é $env:HIAPI_API_KEY="your_api_key_here". Antes de continuar, confirme que o shell consegue lê-la:
echo "$HIAPI_API_KEY" | head -c 4; echo
Você deve ver os primeiros caracteres da chave. Se não vir nada, a variável não foi persistida na sessão atual do shell — uma causa comum de respostas 401 mais adiante.
Escolher uma imagem de primeiro quadro válida
O primeiro quadro determina o sujeito, a composição e a proporção de aspecto de todo o vídeo. Algumas restrições práticas evitam a maioria das rejeições:
- Formato: use formatos de imagem web padrão, como JPEG ou PNG. Evite codecs obscuros, casos extremos de progressivo ou perfis de cor CMYK.
- Acessibilidade: a API precisa realmente ler esses bytes. Se você passar uma URL, garanta que ela seja acessível publicamente ou esteja devidamente autenticada; se passar dados inline, garanta que a codificação esteja completa.
- Tamanho: imagens de tamanho razoável são validadas mais rápido. Arquivos muito grandes atrasam a requisição e frequentemente são a origem de erros de upload.
- Conteúdo: escolha um quadro que faça sentido como ponto de partida do movimento — sujeito nítido e fundo limpo. O prompt de movimento descreve como sair desse quadro, então quadros confusos geram vídeos confusos.
Decidir como referenciar a imagem
O corpo da requisição permite apontar para o primeiro quadro via URL ou dados codificados inline. Escolha conforme onde a imagem está:
- Se o arquivo já está hospedado e pode ser lido publicamente, URL é a opção mais limpa.
- Se a imagem é local ou privada, a codificação inline torna a requisição inteira autocontida, ao custo de um payload maior.
De qualquer forma, mantenha a referência de imagem no mesmo formato de variável em curl e Python, para que os dois exemplos permaneçam diretamente comparáveis.
Fazer a requisição com curl
Esta é uma requisição de envio completa. Substitua pela sua própria referência de imagem e prompt e execute.
curl -sS -X POST "https://api.hiapi.ai/v1/kandinsky-6.0-pro/image-to-video" \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"image": "https://your-host.example/first-frame.jpg",
"prompt": "slow dolly-in on the subject, gentle ambient motion, stable camera",
"duration": 5,
"aspect_ratio": "16:9"
}'
O papel de cada parte
- URL aponta especificamente para o modelo
kandinsky-6.0-pro/image-to-video. A escolha do modelo está no caminho, não no corpo da requisição, então um erro de digitação aqui gera 404, e não uma mensagem de validação amigável. Authorizationcarrega o token bearer da variável de ambiente exportada. Nada mais autentica a chamada.Content-Type: application/jsoné importante para qualquer POST com corpo JSON; se omiti-lo, alguns proxies podem corromper o payload antes de ele chegar à API.imageé a referência do seu primeiro quadro — aqui na forma de URL, ou como string codificada inline se a imagem for privada.promptdescreve o movimento. Mantenha-o sobre movimento e comportamento de câmera, não repetindo o que a imagem já contém.durationeaspect_ratiomoldam a saída. A proporção de aspecto deve corresponder ao primeiro quadro; incompatibilidades causam recorte ou barras pretas. A cobrança é medida pela saída gerada, então essas configurações também determinam seu custo.
Ler a resposta
Um envio bem-sucedido retorna o payload da tarefa, não o vídeo. Capture o identificador da tarefa — você o passará ao endpoint de status depois:
curl -sS -X POST "https://api.hiapi.ai/v1/kandinsky-6.0-pro/image-to-video" \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-d @body.json | jq -r '.id'
Usar um arquivo body.json em vez de -d inline mantém prompts longos e imagens codificadas legíveis. O jq é opcional, mas transforma a resposta em uma linha pronta para colar na próxima chamada.
Chamar o mesmo endpoint com Python
A versão em Python é a mesma requisição, o mesmo corpo, apenas encapsulada em funções para reutilização. Mantenha o corpo idêntico ao da versão curl — esse é justamente o sentido de mostrar os dois.
import os
import json
import requests
API_BASE = "https://api.hiapi.ai/v1"
ENDPOINT = f"{API_BASE}/kandinsky-6.0-pro/image-to-video"
def submit_video_job(image_ref: str, prompt: str, duration: int = 5, aspect_ratio: str = "16:9") -> str:
"""Submit a first-frame image-to-video job and return the job id."""
api_key = os.environ.get("HIAPI_API_KEY")
if not api_key:
raise RuntimeError("HIAPI_API_KEY is not set in this environment")
body = {
"image": image_ref,
"prompt": prompt,
"duration": duration,
"aspect_ratio": aspect_ratio,
}
response = requests.post(
ENDPOINT,
headers={
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
},
json=body,
timeout=60,
)
if response.status_code >= 400:
raise RuntimeError(f"submit failed: {response.status_code} {response.text}")
payload = response.json()
return payload["id"]
if __name__ == "__main__":
job_id = submit_video_job(
image_ref="https://your-host.example/first-frame.jpg",
prompt="slow dolly-in on the subject, gentle ambient motion, stable camera",
)
print(json.dumps({"job_id": job_id}, indent=2))
Notas sobre a versão em Python
- Falha rápida quando falta a chave. A verificação explícita do ambiente transforma o erro mais comum em um erro local claro, em vez de um 401 remoto.
json=bodyversusdata=json.dumps(body). Passarjson=faz o requests definir o tipo de conteúdo por você, mantendo os cabeçalhos mínimos. Se usardata=, definaContent-Typeexplicitamente.- Timeout generoso. O envio de vídeo às vezes envolve transmitir payloads grandes de imagem. Timeouts curtos demais produzem falhas falsas.
- Ler o corpo da resposta em caso de erro. Lançar tanto o código de status quanto o corpo da resposta significa que a mensagem de falha realmente informa o que a API rejeitou.
Se o primeiro quadro for um arquivo local, leia-o, codifique em base64 e substitua image_ref dentro de submit_video_job por essa string. A assinatura da função não muda — é exatamente por isso que os dois estilos de chamada compartilham a mesma definição de corpo de requisição.
Consultar a tarefa e baixar o vídeo
Uma tarefa enviada não é o vídeo finalizado. Consulte até a tarefa atingir um estado terminal e então baixe.
Loop de polling
import time
import requests
def wait_for_video(job_id: str, interval: float = 10.0, max_wait: float = 600.0) -> str:
"""Poll the job until it finishes; return the output URL."""
import os
api_key = os.environ["HIAPI_API_KEY"]
url = f"https://api.hiapi.ai/v1/kandinsky-6.0-pro/image-to-video/{job_id}"
headers = {"Authorization": f"Bearer {api_key}"}
deadline = time.time() + max_wait
while time.time() < deadline:
response = requests.get(url, headers=headers, timeout=30)
response.raise_for_status()
payload = response.json()
status = payload.get("status")
if status == "succeeded":
return payload["output"]
if status == "failed":
raise RuntimeError(f"job failed: {payload.get('error', 'unknown error')}")
time.sleep(interval)
raise TimeoutError(f"job {job_id} did not finish within {max_wait}s")
Três detalhes fazem esse loop se comportar bem:
- Espere por intervalo, não por sorte. Um sleep fixo de cerca de dez segundos é um ponto de partida razoável; não martele o endpoint em um loop apertado.
- Trate cada estado terminal.
succeededentrega a saída,failedentrega a mensagem de erro. Ambos devem ser tratados como saída. Um loop que só verifica sucesso continuaria girando em uma tarefa morta até o prazo final. - Limite o tempo de espera.
max_waitgarante que o script termine, em vez de rodar para sempre atrás de uma tarefa travada.
Baixar o vídeo finalizado
Depois de obter a URL de saída, grave em disco em streaming, em vez de carregar o arquivo inteiro na memória:
def download_video(output_url: str, path: str = "result.mp4") -> str:
with requests.get(output_url, stream=True, timeout=120) as response:
response.raise_for_status()
with open(path, "wb") as handle:
for chunk in response.iter_content(chunk_size=8192):
if chunk:
handle.write(chunk)
return path
Juntando as peças:
job_id = submit_video_job(
image_ref="https://your-host.example/first-frame.jpg",
prompt="slow dolly-in on the subject, gentle ambient motion, stable camera",
)
output_url = wait_for_video(job_id)
saved = download_video(output_url, "kandinsky_result.mp4")
print(f"saved to {saved}")
Em produção, persista o job_id no momento em que o envio for bem-sucedido. Se o processo reiniciar, você pode retomar o loop de polling com o identificador armazenado, sem pagar por um novo envio.
Solucionar erros comuns de requisição e de tarefa
A maioria das falhas se enquadra em três categorias: chave, imagem ou estado da tarefa. Isole a categoria antes de mudar qualquer outra coisa.
Chave rejeitada
Sintoma: erro de autenticação ou autorização antes de criar qualquer tarefa.
- Confirme que a variável está de fato exportada no shell atual:
echo "$HIAPI_API_KEY". - Verifique se você colou espaços ou quebras de linha junto com a chave; remova-os.
- Confirme que o cabeçalho está escrito
Authorizatione que o valor começa comBearerseguido de um espaço. - Se o curl funciona mas o Python não, imprima
os.environ.get("HIAPI_API_KEY")a partir do mesmo interpretador — a variável pode existir no seu shell, mas não no ambiente herdado pelo script.
Imagem rejeitada
Sintoma: erro de validação no envio, ou tarefa que falha imediatamente.
- Formato incorreto ou obscuro. Reexporte como JPEG ou PNG padrão e tente novamente. Perfis CMYK e codecs exóticos são culpados comuns.
- URL inacessível. Abra a URL da sua imagem em uma janela anônima do navegador; se você não consegue obter os bytes, a API também não consegue.
- Codificação inline corrompida. Se você fez a codificação base64 localmente, decodifique a string uma vez e confirme que é uma imagem válida antes de enviar.
- Proporção de aspecto incompatível. Se o vídeo voltar cortado ou com barras pretas, alinhe
aspect_ratiocom o primeiro quadro, em vez de lutar com a saída depois.
Tarefa que não termina ou falha
Sintoma: o status permanece em queued/processing, ou reporta failed.
- Leia o campo error. Tarefas com falha carregam uma mensagem que geralmente indica o campo ou condição problemática. Não adivinhe — registre o payload de status inteiro.
- Respeite o design assíncrono. Esperar alguns segundos antes de verificar é normal. Um loop de retry apertado que desiste após dois segundos não é um mecanismo de status.
- Limite o tempo de espera. Se a tarefa ultrapassar um limite razoável, trate como falha, exponha o id da tarefa e envie uma nova tarefa, em vez de consultar para sempre.
- Compare com uma chamada comprovadamente boa. Se uma imagem personalizada falha, execute o curl exato do início deste tutorial com uma imagem pública pequena e um prompt curto. Se funcionar, o problema está na sua entrada; se também falhar, o problema está nas credenciais ou no formato da requisição.
Erro de download após sucesso
Se a tarefa reporta succeeded mas o download falha, a URL de saída pode ter expirado ou estar sujeita a controle de acesso. Consulte a tarefa novamente para obter uma nova localização de saída, em vez de repetir a URL expirada.
Trate essas categorias em ordem — chave, depois imagem, depois estado da tarefa — e cada erro se reduz rapidamente a uma causa. Esse é o ciclo completo: enviar o primeiro quadro, consultar até concluir, baixar o resultado.
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