Legende qualquer vídeo
com três chamadas de API
Envie a URL de um vídeo e receba um mp4 com legendas gravadas palavra a palavra. O Whisper sincroniza cada palavra, uma passagem de IA escolhe as que ficam em destaque e a renderização corre na nossa frota de workers.
POSThttps://autostud.ai/api/v1/actions/subtitles.generateO percurso todo, num único ficheiro
Cole, ponha a sua chave, execute. Nada disto é pseudocódigo.
const API = 'https://autostud.ai/api/v1'
const KEY = process.env.AUTOSTUD_API_KEY
const call = async (path, body) => {
const response = await fetch(API + path, {
method: body ? 'POST' : 'GET',
headers: {
Authorization: `Bearer ${KEY}`,
'Content-Type': 'application/json',
},
body: body ? JSON.stringify(body) : undefined,
})
const payload = await response.json()
if (!response.ok) throw new Error(payload.error?.message || response.statusText)
return payload
}
// 1. A video project to hang the timeline on.
const created = await call('/actions/videos.create', {
video_name: 'Interview clip',
video_type_id: 'timeline',
video_format: 'portrait',
video_lang: 'en',
})
const video_id = created.data.video_id
// 2. Transcribe, emphasise, style — one call.
const subtitled = await call('/actions/subtitles.generate', {
video_id,
media_url: 'https://cdn.example.com/interview.mp4',
style_preset: 'beast',
words_per_group: 3,
})
console.log(subtitled.data.word_count, 'words', subtitled.data.duration_seconds, 's')
// 3. Render it.
await call('/actions/renders.create', {
video_id,
video_type_id: 'timeline',
video_format: 'portrait',
})
// 4. Wait for the worker. A webhook is better; this is the short version.
let render
do {
await new Promise((resolve) => setTimeout(resolve, 15000))
const list = await call(`/renders?video_id=${video_id}&limit=1`)
render = list.data[0]
} while (render && ['not_started', 'processing'].includes(render.render_status))
if (render.render_status !== 'done') throw new Error('Render failed')
console.log(render.render_url)URL base: https://autostud.ai/api/v1 — autentique com `Authorization: Bearer sk_live_…`. Uma chave `sk_test_` percorre exatamente o mesmo caminho e para antes de gastar.
O que tem mesmo de fazer
Seis passos, quatro deles um único pedido.
- 1
Criar uma chave de API
Painel, Definições, Chaves de API. Escolha a predefinição «Automation» se não quiser selecionar scopes à mão: cobre vídeos, renders e ficheiros. O segredo é mostrado uma vez. `sk_live_` gasta, `sk_test_` valida o pedido todo e para antes da escrita.
export AUTOSTUD_API_KEY="sk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" # What can this key actually do? curl https://autostud.ai/api/v1 \ -H "Authorization: Bearer $AUTOSTUD_API_KEY" - 2
Pôr o vídeo atrás de um URL
Não há endpoint de upload: a API lê um URL. O seu bucket, a sua CDN, um link assinado — tudo o que os nossos servidores conseguirem descarregar. Registá-lo na biblioteca do espaço é opcional e não muda o fluxo.
# The API takes a URL, never a file upload. Anything publicly # reachable works: your bucket, your CDN, a signed URL. export MEDIA_URL="https://cdn.example.com/interview.mp4" # Optional: keep a record of it in the workspace library. curl -X POST https://autostud.ai/api/v1/assets \ -H "Authorization: Bearer $AUTOSTUD_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "provider_file_url": "'"$MEDIA_URL"'", "provider_file_name": "interview.mp4", "file_mime_type": "video/mp4", "provider_file_id": "interview-2026-08-18", "sync_method": "api" }' - 3
Criar o projeto de vídeo
`videos.create` devolve o `video_id` a que tudo o resto se liga. Envie um `Idempotency-Key` e um pedido repetido devolve a primeira resposta em vez de criar um segundo projeto.
curl -X POST https://autostud.ai/api/v1/actions/videos.create \ -H "Authorization: Bearer $AUTOSTUD_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: interview-2026-08-18" \ -d '{ "video_name": "Interview clip", "video_type_id": "timeline", "video_format": "portrait", "video_lang": "en" }' - 4
Legendar
`subtitles.generate` faz o trabalho todo no servidor: o Whisper transcreve com tempos por palavra, uma passagem de IA marca as palavras a destacar e um dos 116 estilos é aplicado. Responde com o número de palavras, a duração exata e o idioma detetado.
export VIDEO_ID="9f0c4e2a-1d6b-4a77-9d51-6b0f2e8c3a4d" curl -X POST https://autostud.ai/api/v1/actions/subtitles.generate \ -H "Authorization: Bearer $AUTOSTUD_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "video_id": "'"$VIDEO_ID"'", "media_url": "'"$MEDIA_URL"'", "style_preset": "beast", "words_per_group": 3, "enable_emphasis": true, "enable_diarization": false }' - 5
Pôr o render na fila
`renders.create` coloca o trabalho na frota de workers e responde de imediato com o `render_id`. Um render custa 20 créditos, seja qual for a duração.
curl -X POST https://autostud.ai/api/v1/actions/renders.create \ -H "Authorization: Bearer $AUTOSTUD_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "video_id": "'"$VIDEO_ID"'", "video_type_id": "timeline", "video_format": "portrait" }' - 6
Recolher o mp4
Subscreva `render.completed` e o URL final chega-lhe assinado. Se preferir consultar, liste os renders do vídeo e leia `render_status` até dizer `done`.
# Register once, then stop polling. curl -X POST https://autostud.ai/api/v1/webhooks \ -H "Authorization: Bearer $AUTOSTUD_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://your-app.com/hooks/autostud", "events": ["render.completed", "render.failed"], "description": "Subtitled videos" }' # The signing secret comes back once, in this response. Store it.
Todas as chamadas desta página
Os scopes são o que a chave tem de levar, não o que você envia.
| Chamada | Scope | O que faz |
|---|---|---|
| GET /api/v1 | - | Descoberta: o que esta chave pode fazer, os seus limites e o seu consumo. |
| POST /api/v1/actions/videos.create | videos:write | Cria o projeto de vídeo e devolve o identificador. |
| POST /api/v1/actions/subtitles.generate | videos:execute | Transcreve, destaca, aplica estilo e guarda a faixa de legendas. |
| POST /api/v1/actions/renders.create | renders:execute | Põe o render na frota de workers. 20 créditos. |
| GET /api/v1/renders?video_id=… | renders:read | Os renders de um vídeo, do mais recente, com estado e URL. |
| POST /api/v1/webhooks | webhooks:write | Regista um endpoint https e devolve o segredo de assinatura, uma vez. |
Estilos disponíveis
Passe um em `style_preset`. Cada um define de uma vez a tipografia, o contorno, a cor de destaque e a animação — não há mais nada para configurar.
default4 palavras · simplebold3 palavras · simpleimpact3 palavras · focus_on_one_wordbeast1 palavras · focus_on_one_wordkaraoke5 palavras · focus_on_one_wordkaraoke_fill5 palavras · karaokeneon4 palavras · progressively_visibleminimal6 palavras · simplepop5 palavras · progressively_visiblepill5 palavras · simplecinematic9 palavras · simpleA saber antes de programar
- Os limites de pedidos são por chave e vêm nos cabeçalhos: `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `Retry-After` num 429.
- `Idempotency-Key` em qualquer POST torna uma repetição segura durante 24 horas; a primeira resposta é devolvida tal e qual.
- Uma chave `sk_test_` exercita autenticação, scopes, limites e validação e depois responde `simulated: true` sem escrever nem gastar.
- Os erros são tipados: `invalid_request`, `insufficient_credits`, `rate_limit_exceeded`, `upstream_error` — ramifique pelo código, nunca pela mensagem.
Quando falha
Todos os erros têm a mesma forma: um `code` estável para ramificar, um `hint` que nomeia a chamada que corrige e um `request_id` para citar. O vocabulário completo está em /docs/api/errors.
| Código | O que significa |
|---|---|
| 401 missing_credentials | Sem chave no pedido, ou uma que não reconhecemos. |
| 403 insufficient_scope | Falta um scope à chave; `details.required_scopes` nomeia-o. |
| 422 validation_failed | Um campo está inválido; `details.issues` lista-os todos. |
| 402 insufficient_credits | Créditos insuficientes para o render. Carregue, ou use uma chave de teste. |
| 429 rate_limit_exceeded | Chamadas a mais. Espere `Retry-After` segundos e tente de novo. |
| 502 upstream_error | Um fornecedor de que dependemos falhou. Tente de novo com backoff. |
{
"error": {
"type": "permission_error",
"code": "insufficient_scope",
"message": "This API key is missing the required scope: videos:execute.",
"details": {
"required_scopes": ["videos:execute"],
"granted_scopes": ["videos:read", "videos:write"]
},
"hint": "`details.required_scopes` lists what is missing. Call GET /api/v1 to see what this key does hold, then re-scope it at /app/settings/api-keys.",
"retryable": false,
"request_id": "req_8f2c41d0a95b",
"doc_url": "https://autostud.ai/docs/api/errors#insufficient_scope"
}
}Pare de fazer polling
Um render demora minutos. Deixe-o chegar até si.
Assinados e verificáveis em dez linhas
`X-Autostud-Signature: t=<unix>,v1=<hex>` é um HMAC-SHA256 sobre `"<timestamp>.<corpo cru>"`. Verifique contra o corpo cru, antes de o interpretar.
Repetidos por si
Cinco tentativas ao longo de cerca de duas horas, com a linha de entrega escrita antes da primeira. Um endpoint que falha vinte vezes seguidas é desativado em vez de martelado.
Deduplique pelo id de entrega
Cada tentativa traz `X-Autostud-Delivery`. Mesmo id, mesmo evento: trate-o como a chave primária do trabalho que desencadeia.
import crypto from 'node:crypto'
// The header is "t=<unix>,v1=<hex>" and the signed string is "<t>.<raw body>".
// Raw body: parse it AFTER verifying, never re-serialize before.
export function verify(raw_body, header, secret, tolerance = 300) {
const parts = Object.fromEntries(
String(header || '').split(',').map((entry) => entry.split('=').map((s) => s.trim()))
)
const timestamp = Number(parts.t)
if (!Number.isFinite(timestamp)) return false
if (Math.abs(Date.now() / 1000 - timestamp) > tolerance) return false
const expected = crypto
.createHmac('sha256', secret)
.update(`${timestamp}.${raw_body}`)
.digest('hex')
const received = String(parts.v1 || '')
if (received.length !== expected.length) return false
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(received))
}
app.post('/hooks/autostud', express.raw({ type: '*/*' }), (req, res) => {
const raw = req.body.toString('utf8')
if (!verify(raw, req.get('X-Autostud-Signature'), process.env.AUTOSTUD_WEBHOOK_SECRET)) {
return res.status(400).send('bad signature')
}
// Deliveries can repeat: deduplicate on this id before doing any work.
const delivery_id = req.get('X-Autostud-Delivery')
const event = JSON.parse(raw)
if (event.event === 'render.completed') {
console.log(event.data.render_url)
}
res.status(200).send('ok')
})Porque é uma chamada e não quinze
As partes difíceis de refazer são as que nós operamos.
Sincronia palavra a palavra
Cada palavra tem início e fim próprios, por isso o destaque cai na sílaba. A sincronia por frase é o que faz a maioria das legendas automáticas parecer atrasada.
116 estilos, uma string
Da legenda sóbria de televisão ao estilo saltitante destacado. A predefinição acerta tipografia, contorno, cor e animação em conjunto, à escala da sua tela.
A IA escolhe os acentos
Uma segunda passagem lê a transcrição e marca as palavras que carregam o sentido. As palavras de enchimento ficam quietas. Um booleano desliga isto.
Duas vozes, duas cores
A diarização é uma flag. Uma entrevista volta com cada voz na sua cor, e quem vê sabe quem fala sem som.
Os mesmos objetos do painel
Um vídeo legendado pela API abre no editor como qualquer outro. Corrija uma palavra à mão, volte a renderizar, continue a automatizar: os dois caminhos escrevem o mesmo documento.
Feita para ser repetida
Chaves de idempotência, erros tipados, chaves de teste, webhooks assinados e um registo de pedidos de 30 dias. A metade aborrecida de uma API, que é a que se sente às 3 da manhã.
As perguntas que os programadores nos enviam mesmo
Não, e é de propósito: a API recebe um URL. Aloje o vídeo onde os nossos servidores o consigam ler — o seu bucket, uma CDN, um link assinado — e passe-o em `media_url`. Os ficheiros enviados pelo painel já têm um URL utilizável.
Hoje não. As legendas são gravadas no mp4, que é o que as plataformas de formato curto precisam. A lista de palavras com os tempos fica guardada no vídeo, por isso um ficheiro à parte é uma transformação que pode fazer do seu lado.
Um render são 20 créditos, fixos, dure o que durar. A transcrição e a passagem de ênfase ficam registadas no seu espaço como qualquer outra chamada de IA, e `GET /api/v1/usage` diz o que uma chave gastou no período.
Minutos, não segundos: corre numa frota de workers, não dentro do pedido. É exatamente para isso que existe `render.completed` — subscreva em vez de manter uma ligação aberta.
Sim. Chame `subtitles.generate` outra vez com `restyle_only: true` e outro `style_preset`: reutiliza as palavras já guardadas no vídeo, nada é transcrito duas vezes.
O Whisper deteta o idioma sozinho e as legendas voltam no que está a ser falado — nada a declarar. O idioma detetado vem na resposta, caso queira ramificar.
Mais ferramentas grátis
Todas funcionam no navegador, sem conta para experimentar.
Gerador de Vídeo Quiz TikTok
Ferramenta ao vivoTransforme qualquer tema num vídeo quiz
Gerador de Vídeo Quiz YouTube
Ferramenta ao vivoVídeos quiz em 16:9 ou em Shorts
Gerador de Vídeo Quiz de Idiomas
Ferramenta ao vivoTransforme uma lista de palavras num vídeo de idiomas
Tweet para Vídeo
Ferramenta ao vivoTransforme tweets em vídeos
YouTube para Vídeo Karaokê
Ferramenta ao vivoTransforme uma faixa em vídeo karaokê
Post do Reddit para Vídeo
Ferramenta ao vivoTransforme uma thread do Reddit em short
CSV para Vídeo Ranking
Ferramenta ao vivoTransforme uma planilha num vídeo de ranking
Comentários TikTok em Vídeo Música
Ferramenta ao vivoTransforme os comentários do TikTok num vídeo música
Vídeos IA Frutas
Crie vídeos de frutas virais com IA
Publique vídeo legendado a partir do seu próprio backend
Crie uma chave, corra o quickstart, e o primeiro mp4 legendado está a minutos.