Vídeo
Geração de vídeo é assíncrona. Diferente dos outros endpoints, uma chamada não te dá um resultado direto: você enfileira um job, consulta seu progresso, e libera a mídia armazenada quando termina.
Ver o prompt
Me ajude a configurar isto para usar a Bayle AI, uma API compatível com a OpenAI. Guia completo: https://docs.bayle.ai/pt-br/guides/video/
Objetivo: gerar um vídeo pela Bayle a partir deste projeto: cotar, enfileirar, consultar, salvar, liberar.
1. Escolha um modelo de vídeo: curl -s "https://api.bayle.ai/models?type=video" | jq -r '.data[].id'
Me mostre a lista e deixe eu escolher o VIDEO_MODEL_ID.
2. Cote o job primeiro. Esta chamada não precisa de key:
curl https://api.bayle.ai/video/quote -H "Content-Type: application/json" -d '{"model": "VIDEO_MODEL_ID", "duration": "5s", "resolution": "720p", "audio": false}'
Me mostre o preço e espere meu aval antes de gastar qualquer coisa.
3. Enfileire: POST https://api.bayle.ai/video/queue com uma key bearer e {"model": "VIDEO_MODEL_ID", "prompt": "A paper boat drifting down a rain gutter.", "duration": "5s"}. Guarde o `queue_id` devolvido. Para imagem para vídeo, adicione `image_url` (uma URL https pública ou um data: URI).
4. Um 409 com o código needs_consent significa que o modelo exige uma atestação de consentimento para rostos. Nada foi cobrado. Me mostre `consent.policy_text` e só reenvie o corpo idêntico com o objeto `consents` (os três booleanos true) se eu confirmar que a pessoa na mídia consentiu.
5. Consulte POST /video/retrieve com {"model": "VIDEO_MODEL_ID", "queue_id": "QUEUE_ID"} a cada uns 10 segundos. Um 200 é JSON (ainda rodando: `status`, `execution_duration`) ou `video/mp4` (pronto). Ramifique pelo Content-Type da resposta, nunca por um campo do corpo. Alguns modelos devolvem no JSON uma `download_url` pré-assinada: baixe-a direto, sem credencial. Trate os dois casos e salve o vídeo como out.mp4.
6. Libere a mídia armazenada: POST /video/complete com o mesmo model e queue_id. Um 400 informando que o job não foi encontrado é esperado depois de um modelo que devolveu os bytes inline; não trate como falha.
7. Teste: rode o fluxo inteiro uma vez com a menor duração e me mostre o tamanho do out.mp4.
Regras:
1. A base URL é https://api.bayle.ai exatamente como está escrita. Não acrescente nenhum caminho a ela.
2. Leia a key da variável de ambiente BAYLE_API_KEY. Nunca escreva a key em um arquivo que você criar, em um comando, em um commit nem no chat. Onde a ferramenta guarda a key na própria tela de configurações, me diga onde colar. Se BAYLE_API_KEY não estiver definida, pare e peça que eu crie uma key em https://chat.bayle.ai/developers e a exporte.
3. Pegue os ids de modelo só de GET https://api.bayle.ai/models (sem key). Nunca chute um.
4. Termine com uma requisição de teste de verdade, me mostre o resultado e liste os arquivos que você alterou.Quatro endpoints, em ordem:
| Endpoint | Finalidade | Key exigida |
|---|---|---|
POST /video/quote |
Cotar um job antes de confirmá-lo | Não |
POST /video/queue |
Iniciar o job, obter um queue_id |
Sim |
POST /video/retrieve |
Consultar o progresso, depois obter o vídeo | Sim |
POST /video/complete |
Liberar a mídia armazenada | Sim |
O fluxo completo
Seção intitulada “O fluxo completo”-
Cote, opcionalmente
Seção intitulada “Cote, opcionalmente”Nenhuma credencial necessária, então você pode cotar um job antes de ter uma.
Janela do terminal curl https://api.bayle.ai/video/quote \-H "Content-Type: application/json" \-d '{"model": "VIDEO_MODEL_ID", "duration": "5s", "resolution": "720p", "audio": false}'O preço decimal é retornado exatamente como calculado, nunca decodificado e recodificado, então é seguro compará-lo ou armazená-lo ao pé da letra.
-
Enfileire o job
Seção intitulada “Enfileire o job”Janela do terminal curl https://api.bayle.ai/video/queue \-H "Authorization: Bearer $BAYLE_API_KEY" \-H "Content-Type: application/json" \-d '{"model": "VIDEO_MODEL_ID","prompt": "A paper boat drifting down a rain gutter.","duration": "5s"}'Você recebe um
queue_idde volta. Guarde-o: as próximas duas chamadas precisam dele.Para image-to-video, adicione
image_url, uma URL https pública ou umadata:URI.Se isso retornar
409, veja Consentimento abaixo. -
Consulte até terminar
Seção intitulada “Consulte até terminar”Consulte a cada 10 segundos, aproximadamente. Geração de vídeo demora consideravelmente mais que os endpoints síncronos, então não espere que a primeira chamada já esteja pronta.
Janela do terminal curl https://api.bayle.ai/video/retrieve \-H "Authorization: Bearer $BAYLE_API_KEY" \-H "Content-Type: application/json" \-d '{"model": "VIDEO_MODEL_ID", "queue_id": "QUEUE_ID"}'Esse endpoint retorna uma de duas coisas completamente diferentes sob o mesmo status
200, e qual delas você recebe depende de o job ter terminado ou não. Veja Trate pelo Content-Type. -
Libere a mídia armazenada
Seção intitulada “Libere a mídia armazenada”Janela do terminal curl https://api.bayle.ai/video/complete \-H "Authorization: Bearer $BAYLE_API_KEY" \-H "Content-Type: application/json" \-d '{"model": "VIDEO_MODEL_ID", "queue_id": "QUEUE_ID"}'A Bayle não mantém registro de um
queue_id, então chamar isso duas vezes é respondido pelo seu próprio mérito em vez de interrompido antecipadamente.
Trate pelo Content-Type
Seção intitulada “Trate pelo Content-Type”POST /video/retrieve retorna um documento JSON de status ou o vídeo bruto, ambos com 200.
Trate pelo Content-Type da resposta, não por um campo do corpo. Fazer parse da resposta como
JSON incondicionalmente vai falhar no vídeo.
| Content-Type | Significado |
|---|---|
application/json |
Ainda rodando. Carrega status, average_execution_time, execution_duration. |
video/mp4 |
Terminado. O corpo é o vídeo. |
import time, httpx, pathlib
body = {"model": "VIDEO_MODEL_ID", "queue_id": queue_id}headers = {"Authorization": f"Bearer {api_key}"}
while True: res = httpx.post("https://api.bayle.ai/video/retrieve", json=body, headers=headers, timeout=120) res.raise_for_status()
if res.headers["content-type"].startswith("video/"): pathlib.Path("out.mp4").write_bytes(res.content) break
status = res.json() # Some models put a download_url here instead of returning the bytes above. if url := status.get("download_url"): pathlib.Path("out.mp4").write_bytes(httpx.get(url, timeout=300).content) break
print(status.get("status"), status.get("execution_duration")) time.sleep(10)import { writeFile } from 'node:fs/promises'
const body = JSON.stringify({ model: 'VIDEO_MODEL_ID', queue_id: queueId })const headers = { Authorization: `Bearer ${apiKey}`, 'Content-Type': 'application/json' }
for (;;) { const res = await fetch('https://api.bayle.ai/video/retrieve', { method: 'POST', headers, body }) if (!res.ok) throw new Error(`retrieve failed: ${res.status}`)
if (res.headers.get('content-type')?.startsWith('video/')) { await writeFile('out.mp4', Buffer.from(await res.arrayBuffer())) break }
const status = await res.json() if (status.download_url) { const file = await fetch(status.download_url) await writeFile('out.mp4', Buffer.from(await file.arrayBuffer())) break }
console.log(status.status, status.execution_duration) await new Promise((r) => setTimeout(r, 10_000))}A Bayle não envia header de retry-delay e não impõe uma cadência de consulta. A cifra de 10 segundos é uma orientação para você, não um sinal a ser interpretado.
Sobre o download_url
Seção intitulada “Sobre o download_url”Alguns modelos entregam o vídeo finalizado através de um download_url no documento de status em
vez de como bytes vindos de retrieve. É uma URL pré-assinada que seu próprio client busca
diretamente: ela não exige credencial, e não passa de volta pela Bayle. Trate as duas formas, como
os exemplos acima fazem.
Consentimento
Seção intitulada “Consentimento”Alguns modelos exigem um atestado de consentimento antes de gerar mídia contendo um rosto. A
primeira chamada a queue volta 409, e nada é cobrado:
{ "error": { "code": "needs_consent", "message": "..." }, "consent_flow": "...", "face_media_roles": ["..."], "consent": { "consent_version": "...", "policy_text": "..." }, "docs_url": "https://docs.bayle.ai"}consent.policy_text é o texto a ser mostrado para a pessoa cuja imagem aparece na mídia, e
face_media_roles indica quais partes da sua requisição precisam dessa confirmação.
Para prosseguir, reenvie o corpo da requisição idêntico ao mesmo endpoint com um objeto
consents adicionado. Os três booleanos precisam ser literalmente true:
{ "model": "VIDEO_MODEL_ID", "prompt": "A paper boat drifting down a rain gutter.", "duration": "5s", "consents": { "seedance": { "confirmed_terms_and_privacy": true, "confirmed_legal_right": true, "confirmed_screening_acknowledged": true } }}Não existe um token de consentimento para carregar, nenhuma versão para você enviar de volta, e
nenhum endpoint separado para a nova tentativa. É a mesma chamada POST /video/queue com mais um
campo. A chave dentro de consents nomeia a família do modelo, que o corpo do 409 informa.
Só ateste em nome de alguém que realmente deu consentimento. É para isso que a confirmação existe.