API REST|API de Subtítulos de Vídeo

Subtitula cualquier vídeo
con tres llamadas API

Envía la URL de un vídeo y recibe un mp4 con subtítulos incrustados palabra por palabra. Whisper ajusta cada palabra, una pasada de IA elige las que se resaltan y el render corre en nuestra flota de workers.

POSThttps://autostud.ai/api/v1/actions/subtitles.generate
Inicio rápido

Todo el recorrido, en un solo archivo

Pégalo, pon tu clave, ejecútalo. Aquí no hay 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 — autentica con `Authorization: Bearer sk_live_…`. Una clave `sk_test_` recorre exactamente el mismo camino y se detiene antes de gastar nada.

Paso a paso

Lo que realmente tienes que hacer

Seis pasos, cuatro de ellos una sola petición.

  1. 1

    Crear una clave API

    Panel, Ajustes, Claves API. Elige el preajuste «Automation» si no quieres seleccionar scopes a mano: cubre vídeos, renders y archivos. El secreto se muestra una vez. `sk_live_` gasta, `sk_test_` valida toda la petición y se detiene antes de escribir.

    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

    Poner el vídeo tras una URL

    No hay endpoint de subida: la API lee una URL. Tu bucket, tu CDN, un enlace firmado — cualquier cosa que nuestros servidores puedan descargar. Registrarlo en la biblioteca del espacio es opcional y no cambia el flujo.

    # 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

    Crear el proyecto de vídeo

    `videos.create` devuelve el `video_id` del que cuelga todo lo demás. Envía un `Idempotency-Key` y una petición reintentada devuelve la primera respuesta en lugar de crear un segundo proyecto.

    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

    Subtitularlo

    `subtitles.generate` hace todo el trabajo en el servidor: Whisper transcribe con tiempos por palabra, una pasada de IA marca las palabras a resaltar y se aplica uno de los 116 estilos. Responde con el número de palabras, la duración exacta y el idioma detectado.

    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

    Encolar el render

    `renders.create` pone el trabajo en la flota de workers y responde al momento con el `render_id`. Un render cuesta 20 créditos, sea cual sea la duración.

    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

    Recoger el mp4

    Suscríbete a `render.completed` y la URL final llega firmada. Si prefieres consultar tú, lista los renders del vídeo y lee `render_status` hasta que diga `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.
Referencia

Todas las llamadas de esta página

Los scopes son lo que debe llevar la clave, no lo que envías.

LlamadaScopeQué hace
GET /api/v1-Descubrimiento: qué puede hacer la clave, sus límites y su consumo.
POST /api/v1/actions/videos.createvideos:writeCrea el proyecto de vídeo y devuelve su identificador.
POST /api/v1/actions/subtitles.generatevideos:executeTranscribe, resalta, aplica estilo y guarda la pista de subtítulos.
POST /api/v1/actions/renders.createrenders:executeEncola el render en la flota de workers. 20 créditos.
GET /api/v1/renders?video_id=…renders:readLos renders de un vídeo, del más reciente al más antiguo, con estado y URL.
POST /api/v1/webhookswebhooks:writeRegistra un endpoint https y devuelve su secreto de firma, una vez.

Estilos disponibles

Pasa uno en `style_preset`. Cada uno fija a la vez la tipografía, el contorno, el color de acento y la animación — no hay nada más que configurar.

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

Lo que conviene saber antes de programar

  • Los límites de peticiones son por clave y vienen en cabeceras: `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `Retry-After` en un 429.
  • `Idempotency-Key` en cualquier POST hace seguro un reintento durante 24 horas; la primera respuesta se repite tal cual.
  • Una clave `sk_test_` ejercita autenticación, scopes, límites y validación, y luego responde `simulated: true` sin escribir ni gastar.
  • Los errores son tipados: `invalid_request`, `insufficient_credits`, `rate_limit_exceeded`, `upstream_error` — ramifica por el código, nunca por el mensaje.

Cuando falla

Todos los fallos tienen la misma forma: un `code` estable sobre el que ramificar, un `hint` que nombra la llamada que lo arregla y un `request_id` que citar. El vocabulario completo está en /docs/api/errors.

