HiAPI
Blogue

Tutorial

Como chamar veo-3.1-fast/image-to-video através da API HiAPI (curl + Python)

Uma demonstração executável de requisição: entrada de quadro inicial, áudio nativo, polling assíncrono e download da saída

Nov 3, 2026
深靛蓝底色上三层嵌套的扁平折线箭头依次向右推进,前缘为杏橙色,像信号流程图,是教程文章的封面图。

O que este tutorial constrói e o que é preciso preparar antes

Este tutorial demonstrará por completo uma integração HiAPI executável, usando veo-3.1-fast/image-to-video: você envia uma imagem de quadro inicial e um prompt, recebe uma tarefa assíncrona, faz polling até ela terminar, e então baixa o vídeo gerado e seu áudio nativo.

Ao final do tutorial, você terá duas implementações equivalentes — um comando curl executável no terminal, e um cliente Python que pode ser colocado em um script ou serviço — ambas acessando o mesmo endpoint HiAPI.

Pré-requisitos

Antes de começar, prepare o seguinte:

  • Uma chave de API HiAPI. Não a envie ao controle de código-fonte. No exemplo abaixo, ela é lida de variável de ambiente, em vez de colada diretamente.
  • A URL base da HiAPI. Use o endereço mostrado no seu painel HiAPI ou na referência da API; armazene-a como variável para configurá-la uma única vez.
  • Uma imagem de quadro inicial. URL publicamente acessível é a entrada mais simples. Use uma imagem real que você tem direito de usar, em formato comum como JPEG ou PNG.
  • Um cliente HTTP. Para esta demonstração, curl é suficiente. Para a versão Python, você só precisa de requests — nenhum SDK de fornecedor.
  • Um diretório local para armazenar o vídeo baixado.

Configurar seu ambiente

Exporte a chave e a URL base para que os exemplos shell e Python possam reutilizá-las:

export HIAPI_API_KEY="your-api-key"
export HIAPI_BASE_URL="https://api.hiapi.ai"
export FIRST_FRAME_URL="https://example.com/your-first-frame.jpg"

O caminho exato do endpoint e os nomes de parâmetros de veo-3.1-fast/image-to-video estão documentados na referência da API HiAPI junto à entrada do modelo — consulte lá a lista canônica de campos, em vez de confiar em fragmentos copiados de outro lugar. Este tutorial concentra-se na estrutura da requisição e no fluxo de trabalho, que permanecem os mesmos mesmo que os detalhes dos campos precisem ser confirmados na documentação.

Vale já memorizar um ponto: este é um modelo baseado em tarefa, não síncrono. Você não recebe o vídeo diretamente na resposta de envio. Você recebe um identificador de tarefa, e o vídeo chega depois.

A estrutura da requisição de imagem para vídeo

Antes de escrever qualquer código, ajuda entender o que você realmente envia e o que recebe de volta. A geração de imagem para vídeo é mais pesada que geração de texto, então a API a modela como uma tarefa em segundo plano.

Entrada: prompt e quadro inicial

Uma requisição veo-3.1-fast/image-to-video tem duas partes conceituais:

  • Um prompt de texto descrevendo movimento, comportamento de câmera e intenção da cena — ou seja, o que deve acontecer depois do quadro inicial.
  • Uma imagem de referência que ancora o quadro inicial da saída. É isso que distingue imagem para vídeo de texto para vídeo: o modelo continua a partir da sua imagem, em vez de criar um quadro de abertura do zero.

O próprio nome do modelo revela o trade-off: a variante fast é ajustada para retorno mais rápido em tarefas de imagem para vídeo, o que a torna ideal para iteração e para pipelines em que latência importa mais que fidelidade máxima.

A saída gerada inclui áudio nativo, então o arquivo final é um vídeo completo com som, não um clipe silencioso que precisa de trilha separada.

Ciclo de vida da tarefa

Como a geração leva tempo, a interação se divide em três fases:

  1. Envio. Você faz POST do prompt e da imagem no endpoint do modelo. A resposta é uma confirmação leve contendo o identificador da tarefa — não o vídeo.
  2. Polling. Você faz GET repetidamente do status da tarefa com esse identificador, até ela reportar conclusão (ou falha).
  3. Obtenção. Uma vez concluída, a resposta da tarefa aponta para o vídeo gerado, que você baixa e armazena.

Manter essas fases separadas no código é a decisão estrutural mais importante. Misturá-las torna retentativas, timeouts e tratamento de erro nebulosos.

Como é um envio bem formatado

