HiAPI
Blogue

Tutorial

API vidu-q3-ad reference-to-video: curl, Python e uma requisição pronta para executar

Oct 20, 2026
API vidu-q3-ad reference-to-video: curl, Python e uma requisição pronta para executar

O que você vai construir

Dispare uma chamada reference-to-video na API assíncrona /v1/tasks do hiapi e transforme de 1 a 7 imagens estáticas de produto em um anúncio curto com áudio nativo. vidu-q3-ad/reference-to-video é o tier do Vidu Q3 voltado para publicidade: você envia imagens de referência do produto, escreve uma descrição de cena e o modelo retorna um MP4 de 3 a 15 segundos, já com edição inteligente, movimento de câmera e efeitos sonoros gerados — não há faixa de áudio separada para você fornecer.

Basta enviar um único POST, depois fazer polling (ou receber callbacks) até a tarefa concluir e baixar o MP4. Primeiro obtenha sua chave no console do hiapi; toda requisição abaixo usa Authorization: Bearer sk-....

Três itens para copiar literalmente:

  • Endpoint de envio: POST https://api.hiapi.ai/v1/tasks
  • Endpoint de polling: GET https://api.hiapi.ai/v1/tasks/{taskId}
  • ID do modelo: vidu-q3-ad/reference-to-video — a barra e o sufixo -ad não podem ser omitidos; vidu-q3-mix e vidu-q4-preview são endpoints diferentes.

Schema de entrada (obtido por sondagem em tempo real)

Campo Obrigatório Tipo Descrição
prompt Sim string Descrição da cena; refira-se aos produtos por número, por exemplo "o relógio da imagem de referência 1"
reference_image_urls Sim array, 1–7 URLs públicas das imagens estáticas do produto (buscadas pelo servidor)
duration Sim integer, 3–15 Segundos; deve ser inteiro (4.5 é rejeitado)
size Não enum (10 valores) Determina ao mesmo tempo a proporção e o tier 720/1080; padrão é 1280*720

O validador é bem rígido — qualquer campo fora da lista acima é rejeitado de imediato. Duas categorias de rejeição merecem destaque, porque pegam todos que migram de outros modelos de vídeo do hiapi:

  • aspect_ratio e resolution são ambos rejeitados. Quase todos os outros endpoints de vídeo do hiapi usam esses dois campos; o q3-ad não usa. O tamanho só pode ser controlado via size.
  • reference_audio_urls também é rejeitado. Diferente do tier reference-to-video do Vidu Q4 (que aceita trechos de voz), o q3-ad não tem entrada de áudio — sua trilha é gerada nativamente. (Como confirmei isso, veja a seção de verificação abaixo.)

Como escolher size

size é uma string width*height de um conjunto fixo. Pode ser lida como uma grade de cinco proporções × dois tiers de qualidade:

Proporção Tier 720 Tier 1080
16:9 paisagem 1280*720 1920*1080
9:16 retrato 720*1280 1080*1920
1:1 quadrado 1280*1280 1808*1808
4:3 1280*960 1920*1440
3:4 960*1280 1440*1920

Escolha a linha conforme o local de veiculação (Reels e TikTok usam retrato 720*1280 / 1080*1920, anúncios in-stream no YouTube usam a linha paisagem) e a coluna conforme o equilíbrio entre qualidade e orçamento. A cobrança é por segundos de saída, e o tier define o preço por segundo — veja Preços; a cobrança mínima é de 3 segundos de clipe.

Requisição mínima (curl)

Dois passos: enviar e depois fazer polling.

# 1) Enviar — retorna um taskId
curl -s -X POST https://api.hiapi.ai/v1/tasks \
  -H "Authorization: Bearer sk-your-key" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "vidu-q3-ad/reference-to-video",
    "input": {
      "prompt": "Um anúncio do produto smartwatch de titânio da imagem de referência 1: ele gira lentamente sobre um pedestal escuro e reflexivo enquanto uma faixa dura de luz varre a caixa escovada da esquerda para a direita, a tela permanece escura, a câmera mantém um ângulo baixo de herói. Iluminação comercial, alto contraste, detalhe macro.",
      "reference_image_urls": [
        "https://your-cdn.com/watch-front.png",
        "https://your-cdn.com/watch-side.png"
      ],
      "duration": 5,
      "size": "1280*720"
    }
  }'
# -> {"data":{"taskId":"..."}}

# 2) Fazer polling até data.status ser "success" ou "fail"
curl -s https://api.hiapi.ai/v1/tasks/THE_TASK_ID \
  -H "Authorization: Bearer sk-your-key"
# Sucesso -> data.status == "success", e data.output[0].url é o MP4

As URLs das imagens de referência devem ser acessíveis publicamente — a tarefa as busca no servidor, então localhost ou URLs assinadas que expiram em segundos não funcionam.

Python: enviar, fazer polling, baixar

import time
import requests  # ou use urllib/subprocess para manter zero dependências

API = "https://api.hiapi.ai/v1/tasks"
HEADERS = {"Authorization": "Bearer sk-your-key", "Content-Type": "application/json"}