CódigoQué significa
401 missing_credentialsNo hay clave en la petición, o no la reconocemos.
403 insufficient_scopeA la clave le falta un scope; `details.required_scopes` lo nombra.
422 validation_failedAlgún campo es inválido; `details.issues` los lista todos.
402 insufficient_creditsNo hay créditos suficientes para el render. Recarga, o usa una clave de prueba.
429 rate_limit_exceededDemasiadas llamadas. Espera `Retry-After` segundos y reintenta.
502 upstream_errorUn proveedor del que dependemos ha fallado. Reintenta con 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 error

Webhooks

Deja de hacer polling

Un render tarda minutos. Deja que llegue solo.

Firmados y verificables en diez líneas

`X-Autostud-Signature: t=<unix>,v1=<hex>` es un HMAC-SHA256 sobre `"<timestamp>.<cuerpo crudo>"`. Verifica contra el cuerpo crudo, antes de parsearlo.

Reintentados por ti

Cinco intentos a lo largo de unas dos horas, con la fila de entrega escrita antes del primero. Un endpoint que falla veinte veces seguidas se desactiva en vez de machacarse.

Deduplica por el id de entrega

Cada intento lleva `X-Autostud-Delivery`. Mismo id, mismo evento: trátalo como la clave primaria del trabajo que dispares.

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')
})
Qué obtienes

Por qué es una llamada y no quince

Las piezas difíciles de rehacer son las que operamos nosotros.

Ajuste por palabra

Cada palabra lleva su inicio y su final, así que el resaltado cae en la sílaba. El ajuste por frase es lo que hace que casi todos los subtítulos automáticos parezcan tardíos.

116 estilos, una cadena

Del subtítulo sobrio de televisión al estilo saltarín resaltado. El preajuste fija tipografía, contorno, color y animación juntos, escalados a tu lienzo.

La IA elige los acentos

Una segunda pasada lee la transcripción y marca las palabras que llevan el sentido. Las muletillas se quedan quietas. Un booleano la desactiva.

Dos voces, dos colores

La diarización es un flag. Una entrevista vuelve con cada voz en su color, y el espectador sabe quién habla sin sonido.

Los mismos objetos que el panel

Un vídeo subtitulado por la API se abre en el editor como cualquier otro. Corrige una palabra a mano, vuelve a renderizar, sigue automatizando: los dos caminos escriben el mismo documento.

Pensada para reintentos

Claves de idempotencia, errores tipados, claves de prueba, webhooks firmados y un registro de peticiones de 30 días. La mitad aburrida de una API, que es la que se nota a las 3 de la mañana.

FAQ

Las preguntas que de verdad nos llegan

No, y es a propósito: la API toma una URL. Aloja el vídeo donde nuestros servidores puedan leerlo — tu bucket, un CDN, un enlace firmado — y pásalo en `media_url`. Los archivos subidos desde el panel ya tienen una URL utilizable.

Hoy no. Los subtítulos se incrustan en el mp4, que es lo que piden las plataformas de formato corto. La lista de palabras con sus tiempos queda guardada en el vídeo, así que un archivo aparte es una transformación que puedes hacer tú con lo que devuelve la API.

Un render son 20 créditos, fijos, dure lo que dure. La transcripción y la pasada de énfasis se registran en tu espacio como cualquier otra llamada de IA, y `GET /api/v1/usage` informa de lo que ha gastado una clave en el periodo.

Minutos, no segundos: corre en una flota de workers, no dentro de la petición. Justo para eso existe `render.completed` — suscríbete en lugar de mantener una conexión abierta.

Sí. Llama otra vez a `subtitles.generate` con `restyle_only: true` y otro `style_preset`: reutiliza las palabras ya guardadas en el vídeo, así que nada se transcribe dos veces.

Whisper detecta el idioma por su cuenta y los subtítulos salen en el que se habla — no hay que declarar nada. El idioma detectado viene en la respuesta por si quieres ramificar.

Publica vídeo subtitulado desde tu propio backend

Crea una clave, ejecuta el quickstart y el primer mp4 subtitulado está a unos minutos.

Pagos seguros
Acceso instantáneo
Cancela cuando quieras