Independentemente da serialização exata, planeje uma requisição contendo:

  • O identificador do modelo veo-3.1-fast/image-to-video.
  • O texto do seu prompt.
  • A referência à imagem de quadro inicial.
  • Autenticação via chave de API no cabeçalho da requisição.

Todo o resto — parâmetros opcionais, valores padrão, opções de resolução de saída — pertence à referência da API HiAPI desse modelo. Leia uma vez, coloque os nomes de campos confirmados em constantes, e não os espalhe pela base de código.

Trate a resposta de envio como um recibo. Registre-a, armazene o ID da tarefa, e siga para o polling.

Fazer a chamada com curl

Vamos enviar uma tarefa. O comando abaixo envia o corpo JSON contendo o prompt e a imagem de quadro inicial ao endpoint do modelo na HiAPI, autenticando com a chave do ambiente.

curl -sS -X POST "$HIAPI_BASE_URL/v1/videos/generations" \
  -H "Authorization: Bearer $HIAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "veo-3.1-fast/image-to-video",
    "prompt": "Slow dolly forward through a sunlit forest path, leaves drifting, soft birdsong ambience",
    "image": "'"$FIRST_FRAME_URL"'"
  }'

Algumas observações sobre este comando:

  • O caminho do endpoint e o nome do campo image devem ser conferidos na referência da API HiAPI de veo-3.1-fast/image-to-video; se divergirem, substitua pelos valores canônicos. A estrutura aqui — modelo, prompt, imagem — é o que importa.
  • O prompt realmente faz diferença. Descreva explicitamente o movimento e a movimentação de câmera (“dolly lento para frente”, “folhas flutuando”) e mencione a atmosfera sonora desejada, porque o modelo gera imagem e áudio nativo ao mesmo tempo.
  • -sS mantém a saída silenciosa, mas ainda mostra erros, que é exatamente o que você quer numa demonstração de terminal.
  • A imagem é passada como string de URL. Se preferir enviar bytes, consulte as formas de entrada suportadas na referência, em vez de adivinhar.

Ler a resposta de envio

Um envio bem-sucedido retorna um objeto JSON compacto contendo o identificador da tarefa e o status — mais ou menos assim:

{
  "id": "job_abc123",
  "status": "queued"
}

Os nomes de campos são apenas exemplos; confirme contra a resposta real. O importante é que agora você tem um ID utilizável para polling, e um status que não é de vídeo concluído.

Capture esse ID no shell para simplificar os passos seguintes:

JOB_ID="job_abc123"

Se receber erro de autenticação, sua chave está ausente ou malformada. Erros de validação geralmente significam que o corpo da requisição não corresponde ao esquema documentado — verifique primeiro nomes de campos e tipos. Respostas de rate limit significam que você está enviando rápido demais; faça backoff, em vez de retentar em loop apertado.

A mesma requisição em Python

Abaixo está o envio equivalente em Python. Ele usa apenas requests, lê a configuração de variáveis de ambiente, e separa o passo de envio do polling, para que você possa reutilizar cada parte.

import os
import time
import requests

API_KEY = os.environ["HIAPI_API_KEY"]
BASE_URL = os.environ["HIAPI_BASE_URL"].rstrip("/")
FIRST_FRAME_URL = os.environ["FIRST_FRAME_URL"]

HEADERS = {
    "Authorization": f"Bearer {API_KEY}",
    "Content-Type": "application/json",
}

MODEL = "veo-3.1-fast/image-to-video"

def submit_job(prompt: str, image_url: str) -> str:
    payload = {
        "model": MODEL,
        "prompt": prompt,
        "image": image_url,
    }
    resp = requests.post(
        f"{BASE_URL}/v1/videos/generations",
        headers=HEADERS,
        json=payload,
        timeout=60,
    )
    resp.raise_for_status()
    data = resp.json()
    job_id = data["id"]
    print(f"submitted job: {job_id} (status={data.get('status')})")
    return job_id

if __name__ == "__main__":
    job_id = submit_job(
        prompt="Slow dolly forward through a sunlit forest path, "
               "leaves drifting, soft birdsong ambience",
        image_url=FIRST_FRAME_URL,
    )

Por que essa estrutura

  • Configuração em constantes. URL base, cabeçalhos, caminho do endpoint e nome do modelo são definidos uma única vez. Quando a referência da API indicar que o nome de campo é diferente, você muda apenas uma linha.
  • timeout é explícito. Um envio que trava para sempre é pior que uma falha rápida. Para um corpo JSON pequeno, sessenta segundos é bastante.
  • raise_for_status() expõe falhas cedo. Em 4xx e 5xx você recebe exceção, em vez de silenciosamente interpretar um corpo de erro como tarefa.
  • A função retorna apenas o ID da tarefa. O chamador não precisa saber como é o payload de envio; ele só precisa de algo para fazer polling.

