HiAPI
Blogue

Tutorial

Como chamar veo-3.1-lite/text-to-video pela API HiAPI: curl e Python

Exemplos de requisição prontos para copiar, o fluxo de geração de vídeo a partir de prompt e o padrão de polling assíncrono

Nov 3, 2026
深炭底色上,一道琥珀色光带横向掠过,构图克制,是这篇教程文章的封面图。

O que veo-3.1-lite/text-to-video oferece

veo-3.1-lite/text-to-video é um endpoint de texto para vídeo na HiAPI: você envia um prompt de texto e o modelo retorna um clipe de vídeo gerado. O "lite" no nome é o sinal mais importante na hora de escolher o modelo. A camada leve foi feita para throughput e iteração rápida, não para fidelidade máxima, então é uma ótima escolha padrão quando você precisa de muitos clipes curtos, quando quer prototipar funcionalidades antes de decidir adotar um modelo mais pesado, ou quando o clipe é apenas uma peça de um pipeline de automação maior e a latência por clipe e a escala em lote importam mais que a qualidade cinematográfica.

Quando o clipe em si é o produto — um plano principal, material de marketing, ou qualquer conteúdo que será examinado quadro a quadro por pessoas — vale trocar para um modelo de texto para vídeo mais pesado. Mas quando o clipe é apenas um componente — uma prévia de rascunho, um loop de fundo, um esboço de storyboard, ou uma primeira renderização que depois será refinada — escolha veo-3.1-lite/text-to-video.

Duas características deste endpoint determinam tudo o que vem a seguir:

  • É assíncrono. Uma requisição de geração não retorna um arquivo reproduzível diretamente na mesma resposta. Você envia uma tarefa, recebe uma referência a ela e então faz polling até que a tarefa esteja concluída e a URL de saída esteja disponível.
  • É guiado por prompt. Não há imagem de entrada nem vídeo de origem neste fluxo — o texto que você envia é o briefing criativo completo, então a qualidade do prompt é a principal alavanca que você controla.

O restante deste guia é um passo a passo acionável: construir a requisição, enviá-la com curl, portar para Python, fazer polling corretamente e lidar com as falhas que você realmente vai encontrar.

Pré-requisitos e autenticação

Antes da primeira chamada, garanta que você tenha:

  • Uma chave de API da sua conta HiAPI. Trate-a como segredo, com o mesmo cuidado de uma senha de banco de dados.
  • Uma forma de enviar requisições HTTP: curl para verificações rápidas e Python 3 com a biblioteca requests para qualquer tarefa automatizada.
  • Um local para armazenar o clipe final, já que a saída chega como uma URL que você precisará baixar.

A chave é enviada em cada requisição por meio de um cabeçalho HTTP. Copie exatamente o nome do cabeçalho e o prefixo Bearer mostrados; cabeçalho ausente ou mal formatado é a causa mais comum de falha na primeira chamada.

Mantenha a chave fora do controle de versão

Nunca cole uma chave real em um arquivo que você vai commitar. Use variáveis de ambiente:

export HIAPI_API_KEY="your-key-here"

E leia-a em tempo de execução. Se você trabalha em equipe ou em um pipeline de CI, guarde a chave em um gerenciador de segredos e injete-a como variável de ambiente no deploy. Se a chave for exposta alguma vez — por um commit acidental, colada em um ticket, impressa em logs — rotacione-a imediatamente, em vez de contar que ninguém vai notar.

Antes de começar a depurar qualquer outra coisa, faça uma verificação rápida de que a variável de ambiente está definida:

test -n "$HIAPI_API_KEY" && echo "key is set" || echo "key is missing"

Uma requisição curl funcional

Abaixo está o formato da requisição de envio de texto para vídeo. Substitua a base URL de placeholder pelo endereço documentado na sua referência da HiAPI e mantenha o identificador do modelo exatamente como está: veo-3.1-lite/text-to-video.

curl -X POST "$HIAPI_BASE_URL/v1/videos/generations" \
  -H "Authorization: Bearer $HIAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "veo-3.1-lite/text-to-video",
    "prompt": "A slow dolly-in on a rain-soaked neon sign at night, shallow depth of field, gentle camera drift, cinematic color"
  }'

Um envio bem-sucedido não retorna os bytes do vídeo. Ele retorna um objeto de tarefa: contendo o identificador da tarefa, um campo status com valor inicial como queued ou processing, e metadados sobre o horário de criação. Esse identificador é o handle de tudo o que você fizer depois — você fará polling dele até que o status atinja um estado terminal, e só então a resposta trará a URL do clipe gerado.

