HiAPI
Blogue

Tutorial

API de vídeo a partir de imagem PixVerse V6: exemplos curl e Python prontos para executar

Oct 20, 2026
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 de pixverse-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 localhost ou 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ção aspect_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 o image_url para 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 como first_frame, image, input_image, motion_mode e camera_movement são todos rejeitados — a imagem inicial vai em image_url, e o movimento de câmera é escrito no texto de prompt.
  • duration é um inteiro, diferente de alguns outros modelos de vídeo da plataforma que aceitam string ("6"). Enviar "5" retorna invalid input: duration: got string, want integer. Valores fora do intervalo reportam o limite diretamente, por exemplo duration: 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 retorna image_url: got number, want string; omiti-lo retorna missing 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 em pixverse-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ão false) — enviar uma string retorna generate_audio: got string, want boolean. Se você quer som, defina como true; 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

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 →

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