Se sua aplicação for um serviço web, chame submit_job no handler da requisição e passe o ID retornado a um worker em segundo plano. Não bloqueie a requisição HTTP enquanto o vídeo renderiza.

Fazer polling da tarefa assíncrona até o vídeo estar pronto

Agora a tarefa existe e está enfileirada. O polling é como você descobre quando ela termina.

O loop básico de polling

def poll_job(job_id: str, timeout_s: int = 900, initial_delay: float = 5.0) -> dict:
    url = f"{BASE_URL}/v1/videos/generations/{job_id}"
    delay = initial_delay
    deadline = time.time() + timeout_s

    while time.time() < deadline:
        resp = requests.get(url, headers=HEADERS, timeout=30)
        resp.raise_for_status()
        data = resp.json()
        status = data.get("status")
        print(f"status={status}")

        if status == "succeeded":
            return data
        if status in ("failed", "cancelled"):
            raise RuntimeError(f"job {job_id} ended with status={status}: {data}")

        time.sleep(delay)
        delay = min(delay * 1.5, 30.0)  # back off, cap at 30s

    raise TimeoutError(f"job {job_id} did not finish within {timeout_s}s")

Por que cada parte existe

  • Polling, não webhook. A integração mais simples e confiável é perguntar o status periodicamente. Webhooks são melhores em larga escala, mas exigem endpoint público; polling funciona em qualquer lugar.
  • Backoff exponencial com teto. Polling no início é barato e frequente; depois os intervalos aumentam. O teto evita que o intervalo dispare em tarefas longas. Comece com cerca de cinco segundos e cresça.
  • Estados terminais explícitos. Sucesso é um estado; falha e cancelamento são outros. Trate qualquer outra coisa como “continue esperando”, mas nunca espere indefinidamente.
  • Prazo real de tempo. Geração de vídeo é lenta comparada a texto. Escolha um timeout total generoso que corresponda à paciência do seu produto e falhe limpo ao ultrapassá-lo, em vez de fazer loop para sempre.
  • Log de status. Imprima ou registre cada transição de status. Quando algo der errado, é a primeira coisa que você vai querer.

Polling a partir do shell

Para uma verificação manual rápida durante o debug, uma chamada curl já basta:

curl -sS "$HIAPI_BASE_URL/v1/videos/generations/$JOB_ID" \
  -H "Authorization: Bearer $HIAPI_API_KEY"

Execute novamente a cada poucos segundos, ou envolva em um pequeno loop while, até o status virar terminal. Confirme os nomes exatos de campos e caminhos contra a referência da API HiAPI — o ciclo de vida é estável, mas os detalhes de rede são a fonte da verdade.

Quando poll_job retornar, a resposta deve conter uma referência ao vídeo concluído. É isso que você baixa a seguir.

Baixar e validar a saída

A tarefa terminou e a resposta aponta para o arquivo gerado. Baixá-lo é um GET HTTP comum, mas faça duas coisas: grave em disco por streaming e valide o que você recebeu.

Obter o vídeo

import os
from urllib.parse import urlparse

def download_video(job: dict, out_dir: str = "outputs") -> str:
    video_url = job["output"]["url"]  # confirm the field path in the API reference
    os.makedirs(out_dir, exist_ok=True)

    parsed = urlparse(video_url)
    ext = os.path.splitext(parsed.path)[1] or ".mp4"
    dest = os.path.join(out_dir, f"{job['id']}{ext}")

    with requests.get(video_url, stream=True, timeout=120) as r:
        r.raise_for_status()
        with open(dest, "wb") as f:
            for chunk in r.iter_content(chunk_size=1 << 16):
                f.write(chunk)

    size = os.path.getsize(dest)
    if size == 0:
        raise IOError(f"downloaded file is empty: {dest}")
    print(f"saved {dest} ({size} bytes)")
    return dest

Observações:

  • Streaming em blocos. Arquivos de vídeo são grandes; carregá-los em memória com r.content é desperdício e pode falhar em hosts com recursos limitados.
  • Preserve a extensão para que players e ferramentas downstream reconheçam o formato do contêiner.
  • Nomeie pelo ID da tarefa. É único, rastreável e evita conflitos ao executar lotes.
  • Rejeite arquivos vazios. Download de zero byte é download falho, mesmo que o código de status seja 200.