Se você estiver testando manualmente, capture a resposta completa em vez de apenas olhar para ela:

curl -s -X POST "$HIAPI_BASE_URL/v1/videos/generations" \
  -H "Authorization: Bearer $HIAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"veo-3.1-lite/text-to-video","prompt":"A paper boat drifting down a sunlit gutter, macro lens"}' \
  | tee submission.json

Dois hábitos valem a pena cultivar desde já. Primeiro, sempre envie Content-Type: application/json; um corpo que parece JSON mas não está declarado como JSON será rejeitado. Segundo, se a chamada falhar, leia o corpo do erro antes de mudar qualquer coisa — a HiAPI retorna mensagens estruturadas que normalmente apontam o problema (chave errada, modelo desconhecido, campo ausente) com mais precisão do que apenas o código de status.

Se aqui vier um código de status não-2xx, a tarefa nunca foi aceita. Não faça polling de um envio que falhou; corrija a requisição primeiro.

Implementando a mesma requisição em Python

A chamada curl pode ser porta direto para Python. O script abaixo envia o prompt, verifica se o envio foi bem-sucedido e guarda a referência da tarefa para a etapa de polling.

import os
import requests

BASE_URL = os.environ["HIAPI_BASE_URL"]
API_KEY = os.environ["HIAPI_API_KEY"]

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

payload = {
    "model": "veo-3.1-lite/text-to-video",
    "prompt": (
        "A single lantern floating on still black water, "
        "soft ripples, warm light against cool darkness, slow push-in"
    ),
}

response = requests.post(
    f"{BASE_URL}/v1/videos/generations",
    headers=headers,
    json=payload,
    timeout=30,
)

if response.status_code >= 400:
    raise RuntimeError(f"Submission failed: {response.status_code} {response.text}")

job = response.json()
job_id = job["id"]
print(f"submitted job {job_id} with status {job.get('status')}")

Há algumas escolhas intencionais nesse trecho:

  • Use json=payload em vez de montar a string à mão. Deixe a biblioteca cuidar da serialização e do content type; isso evita toda uma classe de bugs relacionados a aspas.
  • Defina timeout explicitamente. Sem isso, uma conexão travada deixa o script pendurado indefinidamente. A chamada de envio é rápida — mesmo um timeout generoso aqui continua curto.
  • Falhe de forma dura em não-2xx. Lançar uma exceção imediatamente mantém o erro próximo da causa, em vez de deixar um ID de tarefa corrompido propagar até o loop de polling.
  • A chave vem de os.environ. O arquivo em si não contém nenhum segredo.

Envolva isso em uma função que retorna o job_id e você terá a parte de envio de qualquer pipeline.

Fazendo polling da tarefa assíncrona e baixando o clipe

Geração de vídeo leva tempo. A chamada de envio retorna em um ou dois segundos; a renderização real não. Se você escrever código assumindo que a primeira resposta contém a URL do vídeo, ele vai falhar — não porque a API está quebrada, mas porque você está lendo uma tarefa que ainda não terminou.

O padrão correto é polling com limite: durma entre verificações, aumente o intervalo gradualmente conforme a espera cresce, e desista após um número máximo de tentativas em vez de fazer um loop infinito.

import time
import requests

def wait_for_video(job_id, headers, base_url, max_attempts=60, initial_delay=5):
    delay = initial_delay
    for attempt in range(1, max_attempts + 1):
        r = requests.get(
            f"{base_url}/v1/videos/generations/{job_id}",
            headers=headers,
            timeout=30,
        )
        if r.status_code >= 400:
            raise RuntimeError(f"Poll failed: {r.status_code} {r.text}")

        data = r.json()
        status = data.get("status")
        print(f"attempt {attempt}: {status}")

        if status == "completed":
            return data
        if status in {"failed", "cancelled"}:
            raise RuntimeError(f"Job {job_id} ended as {status}: {data}")

        time.sleep(delay)
        delay = min(delay * 1.5, 30)

    raise TimeoutError(f"Job {job_id} did not finish within polling budget")

Quando a tarefa atinge completed, a resposta traz a localização da saída — a URL do clipe concluído. Se o clipe for grande, baixe-o em vez de lê-lo por streaming para a memória:

result = wait_for_video(job_id, headers, BASE_URL)
video_url = result["output"]["url"]

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

print("saved", f"{job_id}.mp4")

