Documentação

Documentação

Geração de vídeo

Geração de vídeos a partir de texto e de imagens da família Veo 3.1 do Google. Dois formatos de API: um endpoint síncrono de chamada única que aguarda até o clipe ficar pronto e um par assíncrono de envio e consulta, recomendado para cargas de trabalho de produção.

Endpoints: POST /v1/video/generations (síncrono, apresentado primeiro) e POST /v1/videos + GET /v1/videos/{id} (assíncronos, recomendados para produção). A geração leva de 30 segundos a vários minutos, dependendo do modelo e da duração — no endpoint síncrono, defina um timeout generoso para o cliente (600s é seguro).

Geração de vídeo a partir de texto

Envie um prompt e escolha um modelo. aspect_ratio é opcional.

import requests

resp = requests.post(
    "https://api.kunavo.com/v1/video/generations",
    headers={"Authorization": f"Bearer {API_KEY}"},
    json={
        "model": "veo-3",
        "prompt": "a cinematic dolly-in on a red origami crane unfolding, soft light",
        "aspect_ratio": "16:9",
    },
    timeout=600,  # video generation can take minutes
)
print(resp.json()["data"][0]["url"])

duration (em segundos) define a duração do clipe nos modelos cobrados por segundo: os modelos Seedance e o Wan 2.7. Os modelos Veo ignoram esse valor; cada clipe do Veo tem 8 segundos e é cobrado por vídeo.

# The models billed per second take a clip length: here 5 seconds of
# Seedance 2. Leave "duration" out and these models make a 5-second clip.
resp = requests.post(
    "https://api.kunavo.com/v1/video/generations",
    headers={"Authorization": f"Bearer {API_KEY}"},
    json={
        "model": "seedance-2",
        "prompt": "a paper crane unfolds into a flock of birds over a city at dawn",
        "duration": 5,
        "aspect_ratio": "16:9",
    },
    timeout=600,
)
print(resp.json()["data"][0]["url"])

Imagem para vídeo

Adicione um image_url e o Veo o animará — por padrão, a imagem estática se torna o primeiro quadro do vídeo.

# Image-to-video: animate a still image. The source image can be an
# https URL, or inline base64 — Kunavo hosts it for the model automatically.
resp = requests.post(
    "https://api.kunavo.com/v1/video/generations",
    headers={"Authorization": f"Bearer {API_KEY}"},
    json={
        "model": "veo-3",
        "prompt": "the crane gently lifts off the desk",
        "image_url": "https://your-cdn.com/source.png",
    },
    timeout=600,
)
print(resp.json()["data"][0]["url"])
image_url aceita uma URL https:// ou um URI base64 data: em linha. O base64 é decodificado e hospedado automaticamente na CDN da Kunavo antes da execução do modelo.

Primeiro quadro, último quadro e referências

image_mode controla como o Veo usa a(s) imagem(ns). Envie image_urls (um array) para incluir mais de uma.

  • frame (padrão) — uma imagem se torna o primeiro quadro; duas imagens se tornam o primeiro e o último quadro, e o Veo gera a transição entre elas.
  • reference — até três imagens atuam como referências de estilo e tema. Elas orientam a aparência, mas nunca aparecem como quadros.
# First + last frame: pass two images and Veo fills in the motion.
resp = requests.post(
    "https://api.kunavo.com/v1/video/generations",
    headers={"Authorization": f"Bearer {API_KEY}"},
    json={
        "model": "veo-3",
        "prompt": "a smooth transition between the two poses",
        "image_urls": [
            "https://your-cdn.com/first.png",
            "https://your-cdn.com/last.png",
        ],
        "image_mode": "frame",
    },
    timeout=600,
)

Enviando um quadro de origem

Quando você só tem um arquivo local, envie-o uma vez por POST /v1/files e reutilize a URL permanente retornada.

# No public URL? Upload the still first, then animate it.
with open("frame.png", "rb") as f:
    up = requests.post(
        "https://api.kunavo.com/v1/files",
        headers={"Authorization": f"Bearer {API_KEY}"},
        files={"file": f},
    ).json()

resp = requests.post(
    "https://api.kunavo.com/v1/video/generations",
    headers={"Authorization": f"Bearer {API_KEY}"},
    json={
        "model": "veo-3",
        "prompt": "slow push-in, volumetric light",
        "image_url": up["url"],
    },
    timeout=600,
)

Parâmetros

