Tutorial
API de vídeo a partir de imagem PixVerse V6: exemplos curl e Python prontos para executar
- Modelo
- pixverse-v6
Para animar uma imagem estática com pixverse-v6/image-to-video no hiapi, basta uma chamada POST /v1/tasks mais um polling — exatamente o mesmo padrão de tarefa assíncrona usado por todos os modelos de geração da plataforma. A diferença em relação ao endpoint de texto para vídeo está na entrada: você fornece um quadro inicial (image_url) e um prompt que descreve como ele deve se mover. Este guia traz exemplos curl e Python prontos para copiar e colar, os campos de entrada que esse endpoint realmente valida e um detalhe inesperado que faz muita gente que vem do texto para vídeo tropeçar.
O que você precisa
- Uma chave de API do hiapi. Crie-a no console e envie via
Authorization: Bearer sk-<your-key>. O formato do cabeçalho está na documentação de autenticação. - O ID do modelo
pixverse-v6/image-to-video(mantenha a barra — ela faz parte do ID, não é um caminho de URL). Este é um ID de modelo diferente depixverse-v6/text-to-video. - Uma URL de imagem acessível publicamente como primeiro quadro. A plataforma busca essa imagem no lado do servidor, então links de
localhostou de buckets privados não funcionam — hospede o primeiro quadro em um local que a API possa acessar via GET. - Com curl, não é preciso instalar nada. Para o exemplo em Python, basta a biblioteca padrão mais
requests.
O PixVerse V6 de imagem para vídeo usa a imagem estática que você fornece como quadro inicial e a anima em um clipe de 1 a 15 segundos. Ele roda na API de tarefas assíncronas unificada — você envia a tarefa, faz polling do resultado e baixa a saída antes que a URL expire.
Requisição mínima funcional (curl)
Envie a tarefa para POST /v1/tasks. image_url e prompt são obrigatórios:
curl -s -X POST https://api.hiapi.ai/v1/tasks \
-H "Authorization: Bearer sk-<your-key>" \
-H "Content-Type: application/json" \
-d '{
"model": "pixverse-v6/image-to-video",
"input": {
"image_url": "https://cdn.example.com/your-first-frame.jpg",
"prompt": "Slow push-in; the subject turns toward the camera and smiles, soft breeze in the hair",
"duration": 5,
"resolution": "720p"
}
}'
A resposta retorna um ID de tarefa:
{ "code": 200, "data": { "taskId": "a1b2c3d4-...." } }
Em seguida, faça polling de GET /v1/tasks/<taskId> até que data.status seja success:
curl -s https://api.hiapi.ai/v1/tasks/a1b2c3d4-.... \
-H "Authorization: Bearer sk-<your-key>"
Em caso de sucesso, a URL do vídeo concluído fica em data.output[0].url:
{
"code": 200,
"data": {
"status": "success",
"output": [{ "url": "https://temp.hiapi.ai/....mp4", "expireAt": 1760000000 }]
}
}
Essa URL de saída é temporária — ela traz um timestamp expireAt. Baixe o .mp4 imediatamente e salve no seu próprio bucket; não a referencie diretamente por hotlink.
O mesmo fluxo em Python
import time, requests
BASE = "https://api.hiapi.ai/v1/tasks"
HEADERS = {"Authorization": "Bearer sk-<your-key>", "Content-Type": "application/json"}
def create_task():
payload = {
"model": "pixverse-v6/image-to-video",
"input": {
"image_url": "https://cdn.example.com/your-first-frame.jpg",
"prompt": "Slow push-in; the subject turns toward the camera and smiles",
"duration": 5,
"resolution": "720p",
},
}
r = requests.post(BASE, json=payload, headers=HEADERS, timeout=60)
r.raise_for_status()
data = r.json()
task_id = (data.get("data") or {}).get("taskId")
if not task_id:
raise RuntimeError(f"create failed: {data}") # 400s echo the exact bad field
return task_id
def wait_for(task_id, timeout_s=600):
deadline = time.time() + timeout_s
while time.time() < deadline:
try:
r = requests.get(f"{BASE}/{task_id}", headers=HEADERS, timeout=30)
task = (r.json().get("data") or {})
except requests.RequestException:
time.sleep(5) # one transient network blip should not abort the whole poll
continue
status = task.get("status")
if status == "success":
return task["output"][0]["url"]
if status == "fail":
raise RuntimeError(f"task failed: {task.get('error')}")
time.sleep(5) # still pending — keep the SAME task_id, re-poll
raise TimeoutError(task_id)
task_id = create_task()
url = wait_for(task_id)
video = requests.get(url, timeout=120).content
open("out.mp4", "wb").write(video)
print("saved out.mp4")
O loop de polling verifica explicitamente success e fail e continua aguardando em qualquer outro estado, então ele lida de forma robusta com qualquer rótulo de estado intermediário que a tarefa reporte durante a renderização. Se uma requisição de polling falhar, durma e faça polling novamente do mesmo task_id salvo, em vez de reenviar — reenviar gera uma segunda cobrança de renderização.
Parâmetros que o pixverse-v6/image-to-video realmente valida
Abaixo estão os campos que o endpoint em produção realmente aceita. Confirmei cada um enviando valores inválidos e lendo os erros de validação, depois cruzei as informações com a documentação do modelo:
| Campo | Tipo | Valores / intervalo | Descrição |
|---|---|---|---|
image_url |
string | uma URL de imagem pública | Obrigatório. Primeiro quadro. Uma única string de URL — não um array. |
prompt |
string | — | Obrigatório. Descreve o movimento aplicado a esse quadro. |
duration |
integer | 1–15, padrão 5 |
Em segundos. Deve ser um inteiro, não uma string. |
resolution |
string | 360p, 540p, 720p, 1080p |
Resolução de saída. |
negative_prompt |
string | — | Opcional; conteúdo a evitar. |
seed |
integer | — | Opcional; reutilize para obter resultados reproduzíveis. |
style |
string | anime, 3d_animation, clay, comic, cyberpunk |
Estilo predefinido opcional. |
generate_audio |
boolean | true / false, padrão false |
Opcional; ativa ou desativa a trilha sonora/ambiente gerada. |
Não existe campo aspect_ratio aqui. Essa é a única diferença real em relação às requisições de texto para vídeo que você talvez já tenha visto, e vale a pena memorizar (veja abaixo).
Considerações para produção
- Sem
aspect_ratio— a geometria da saída segue sua imagem de entrada. O endpoint de texto para vídeo aceita uma enumeraçãoaspect_ratio; este endpoint a rejeita. Como você fornece o primeiro quadro, o clipe herda as dimensões desse quadro. Se você precisa de um clipe vertical 9:16, corte ou preencha oimage_urlpara 9:16 antes de enviar — não há botão de proporção para sobrescrever isso. - A estrutura da requisição é fechada. Qualquer campo fora da tabela acima é rejeitado com o erro
invalid input: <root>: additional properties '<field>' not allowed. Erros de digitação ou campos inventados falham ruidosamente no envio, em vez de serem silenciosamente ignorados. Campos comofirst_frame,image,input_image,motion_modeecamera_movementsão todos rejeitados — a imagem inicial vai emimage_url, e o movimento de câmera é escrito no texto deprompt. durationé um inteiro, diferente de alguns outros modelos de vídeo da plataforma que aceitam string ("6"). Enviar"5"retornainvalid input: duration: got string, want integer. Valores fora do intervalo reportam o limite diretamente, por exemploduration: maximum: got 999, want 15.- Os erros retornam um campo por vez, em um envelope achatado:
{"code":400,"error_code":"INVALID_REQUEST","message":"invalid input: image_url: missing required field \"image_url\""}. Corrija o campo reportado e reenvie; o próximo campo inválido (se houver) aparecerá na próxima chamada. - Falha de tarefa ainda retorna HTTP 200. A informação de falha fica no corpo da resposta, na forma de
data.status: "fail"— verifique o campo de status, não apenas o código de status HTTP. URLs de imagem que a plataforma não consegue buscar são uma causa comum, então confirme primeiro que seu primeiro quadro é público. - Cobrança por segundo, que cresce com a resolução e a duração, então um clipe 1080p/15s custa muito mais que um 540p/3s. Consulte as tarifas atuais na página de preços antes de gerar em lote.
O que verifiquei (e o que não verifiquei)
Mapeei o contrato de entrada enviando valores deliberadamente inválidos ao endpoint /v1/tasks em produção do pixverse-v6/image-to-video e lendo as respostas de validação da própria API — foi uma sondagem de schema somente leitura, sem callback e sem custo de renderização. Descobertas específicas que vale lembrar:
image_urlé o campo obrigatório do primeiro quadro, e é uma única string, não um array. Enviar um número retornaimage_url: got number, want string; omiti-lo retornamissing required field "image_url". Campos alternativos previsíveis (first_frame,image,input_image) são todos rejeitados como propriedades desconhecidas.- Este endpoint rejeita
aspect_ratio(additional properties 'aspect_ratio' not allowed), embora ele seja válido empixverse-v6/text-to-video. É exatamente por isso que uma requisição de imagem para vídeo parece diferente de uma de texto para vídeo: a forma da saída é determinada pelo seu quadro, não por um parâmetro. generate_audioé um booleano de verdade (padrãofalse) — enviar uma string retornagenerate_audio: got string, want boolean. Se você quer som, defina comotrue; ele não é forçado a ligar ou desligar.
Não renderizei clipes nessa sondagem, então este guia não faz afirmações sobre a qualidade do movimento ou sobre o quão fielmente o modelo preserva o quadro de entrada — ele apenas documenta o contrato exato de requisição que esse endpoint aplica.
Conteúdo relacionado
- PixVerse V6 imagem para vídeo — página do modelo
- PixVerse V6 imagem para vídeo — documentação da API
- Referências de criar tarefa e obter detalhes da tarefa
- Autenticação
- Preços
- Outros tutoriais de imagem para vídeo no hiapi: hailuo-2.3 e Grok Imagine 1.5
Perguntas frequentes
Por que imagem para vídeo não tem aspect_ratio? Porque o clipe começa com o seu quadro de image_url e herda as dimensões dele. Para controlar a proporção da saída, redimensione ou corte a imagem de entrada antes de enviar — esse endpoint rejeita aspect_ratio como campo desconhecido.
image_url aceita várias imagens? Não. Neste endpoint ele é uma única string de URL; passar um array causa falha de validação. Ele define apenas o quadro inicial.
A unidade de duration é segundos ou quadros? Segundos — qualquer inteiro de 1 a 15, padrão 5. Deve ser inteiro; a string "5" é rejeitada.
Como isso difere do endpoint de texto para vídeo? pixverse-v6/image-to-video anima o quadro que você fornece (image_url + prompt, sem aspect_ratio). pixverse-v6/text-to-video é um ID de modelo separado, gera apenas a partir do prompt e aceita aspect_ratio. Use imagem para vídeo quando você já tem o quadro estático de abertura exato que quer colocar em movimento.
Por que minha requisição falha com “additional properties not allowed”? Você enviou um campo que não existe no schema. A entrada aceita apenas image_url, prompt, duration, resolution, negative_prompt, seed, style e generate_audio.
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