Dois detalhes importam em produção. Primeiro, as URLs de saída podem ter prazo de validade, então baixe-as em tempo hábil em vez de guardar o link por dias e tentar buscá-lo depois — persista o arquivo, ou copie-o para o seu próprio armazenamento. Segundo, registre o ID da tarefa ao lado de cada artefato. Quando algo der errado a jusante, o ID da tarefa permite cruzar seus registros com o que a API realmente retornou.

Erros comuns e como corrigi-los

A maioria das falhas na primeira execução cai em poucas categorias. Abaixo está como cada uma se manifesta e o que mudar.

Falha de autenticação

Um 401 ou 403 no envio significa que a chave não foi aceita. Verifique em ordem: a variável de ambiente realmente tem valor naquele shell ou processo; o nome do cabeçalho é Authorization; o prefixo Bearer está presente e seguido de um espaço; a chave foi rotacionada ou revogada. Uma chave que funciona no seu terminal mas falha no CI quase sempre significa que o segredo não foi injetado naquele ambiente.

Identificador de modelo inválido

Um 400 ou 404 apontando para o nome do modelo significa que a string não corresponde. O identificador é veo-3.1-lite/text-to-video — a barra e o número de versão fazem parte dele. Copie e cole, não digite de novo; um espaço extra ou um número de versão alterado já basta para falhar.

Prompt mal formatado ou vazio

Um prompt vazio, um campo prompt ausente, ou um corpo enviado sem Content-Type: application/json serão rejeitados antes de a geração começar. Se você constrói prompts dinamicamente, registre a string exata que enviou quando a chamada falhar — bugs de template que produzem prompts em branco ou truncados são comuns e ficam invisíveis até serem impressos.

Timeout no polling

Se wait_for_video esgotar o orçamento, não reenvie imediatamente. A tarefa pode simplesmente ainda estar rodando. Verifique o status mais uma vez, confirme que seu intervalo de polling não é tão longo que consome o orçamento inteiro dormindo, e confirme que o orçamento é realista para o comprimento do clipe solicitado. Reenviar a cada timeout é exatamente como você acaba pagando várias vezes pelo mesmo clipe sem perceber.

Limite de taxa

429 significa que é preciso recuar. O atraso exponencial no loop de polling já ajuda nisso; aplique a mesma contenção aos envios, e enfileire o trabalho em vez de enviar em rajadas. Em caso de dúvida, siga qualquer dica de retry nos cabeçalhos de resposta antes de aplicar seu próprio plano de backoff.

Custos e dicas de iteração antes de ir para produção

A geração de vídeo é cobrada por saída — ou seja, pelo comprimento do conteúdo que você produz — então o erro caro é usar clipes longos para testar ideias pequenas. O fluxo de trabalho mais econômico é aquele que nunca gera um clipe que você não precisa.

Teste a estrutura do prompt, não a saída final. Escreva e refine seus prompts usando a duração mais curta suportada pelo endpoint. Uma vez que composição, movimento e linguagem de câmera estejam corretos em um clipe curto, aumente a duração para a renderização final. A maior parte da iteração de prompt diz respeito a redação, não a comprimento, e renderizações curtas respondem igualmente às perguntas de redação.

Mantenha um log de prompts. Registre o prompt de cada teste, a duração e sua própria avaliação. Dez iterações registradas ensinam mais sobre o que o modelo responde do que cinquenta iterações sem registro, e você não acaba retestando prompts que já rejeitou.

Processe em lote com consciência. Se você precisa de muitos clipes, enfileire-os e rode-os em paralelo dentro dos limites de taxa — mas faça um teste piloto do lote inteiro com poucos prompts primeiro. Descobrir um bug sistemático de prompt depois de enviar duzentas tarefas é o custo mais evitável que existe.

Faça polling com moderação. Polling frequente não faz a renderização terminar mais rápido e ainda pode te empurrar para o limite de taxa. Um intervalo inicial modesto com backoff suave é o padrão correto.

Falhe cedo e alto. Cada proteção no código acima — exceção em não-2xx no envio, detecção de estado terminal de falha no polling, número limitado de tentativas — existe para evitar que você pague duas vezes pelo mesmo trabalho. Mantenha-as em produção.

Comece com um prompt curto e uma requisição curl para confirmar o ciclo de vida da tarefa de ponta a ponta, depois mova o mesmo fluxo para o seu pipeline em Python. A referência completa do endpoint está na página de documentação do modelo veo-3.1-lite/text-to-video na HiAPI.

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