ParâmetroTipoObservações
modelstring (obrigatório)Slug de um modelo de vídeo disponível em /v1/models.
promptstring (obrigatório)Descrição textual da cena.
image_urlstringImagem de origem única para geração de vídeo a partir de imagem. URL ou URI data:.
image_urlsstring[]Várias imagens de origem (Veo). Tem prioridade sobre image_url.
image_modestringVeo: "frame" (padrão — primeiro/último quadro) ou "reference" (referências de estilo).
durationintDuração do clipe em segundos nos modelos cobrados por segundo, 5 quando omitida: o Seedance 2, o Seedance 2 Fast e o Seedance 2.0 Mini aceitam 4–15, o Seedance 2.5 aceita 4–30 e o Wan 2.7 aceita 2–15. Uma duração fora do intervalo de um modelo é rejeitada com um erro 400. O Veo ignora esse valor: cada clipe do Veo tem 8 segundos.
aspect_ratiostringPor exemplo: "16:9", "9:16", "1:1".
resolutionstringQuando o modelo disponibiliza opções — por exemplo, "720p", "1080p".
inputobjectOpção de escape — veja abaixo Raw input.

Opção de escape para entrada bruta

Nos modelos cobrados por segundo, input é enviado ao provedor upstream exatamente como foi escrito, no lugar da entrada que nosso adaptador criaria. Os modelos Veo o ignoram.

Os campos que definem o preço são a exceção: resolution é sempre enviado como um dos níveis do modelo (o nível padrão quando é omitido ou não corresponde a nenhum deles), e duration deve ser um número inteiro de segundos dentro do intervalo do modelo (qualquer outro valor é rejeitado com um erro 400; 5 é enviado quando é omitido). resolution e duration da própria solicitação não são lidos quando input está definido.

Modelos de vídeo

SlugProvedorObservações
veo-3GoogleVeo 3.1 Fast — mais rápido e barato que o Quality, com áudio nativo.
veo-3-qualityGoogleVeo 3.1 Quality — máxima fidelidade.
veo-3-liteGoogleVeo 3.1 Lite — opção econômica para prévias.
seedance-2-5ByteDanceSeedance 2.5 — clipes de até 30s, referências multimodais, cobrança por segundo.
seedance-2ByteDanceSeedance 2 — consistência entre várias tomadas, até 1080p, cobrança por segundo.
seedance-2-fastByteDanceSeedance 2 Fast — opção econômica, até 720p, cobrança por segundo.
seedance-2-miniByteDanceSeedance 2.0 Mini — opção mais econômica, apenas 480p/720p, para rascunhos e alto volume.
wan-2-7AlibabaWan 2.7 — geração cinematográfica de vídeos a partir de texto, com cobrança por segundo.

Consulte sempre /models para ver a lista atualizada e os preços por modelo.

Latência e tempos limite

  • A Kunavo consulta a tarefa upstream por até 540 segundos antes de retornar um erro 504.
  • Opções Fast (Veo 3.1 Fast / Lite): geralmente de 30 a 90s.
  • Opções Quality e durações maiores: de 2 a 5 minutos.
  • Defina o timeout do cliente HTTP como pelo menos 600s.
  • Em redes instáveis ou clientes móveis, prefira a API assíncrona — o envio retorna em segundos e você consulta o resultado.

API assíncrona (envio + consulta)

Endpoints: POST /v1/videos + GET /v1/videos/{id}. O objeto de tarefa retornado por eles usa os nomes de campos e os valores de status do objeto de vídeo da OpenAI e adiciona output com a URL do clipe. A solicitação é um corpo JSON com os parâmetros acima.

O endpoint síncrono acima mantém uma conexão HTTP aberta por até nove minutos — é conveniente para scripts de chamada única, mas pouco confiável em dispositivos móveis e ineficiente no servidor. O par assíncrono retorna imediatamente um ID vid_*; consulte GET /v1/videos/{id} até que status seja completed ou failed. Os modelos e preços são os mesmos, assim como as URLs arquivadas.

import requests, time

# 1. Submit — returns immediately with a task id. No long-lived connection.
submit = requests.post(
    "https://api.kunavo.com/v1/videos",
    headers={
        "Authorization": f"Bearer {API_KEY}",
        # Optional: retrying with the same key inside ~24h returns the
        # original task rather than submitting again.
        "Idempotency-Key": "my-task-uuid",
    },
    json={
        "model": "veo-3",
        "prompt": "a cinematic dolly-in on a red origami crane",
        "aspect_ratio": "16:9",
    },
    timeout=60,
).json()

