API REST|API de Legendas de Vídeo

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.generate
Início rápido

O 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.

Passo a passo

O que tem mesmo de fazer

Seis passos, quatro deles um único pedido.

  1. 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. 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. 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. 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. 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. 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.
Referência

Todas as chamadas desta página

Os scopes são o que a chave tem de levar, não o que você envia.

ChamadaScopeO que faz
GET /api/v1-Descoberta: o que esta chave pode fazer, os seus limites e o seu consumo.
POST /api/v1/actions/videos.createvideos:writeCria o projeto de vídeo e devolve o identificador.
POST /api/v1/actions/subtitles.generatevideos:executeTranscreve, destaca, aplica estilo e guarda a faixa de legendas.
POST /api/v1/actions/renders.createrenders:executePõe o render na frota de workers. 20 créditos.
GET /api/v1/renders?video_id=…renders:readOs renders de um vídeo, do mais recente, com estado e URL.
POST /api/v1/webhookswebhooks:writeRegista 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.

makeitlookeasy
default4 palavras · simple
makeitloud
bold3 palavras · simple
thisoneword
impact3 palavras · focus_on_one_word
huge
beast1 palavras · focus_on_one_word
onewordatatime
karaoke5 palavras · focus_on_one_word
itfillsasyousing
karaoke_fill5 palavras · karaoke
lightsonthewords
neon4 palavras · progressively_visible
nothingbehindthetextatall
minimal6 palavras · simple
wordbywordtheyland
pop5 palavras · progressively_visible
darktextonapill
pill5 palavras · simple
twocalmlinesatthebottomoftheframe
cinematic9 palavras · simple

A 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ódigoO que significa
401 missing_credentialsSem chave no pedido, ou uma que não reconhecemos.
403 insufficient_scopeFalta um scope à chave; `details.required_scopes` nomeia-o.
422 validation_failedUm campo está inválido; `details.issues` lista-os todos.
402 insufficient_creditsCréditos insuficientes para o render. Carregue, ou use uma chave de teste.
429 rate_limit_exceededChamadas a mais. Espere `Retry-After` segundos e tente de novo.
502 upstream_errorUm 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"
  }
}

Códigos de erro

Webhooks

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')
})
O que recebe

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ã.

FAQ

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.

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.

Pagamentos seguros
Acesso instantâneo
Cancele quando quiser