Untertitle jedes Video
mit drei API-Aufrufen
Schick eine Video-URL, bekomm ein mp4 mit wortgenau eingebrannten Untertiteln zurück. Whisper timet jedes Wort, ein KI-Durchlauf wählt die Betonungen, und gerendert wird auf unserer Worker-Flotte.
POSThttps://autostud.ai/api/v1/actions/subtitles.generateDer ganze Ablauf, in einer Datei
Einfügen, Key setzen, ausführen. Nichts davon ist Pseudocode.
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)Basis-URL: https://autostud.ai/api/v1 — Authentifizierung über `Authorization: Bearer sk_live_…`. Ein `sk_test_`-Key läuft denselben Pfad und stoppt, bevor etwas ausgegeben wird.
Was du wirklich tun musst
Sechs Schritte, vier davon je ein Request.
- 1
API-Key anlegen
Dashboard, Einstellungen, API-Keys. Nimm das Preset "Automation", wenn du die Scopes nicht selbst wählen willst: es deckt Videos, Renders und Dateien ab. Das Secret wird einmal angezeigt. `sk_live_` gibt aus, `sk_test_` validiert den ganzen Request und stoppt vor dem Schreiben.
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
Video hinter eine URL legen
Es gibt keinen Upload-Endpoint: die API liest eine URL. Dein Bucket, dein CDN, ein signierter Link — alles, was unsere Server abrufen können. Der Eintrag in der Workspace-Bibliothek ist optional und ändert am Ablauf nichts.
# 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
Video-Projekt erstellen
`videos.create` liefert die `video_id`, an der alles Weitere hängt. Schick einen `Idempotency-Key`, dann gibt ein wiederholter Request die erste Antwort zurück statt ein zweites Projekt anzulegen.
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
Untertitel erzeugen
`subtitles.generate` erledigt alles serverseitig: Whisper transkribiert wortgenau, ein KI-Durchlauf markiert die zu betonenden Wörter, und einer von 116 Stilen wird angewendet. Zurück kommen Wortanzahl, exakte Dauer und erkannte Sprache.
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
Render starten
`renders.create` legt den Job auf die Worker-Flotte und antwortet sofort mit der `render_id`. Ein Render kostet 20 Credits, unabhängig von der Länge.
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
mp4 abholen
Abonniere `render.completed`, dann wird dir die fertige URL signiert zugestellt. Wer lieber zieht, listet die Renders des Videos und liest `render_status`, bis dort `done` steht.
# 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.
Alle Aufrufe dieser Seite
Scopes sind das, was der Key tragen muss, nicht was du sendest.
| Aufruf | Scope | Was er tut |
|---|---|---|
| GET /api/v1 | - | Discovery: was dieser Key darf, seine Limits und sein Verbrauch. |
| POST /api/v1/actions/videos.create | videos:write | Legt das Video-Projekt an und gibt seine id zurück. |
| POST /api/v1/actions/subtitles.generate | videos:execute | Transkribiert, betont, stylt und speichert die Untertitelspur. |
| POST /api/v1/actions/renders.create | renders:execute | Stellt den Render auf die Worker-Flotte. 20 Credits. |
| GET /api/v1/renders?video_id=… | renders:read | Die Render-Jobs eines Videos, neueste zuerst, mit Status und URL. |
| POST /api/v1/webhooks | webhooks:write | Registriert einen https-Endpoint und gibt sein Signing-Secret zurück, einmalig. |
Stil-Presets
Übergib eines als `style_preset`. Jedes setzt Schrift, Kontur, Akzentfarbe und Animation gemeinsam — mehr ist nicht zu konfigurieren.
default4 Wörter · simplebold3 Wörter · simpleimpact3 Wörter · focus_on_one_wordbeast1 Wörter · focus_on_one_wordkaraoke5 Wörter · focus_on_one_wordkaraoke_fill5 Wörter · karaokeneon4 Wörter · progressively_visibleminimal6 Wörter · simplepop5 Wörter · progressively_visiblepill5 Wörter · simplecinematic9 Wörter · simpleWas du vor dem Bauen wissen solltest
- Rate Limits gelten pro Key und stehen in den Headern: `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `Retry-After` bei einem 429.
- `Idempotency-Key` auf jedem POST macht einen Retry 24 Stunden lang sicher; die erste Antwort wird unverändert wiederholt.
- Ein `sk_test_`-Key durchläuft Authentifizierung, Scopes, Limits und Validierung und antwortet dann `simulated: true`, ohne zu schreiben oder auszugeben.
- Fehler sind typisiert: `invalid_request`, `insufficient_credits`, `rate_limit_exceeded`, `upstream_error` — verzweige über den Code, nie über die Nachricht.
Wenn es schiefgeht
Jeder Fehler hat dieselbe Form: ein stabiler `code` zum Verzweigen, ein `hint`, der den korrigierenden Aufruf nennt, und eine `request_id` zum Zitieren. Das vollständige Vokabular steht unter /docs/api/errors.
| Code | Was es bedeutet |
|---|---|
| 401 missing_credentials | Kein Schlüssel im Request, oder ein unbekannter. |
| 403 insufficient_scope | Dem Schlüssel fehlt ein Scope; `details.required_scopes` nennt ihn. |
| 422 validation_failed | Ein Feld ist ungültig; `details.issues` listet jedes einzelne auf. |
| 402 insufficient_credits | Zu wenig Credits für den Render. Aufladen, oder einen Test-Key nehmen. |
| 429 rate_limit_exceeded | Zu viele Aufrufe. `Retry-After` Sekunden warten, dann erneut. |
| 502 upstream_error | Ein Anbieter, von dem wir abhängen, ist ausgefallen. Mit Backoff wiederholen. |
{
"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"
}
}Hör auf zu pollen
Ein Render dauert Minuten. Lass ihn zu dir kommen.
Signiert, in zehn Zeilen prüfbar
`X-Autostud-Signature: t=<unix>,v1=<hex>` ist ein HMAC-SHA256 über `"<timestamp>.<roher Body>"`. Prüfe gegen den rohen Body, bevor du ihn parst.
Wiederholt, ohne dein Zutun
Fünf Versuche über rund zwei Stunden, die Delivery-Zeile wird vor dem ersten geschrieben. Ein Endpoint, der zwanzigmal in Folge scheitert, wird deaktiviert statt geflutet.
Dedupliziere über die Delivery-Id
Jeder Versuch trägt `X-Autostud-Delivery`. Gleiche Id heißt gleiches Event: behandle sie als Primärschlüssel der Arbeit, die du auslöst.
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')
})Warum das ein Aufruf ist und nicht fünfzehn
Die Teile, die schwer nachzubauen sind, betreiben wir.
Wortgenaues Timing
Jedes Wort trägt eigenen Start und Ende, das Highlight sitzt also auf der Silbe. Satzweises Timing ist der Grund, warum die meisten automatischen Untertitel zu spät wirken.
116 Stile, ein String
Von sauberen Broadcast-Untertiteln bis zum hüpfenden Highlight-Look. Das Preset setzt Schrift, Kontur, Farbe und Animation zusammen, skaliert auf deine Leinwand.
Die KI wählt die Akzente
Ein zweiter Durchlauf liest das Transkript und markiert die bedeutungstragenden Wörter. Füllwörter bleiben ruhig. Ein Boolean schaltet es ab.
Zwei Stimmen, zwei Farben
Diarisierung ist ein Flag. Ein Interview kommt mit je eigener Farbe pro Stimme zurück, damit man auch ohne Ton weiß, wer spricht.
Dieselben Objekte wie im Dashboard
Ein per API untertiteltes Video öffnet sich im Editor wie jedes andere. Wort von Hand korrigieren, neu rendern, weiter automatisieren — beide Wege schreiben dasselbe Dokument.
Auf Retries gebaut
Idempotency-Keys, typisierte Fehler, Test-Keys, signierte Webhooks und ein 30-Tage-Request-Log. Die langweilige Hälfte einer API — also die, die man um 3 Uhr nachts spürt.
Die Fragen, die Entwickler uns wirklich schicken
Nein, und das ist Absicht: die API nimmt eine URL. Hoste das Video dort, wo unsere Server es lesen können — dein Bucket, ein CDN, ein signierter Link — und übergib es als `media_url`. Dateien aus dem Dashboard haben bereits eine nutzbare URL.
Heute nicht. Die Untertitel werden ins mp4 gerendert, was Kurzvideo-Plattformen brauchen. Die Wortliste mit ihren Zeiten liegt auf dem Video, eine Sidecar-Datei ist also eine Umwandlung, die du aus der API-Antwort selbst bauen kannst.
Ein Render kostet 20 Credits, fix, egal wie lang. Transkription und Betonungsdurchlauf werden wie jeder andere KI-Aufruf in deinem Workspace protokolliert, und `GET /api/v1/usage` zeigt, was ein Key in dieser Periode verbraucht hat.
Minuten, nicht Sekunden: er läuft auf einer Worker-Flotte, nicht im Request. Genau dafür gibt es `render.completed` — abonnieren statt eine Verbindung offenzuhalten.
Ja. Ruf `subtitles.generate` erneut mit `restyle_only: true` und einem anderen `style_preset` auf: die bereits gespeicherten Wörter werden wiederverwendet, nichts wird zweimal transkribiert.
Whisper erkennt die Sprache selbst, und die Untertitel kommen in der gesprochenen Sprache zurück — nichts zu deklarieren. Die erkannte Sprache steht in der Antwort, falls du darauf verzweigen willst.
Weitere kostenlose Tools
Alle laufen im Browser, zum Ausprobieren ohne Konto.
TikTok-Quiz-Video-Generator
Live-ToolAus jedem Thema ein Quiz-Video machen
YouTube-Quiz-Video-Generator
Live-ToolQuiz-Videos in 16:9 oder als Shorts
Sprachquiz-Video-Generator
Live-ToolAus einer Wortliste ein Sprachlern-Video machen
Tweet zu Video
Live-ToolTweets in Videos verwandeln
YouTube zu Karaoke-Video
Live-ToolJeden Track in ein Karaoke-Video verwandeln
Reddit-Post zu Video
Live-ToolEinen Reddit-Thread in ein Short verwandeln
CSV zu Video-Rangliste
Live-ToolAus einer Tabelle ein Ranking-Video machen
TikTok-Kommentare zu Song-Video
Live-ToolAus einer TikTok-Kommentarspalte ein Song-Video machen
KI-Frucht-Videos
Virale Frucht-Videos mit KI erstellen
Untertitelte Videos aus deinem eigenen Backend ausliefern
Key anlegen, Quickstart ausführen — das erste untertitelte mp4 ist Minuten entfernt.