task_id = submit["id"]              # "vid_abc..."
status  = submit["status"]          # "queued"

# 2. Poll until terminal. Recommended cadence: 5s, backing off to 30s.
while status not in ("completed", "failed"):
    time.sleep(5)
    r = requests.get(
        f"https://api.kunavo.com/v1/videos/{task_id}",
        headers={"Authorization": f"Bearer {API_KEY}"},
        timeout=30,
    ).json()
    status = r["status"]

if status == "failed":
    raise RuntimeError(r["error"]["message"])
print(r["output"]["url"])

No SDK da OpenAI, envie esse corpo com client.post e consulte o status com client.get, como mostrado abaixo. O próprio client.videos.create do SDK não funciona aqui: ele aceita apenas os parâmetros do Sora e os envia como um formulário multipart (no SDK Python desde a versão 2.27.0), o que este endpoint rejeita com um erro 400.

import time
from openai import OpenAI

client = OpenAI(api_key=API_KEY, base_url="https://api.kunavo.com/v1")

# The same JSON body, sent with the client's own post().
task = client.post(
    "/videos",
    body={
        "model": "veo-3",
        "prompt": "a cinematic dolly-in on a red origami crane",
        "aspect_ratio": "16:9",
    },
    # object hands back the JSON as parsed. Passing dict instead raises
    # after the task has been created.
    cast_to=object,
    options={"headers": {"Idempotency-Key": "my-task-uuid"}},
)

while task["status"] not in ("completed", "failed"):
    time.sleep(5)
    task = client.get(f"/videos/{task['id']}", cast_to=object)

if task["status"] == "failed":
    raise RuntimeError(task["error"]["message"])
print(task["output"]["url"])

Formato da resposta

CampoTipoObservações
idstringID da tarefa com o prefixo vid_-. Estável mesmo quando o upstream muda.
objectstring"video".
statusstringqueued | in_progress | completed | failed.
modelstringO slug do modelo enviado.
created_atintSegundos Unix em que aceitamos o envio.
completed_atint | nullSegundos Unix em que a tarefa terminou — null enquanto estiver pendente.
expires_atintSegundos Unix, 30 dias após o envio: a tarefa pode ser consultada pelo menos até lá. As URLs dos resultados não expiram.
progressint0–100. Atualmente, informamos 0 até o estado terminal e, então, 100.
outputobject | null{url, urls[], duration_seconds, resolution} quando concluído. duration_seconds é a duração do clipe solicitada ao modelo, null no Veo; resolution é o nível de resolução do clipe, ou null quando nenhum foi registrado.
errorobject | null{code, message} quando falha.

Cabeçalhos e convenções

  • Idempotency-Key (opcional, até 128 caracteres) — enviar duas vezes a mesma chave na mesma conta retorna a tarefa original, em vez de criar uma duplicada. Útil para ciclos de repetição.
  • POST /v1/videos retorna 202 Accepted em um novo envio e 200 OK quando uma chave de idempotência encontra e reproduz um resultado anterior.
  • GET /v1/videos/{id} é restrito ao proprietário: consultar uma tarefa pertencente a outra conta retorna 404 — nunca confirmamos a existência de um ID de outra pessoa.
  • Cadência de consulta recomendada: 5 s, com backoff exponencial até 30 s. As tarefas geralmente são concluídas em 30 s a 5 min, dependendo do modelo e da resolução.

Quando usar cada um

Síncrono /v1/video/generationsAssíncrono /v1/videos
Melhor paraScripts e notebooks de uso pontualProdução, dispositivos móveis, tarefas de longa duração
RedeMantém a conexão aberta por até 9 minEnvie em segundos; consulte no seu ritmo
Recuperação de falhasPerder a resposta = perder a tarefaConsulte o ID novamente a qualquer momento antes de expires_at
ConcorrênciaUm worker do servidor por chamadaEnvia e libera imediatamente
Formato da respostaFormato de /images/generations da OpenAIFormato de /videos da OpenAI

Resposta e armazenamento

A resposta segue o formato padrão da OpenAI — data[].url. A URL é um link permanente files.kunavo.com: a Kunavo arquiva todos os resultados na própria CDN, então, ao contrário das URLs upstream diretas, ele nunca expira. Todos os clipes também aparecem em /app/assets.

Próximos passos