def submit():
    payload = {
        "model": "vidu-q3-ad/reference-to-video",
        "input": {
            "prompt": "Tomada publicitária do tênis da imagem de referência 1 girando em uma plataforma iluminada, "
                      "push-in lento, luz de contorno suave, enquadramento de herói do produto.",
            "reference_image_urls": ["https://your-cdn.com/sneaker.png"],
            "duration": 5,
            "size": "1080*1920",   # retrato 1080p, para Reels / TikTok
        },
    }
    body = requests.post(API, headers=HEADERS, json=payload, timeout=60).json()
    task_id = (body.get("data") or {}).get("taskId")
    if not task_id:
        raise RuntimeError(f"envio rejeitado: {body}")   # 400 cai aqui
    return task_id

def wait(task_id, timeout_s=600, interval=5):
    deadline = time.time() + timeout_s
    while time.time() < deadline:
        data = requests.get(f"{API}/{task_id}", headers=HEADERS, timeout=30).json().get("data") or {}
        status = data.get("status")
        if status == "success":
            return data["output"][0]["url"]
        if status == "fail":
            raise RuntimeError(f"tarefa falhou: {data.get('error')}")
        time.sleep(interval)
    raise TimeoutError(task_id)

url = wait(submit())
mp4 = requests.get(url, timeout=120).content   # baixe imediatamente — a URL de saída expira
open("ad.mp4", "wb").write(mp4)

A renderização do Q3-ad costuma concluir em um ou dois minutos; um intervalo de polling de 5 segundos é suficiente. A URL de saída é temporária (tem expireAt), então baixe os bytes assim que a tarefa concluir, em vez de fazer hotlink na página.

Padrões para produção

Use callback em vez de polling. Em cenários de servidor, configure um callback de nível superior para não precisar manter um loop de polling:

{
  "model": "vidu-q3-ad/reference-to-video",
  "input": { "prompt": "...", "reference_image_urls": ["..."], "duration": 5 },
  "callback": { "url": "https://your-app.com/hooks/hiapi", "when": "final" }
}

when só aceita "final" — o hook dispara uma vez quando a tarefa atinge o estado terminal, e seu corpo é o mesmo payload retornado por GET /v1/tasks/{taskId}. O hook pode ser repetido, então deduplique por taskId e mantenha um polling lento como rede de segurança caso alguma entrega se perca.

Decida pelo corpo da resposta, não pelo código de status HTTP. Falhas de renderização ainda retornam HTTP 200; a informação de falha fica em data.status == "fail", com detalhes em data.error. Apenas rejeições no nível da requisição (campos inválidos, chave ausente, não autenticado) usam 4xx e retornam {"code":400,"error_code":"INVALID_REQUEST","message":"...","data":null}.

Idempotência. Cada envio cria uma nova tarefa cobrada, então salve o taskId no instante em que o receber e, ao tentar de novo, faça polling desse mesmo ID. Nunca reenvie só porque uma única requisição de polling deu timeout.

O que eu verifiquei

Sondei o endpoint online com valores inválidos, que são rejeitados gratuitamente na fase de validação — nenhuma tarefa é criada e nada é cobrado (valores inválidos falham antes de qualquer renderização começar, o que é mais seguro do que omitir campos). A resposta real é o schema:

  • Corpo quase vazio → reference_image_urls: missing required field ...; duration: missing required field .... Portanto prompt, reference_image_urls e duration são obrigatórios.
  • duration: 99999 → maximum: got 99,999, want 15; duration: 1 → minimum: got 1, want 3; duration: 4.5 → got number, want integer. Duration é um inteiro de 3 a 15.
  • reference_image_urls com 8 itens → maxItems: got 8, want 7. O limite é 7.
  • size: "9000p" → value must be one of '1280*720', '1280*960', '1280*1280', '960*1280', '720*1280', '1920*1080', '1920*1440', '1808*1808', '1440*1920', '1080*1920'.
  • aspect_ratio e resolution → ambos retornam additional properties ... not allowed. Eles não são campos do q3-ad; use size.
  • reference_audio_urls enviado como array bem formado → additional properties 'reference_audio_urls' not allowed. (Se enviado como não-array, primeiro dispara uma verificação de mídia separada — "must be an array" — o que engana: o campo realmente não está no schema.) Este tier não tem entrada de referência de voz; o áudio é gerado nativamente.

Esta é uma referência de integração, então não contém alegações de qualidade — não executei renderizações pagas para este artigo. Sobre como é um output[0].url bem-sucedido, a página do modelo abaixo hospeda o exemplo próprio desse endpoint: um MP4 real com faixa de áudio.

Guias relacionados

Perguntas frequentes

Como faço o clipe sair em retrato ou paisagem? Via size, não aspect_ratio. Retrato usa 720*1280 ou 1080*1920; paisagem usa 1280*720 ou 1920*1080.

Posso fornecer narração ou referência de voz? Não neste tier — reference_audio_urls é rejeitado. O Q3-ad gera áudio nativo por conta própria. Se você precisa que um trecho de referência guie o áudio, use o tier reference-to-video do Vidu Q4.

Quantas imagens de produto posso enviar? De uma a sete, em reference_image_urls, referenciadas por número no prompt ("a garrafa da imagem de referência 2").

Minha tarefa retornou 200 mas sem vídeo — por quê? Olhe data.status. "fail" retorna HTTP 200 com o motivo em data.error; só requisições malformadas recebem 4xx.

Quanto custa? A cobrança é por segundos de saída, e o tier de size (nível 720 vs nível 1080) define o preço por segundo. O mínimo é um clipe de 3 segundos. Veja as tarifas atuais em Preços.

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