Sottotitola qualsiasi video
con tre chiamate API
Invia l'URL di un video e ricevi un mp4 con i sottotitoli impressi parola per parola. Whisper sincronizza ogni parola, una passata di IA sceglie quelle da evidenziare e il rendering gira sulla nostra flotta di worker.
POSThttps://autostud.ai/api/v1/actions/subtitles.generateTutto il percorso, in un solo file
Incolla, metti la tua chiave, esegui. Qui non c'è pseudocodice.
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 di base: https://autostud.ai/api/v1 — autenticazione con `Authorization: Bearer sk_live_…`. Una chiave `sk_test_` percorre esattamente la stessa strada e si ferma prima di spendere.
Cosa devi fare davvero
Sei passaggi, quattro dei quali una sola richiesta.
- 1
Creare una chiave API
Dashboard, Impostazioni, Chiavi API. Scegli il preset «Automation» se non vuoi selezionare gli scope a mano: copre video, render e file. Il segreto viene mostrato una volta sola. `sk_live_` spende, `sk_test_` valida tutta la richiesta e si ferma prima della scrittura.
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
Mettere il video dietro un URL
Non esiste un endpoint di upload: l'API legge un URL. Il tuo bucket, la tua CDN, un link firmato — qualsiasi cosa i nostri server possano scaricare. Registrarlo nella libreria dello spazio è facoltativo e non cambia nulla.
# 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
Creare il progetto video
`videos.create` restituisce il `video_id` a cui si aggancia tutto il resto. Manda un `Idempotency-Key` e una richiesta ripetuta restituisce la prima risposta invece di creare un secondo progetto.
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
Aggiungere i sottotitoli
`subtitles.generate` fa tutto lato server: Whisper trascrive con la sincronia parola per parola, una passata di IA marca le parole da enfatizzare e viene applicato uno degli 116 stili. Risponde con il numero di parole, la durata esatta e la lingua rilevata.
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
Mettere in coda il render
`renders.create` mette il job sulla flotta di worker e risponde subito con il `render_id`. Un render costa 20 crediti, qualunque sia la durata.
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
Ritirare l'mp4
Iscriviti a `render.completed` e l'URL finale ti arriva firmato. Se preferisci interrogare tu, elenca i render del video e leggi `render_status` finché non dice `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.
Tutte le chiamate di questa pagina
Gli scope sono ciò che la chiave deve portare, non ciò che invii.
| Chiamata | Scope | Cosa fa |
|---|---|---|
| GET /api/v1 | - | Discovery: cosa può fare questa chiave, i suoi limiti e i suoi consumi. |
| POST /api/v1/actions/videos.create | videos:write | Crea il progetto video e ne restituisce l'identificativo. |
| POST /api/v1/actions/subtitles.generate | videos:execute | Trascrive, enfatizza, applica lo stile e salva la traccia di sottotitoli. |
| POST /api/v1/actions/renders.create | renders:execute | Mette il render in coda sulla flotta di worker. 20 crediti. |
| GET /api/v1/renders?video_id=… | renders:read | I render di un video, dal più recente, con stato e URL. |
| POST /api/v1/webhooks | webhooks:write | Registra un endpoint https e ne restituisce il segreto di firma, una volta. |
Stili disponibili
Passane uno in `style_preset`. Ognuno imposta insieme font, contorno, colore d'accento e animazione — non c'è altro da configurare.
default4 parole · simplebold3 parole · simpleimpact3 parole · focus_on_one_wordbeast1 parole · focus_on_one_wordkaraoke5 parole · focus_on_one_wordkaraoke_fill5 parole · karaokeneon4 parole · progressively_visibleminimal6 parole · simplepop5 parole · progressively_visiblepill5 parole · simplecinematic9 parole · simpleDa sapere prima di scrivere codice
- I limiti di frequenza sono per chiave e arrivano negli header: `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `Retry-After` su un 429.
- `Idempotency-Key` su qualsiasi POST rende sicuro un nuovo tentativo per 24 ore; la prima risposta viene ripetuta identica.
- Una chiave `sk_test_` esercita autenticazione, scope, limiti e validazione, poi risponde `simulated: true` senza scrivere né spendere.
- Gli errori sono tipizzati: `invalid_request`, `insufficient_credits`, `rate_limit_exceeded`, `upstream_error` — ramifica sul codice, mai sul messaggio.
Quando fallisce
Ogni errore ha la stessa forma: un `code` stabile su cui ramificare, un `hint` che nomina la chiamata che risolve e un `request_id` da citare. Il vocabolario completo è su /docs/api/errors.
| Codice | Che cosa significa |
|---|---|
| 401 missing_credentials | Nessuna chiave nella richiesta, oppure una che non riconosciamo. |
| 403 insufficient_scope | Alla chiave manca uno scope; `details.required_scopes` lo nomina. |
| 422 validation_failed | Un campo non è valido; `details.issues` li elenca tutti. |
| 402 insufficient_credits | Crediti insufficienti per il render. Ricarica, o usa una chiave di test. |
| 429 rate_limit_exceeded | Troppe chiamate. Aspetta `Retry-After` secondi e riprova. |
| 502 upstream_error | Un provider da cui dipendiamo ha fallito. Riprova 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"
}
}Smetti di fare polling
Un render dura minuti. Lascia che arrivi da solo.
Firmati, verificabili in dieci righe
`X-Autostud-Signature: t=<unix>,v1=<hex>` è un HMAC-SHA256 su `"<timestamp>.<corpo grezzo>"`. Verifica sul corpo grezzo, prima di fare il parsing.
Riprovati al posto tuo
Cinque tentativi in circa due ore, con la riga di consegna scritta prima del primo. Un endpoint che fallisce venti volte di fila viene disattivato invece di essere martellato.
Deduplica sull'id di consegna
Ogni tentativo porta `X-Autostud-Delivery`. Stesso id, stesso evento: trattalo come chiave primaria del lavoro che avvii.
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')
})Perché è una chiamata e non quindici
I pezzi difficili da rifare sono quelli che gestiamo noi.
Sincronia parola per parola
Ogni parola ha il suo inizio e la sua fine, così l'evidenziazione cade sulla sillaba. La sincronia per frase è ciò che fa sembrare in ritardo quasi tutti i sottotitoli automatici.
116 stili, una stringa
Dal sottotitolo broadcast pulito allo stile saltellante evidenziato. Il preset imposta font, contorno, colore e animazione insieme, in scala sul tuo canvas.
L'IA sceglie gli accenti
Una seconda passata legge la trascrizione e marca le parole che portano il significato. Le parole di riempimento restano calme. Un booleano la disattiva.
Due voci, due colori
La diarizzazione è un flag. Un'intervista torna con ogni voce nel suo colore, e chi guarda capisce chi parla anche senza audio.
Gli stessi oggetti della dashboard
Un video sottotitolato via API si apre nell'editor come qualunque altro. Correggi una parola a mano, rifai il render, continua ad automatizzare: le due strade scrivono lo stesso documento.
Fatta per essere riprovata
Chiavi di idempotenza, errori tipizzati, chiavi di test, webhook firmati e un log delle richieste di 30 giorni. La metà noiosa di un'API, cioè quella che si sente alle 3 di notte.
Le domande che gli sviluppatori ci mandano davvero
No, ed è voluto: l'API prende un URL. Ospita il video dove i nostri server possono leggerlo — il tuo bucket, una CDN, un link firmato — e passalo in `media_url`. I file caricati dalla dashboard hanno già un URL utilizzabile.
Oggi no. I sottotitoli vengono impressi nell'mp4, che è quello che serve alle piattaforme di formato corto. L'elenco delle parole con la loro sincronia resta sul video: un file separato è una trasformazione che puoi fare tu da ciò che l'API restituisce.
Un render costa 20 crediti, fissi, qualunque sia la durata. La trascrizione e la passata di enfasi vengono registrate nel tuo spazio come ogni altra chiamata IA, e `GET /api/v1/usage` dice quanto ha speso una chiave nel periodo.
Minuti, non secondi: gira su una flotta di worker, non dentro la richiesta. È esattamente per questo che esiste `render.completed` — iscriviti invece di tenere una connessione aperta.
Sì. Richiama `subtitles.generate` con `restyle_only: true` e un altro `style_preset`: riusa le parole già salvate sul video, quindi nulla viene trascritto due volte.
Whisper rileva la lingua da solo e i sottotitoli tornano in quella parlata — niente da dichiarare. La lingua rilevata è nella risposta, se vuoi ramificare.
Altri strumenti gratuiti
Funzionano tutti nel browser, senza account per provarli.
Generatore di Video Quiz TikTok
Strumento liveTrasforma qualsiasi tema in un video quiz
Generatore di Video Quiz YouTube
Strumento liveVideo quiz in 16:9 o in Shorts
Generatore di Video Quiz di Lingue
Strumento liveTrasforma una lista di parole in un video di lingua
Tweet in Video
Strumento liveTrasforma i tweet in video
YouTube in Video Karaoke
Strumento liveTrasforma un brano in video karaoke
Post Reddit in Video
Strumento liveTrasforma un thread Reddit in uno short
CSV in Video Classifica
Strumento liveTrasforma un foglio di calcolo in un video classifica
Commenti TikTok in Video Canzone
Strumento liveTrasforma i commenti TikTok in un video canzone
Video IA Frutta
Crea video di frutta virali con l'IA
Pubblica video sottotitolati dal tuo backend
Crea una chiave, esegui il quickstart: il primo mp4 sottotitolato è a pochi minuti.