Se a URL do vídeo for assinada ou tiver validade, baixe imediatamente após a conclusão da tarefa, em vez de armazenar a URL para depois.

Validar que é um vídeo real com áudio

O arquivo existir não significa que o arquivo é utilizável. Verifique:

  • Reproduza localmente, com um player de mídia, e confirme imagem e som. veo-3.1-fast/image-to-video gera áudio nativo, então um resultado silencioso é um sinal de alerta que merece investigação antes de chegar aos usuários.
  • Verifique o contêiner com ffprobe, se você tiver ffmpeg instalado:
ffprobe -hide_banner outputs/job_abc123.mp4

Procure na saída um stream de vídeo e um stream de áudio, além de duração razoável. Esse também é o jeito mais rápido de capturar arquivos truncados.

  • Faça uma verificação básica do quadro inicial. Ele deve se parecer com sua imagem de entrada. Se não, suspeite da URL da imagem ou do campo usado para passá-la, antes de culpar o modelo.

Depois que o download for validado, persista o arquivo em armazenamento durável e registre o ID da tarefa junto. Esse mapeamento viabiliza debug posterior, reexecuções e conciliação de cobrança.

Armadilhas e verificações antes de escalar

A maioria dos problemas de integração com APIs assíncronas de vídeo não é problema de modelo. São problemas de pipeline. Aqui está o que verificar primeiro.

Entrada de imagem

  • A imagem é realmente acessível? Uma URL que funciona no seu navegador pode exigir autenticação, bloquear hotlink ou expirar. Prefira URLs estáveis e publicamente legíveis.
  • É um formato suportado? A menos que a referência da API diga o contrário, fique com formatos comuns como JPEG e PNG.
  • O conteúdo está correto? O quadro inicial ancora todo o clipe. Uma imagem de baixa qualidade ou fora do tema produzirá um vídeo decepcionante que nenhum ajuste de prompt conserta.

Estrutura da requisição

  • Nomes e tipos de campos. Valide contra a referência da API HiAPI de veo-3.1-fast/image-to-video. Fragmentos copiados de outras plataformas falharão — o endpoint é da HiAPI, e nomes de campos diferenciam maiúsculas de minúsculas.
  • Content-Type. Envie Content-Type: application/json e um corpo JSON real, não form data.
  • Identificador do modelo. Use a string completa veo-3.1-fast/image-to-video; nomes truncados retornam erros aparentemente sem relação.

Timeouts e retentativas

  • Timeouts independentes por fase. Envio, requisições de polling e download de arquivo merecem cada um o seu. Um timeout global único ou é apertado demais para download, ou frouxo demais para verificação de status.
  • Retente com segurança. O envio pode ser retentado em erros transitórios de rede e respostas 5xx, mas somente se você conseguir determinar se a tarefa foi de fato criada — caso contrário, pode cobrar e gerar em dobro. Polling e download são idempotentes e podem ser retentados livremente.
  • Faça backoff em rate limit. Obedeça à resposta e desacelere, em vez de martelar o endpoint.

Comportamento de polling

  • Nunca faça polling em loop apertado. Desperdiça cota e parece abuso.
  • Sempre tenha uma condição terminal. Um prazo que lança exceção é melhor que uma tarefa presa na memória.
  • Registre cada transição de status com timestamp. Você precisará disso para inferir latência.

Higiene operacional

  • Mantenha as chaves de API no servidor. Nunca as envie ao navegador nem as embuta em aplicativos móveis.
  • Armazene o ID da tarefa junto a cada artefato. É a chave de ligação entre logs, arquivos e registros de uso.
  • Valide downloads antes de publicar. Verifique tamanho e, idealmente, os streams. Arquivos vazios ou sem áudio que chegam aos usuários custam muito mais para corrigir que um download falho que você retenta.
  • Não presuma caminhos de campos. A posição de output na resposta de tarefa concluída é exatamente o tipo de detalhe que muda — leia-a na referência da API, não em tutoriais, incluindo este.

Antes de abrir ticket contra o modelo, percorra esta lista em ordem. Na maioria das vezes, a correção é URL, nome de campo ou falta de backoff.

Esse é o ciclo completo: envie com imagem de quadro inicial e prompt, faça polling da tarefa até concluir, e então baixe e valide o vídeo com áudio nativo. Comece pela página do modelo veo-3.1-fast/image-to-video na HiAPI, confirme os campos atuais da requisição, e então promova as duas funções acima ao seu próprio serviço.

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