REST-API|API til Videoundertekster

Tekst enhver video
med tre API-kald

Send en video-URL og få en mp4 tilbage med undertekster brændt ind ord for ord. Whisper timer hvert ord, en AI-gennemgang vælger dem, der skal fremhæves, og renderingen kører på vores workerflåde.

POSThttps://autostud.ai/api/v1/actions/subtitles.generate
Hurtig start

Hele forløbet, i én fil

Indsæt, sæt din nøgle, kør. Intet her er pseudokode.

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 — autentificér med `Authorization: Bearer sk_live_…`. En `sk_test_`-nøgle går præcis samme vej og stopper, før noget bliver brugt.

Trin for trin

Det du faktisk skal gøre

Seks trin, fire af dem ét kald hver.

  1. 1

    Opret en API-nøgle

    Dashboard, Indstillinger, API-nøgler. Tag forudindstillingen "Automation", hvis du ikke vil vælge scopes i hånden: den dækker videoer, renderinger og filer. Hemmeligheden vises én gang. `sk_live_` bruger, `sk_test_` validerer hele kaldet og stopper før skrivningen.

    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

    Læg videoen bag en URL

    Der er ingen upload-endpoint: API'et læser en URL. Din bucket, dit CDN, et signeret link — alt vores servere kan hente. At registrere den i arbejdsområdets bibliotek er valgfrit og ændrer intet i flowet.

    # 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

    Opret videoprojektet

    `videos.create` returnerer det `video_id`, alt andet hænger på. Send en `Idempotency-Key`, så giver et gentaget kald det første svar i stedet for at oprette et projekt mere.

    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

    Tekst den

    `subtitles.generate` klarer hele arbejdet på serveren: Whisper transskriberer med ordpræcis timing, en AI-gennemgang markerer ordene, der skal fremhæves, og en af 116 stilarter lægges på. Svaret rummer antal ord, den præcise længde og det registrerede sprog.

    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

    Sæt renderingen i kø

    `renders.create` lægger jobbet på workerflåden og svarer straks med `render_id`. En rendering koster 20 credits, uanset længde.

    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

    Hent mp4'en

    Abonnér på `render.completed`, så bliver den færdige URL sendt signeret til dig. Foretrækker du selv at hente: list videoens renderinger og læs `render_status`, indtil der står `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.
Reference

Alle kald på denne side

Scopes er det, nøglen skal bære, ikke det du sender.

KaldScopeHvad det gør
GET /api/v1-Discovery: hvad nøglen må, dens grænser og dens forbrug.
POST /api/v1/actions/videos.createvideos:writeOpretter videoprojektet og returnerer dets id.
POST /api/v1/actions/subtitles.generatevideos:executeTransskriberer, fremhæver, styler og gemmer undertekstsporet.
POST /api/v1/actions/renders.createrenders:executeSætter renderingen i kø på workerflåden. 20 credits.
GET /api/v1/renders?video_id=…renders:readEn videos renderinger, nyeste først, med status og URL.
POST /api/v1/webhookswebhooks:writeRegistrerer en https-endpoint og returnerer dens signeringsnøgle, én gang.

Stilforvalg

Send ét som `style_preset`. Hvert forvalg sætter skrifttype, kontur, accentfarve og animation på én gang — der er ikke mere at konfigurere.

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

Værd at vide, før du bygger

  • Grænserne gælder pr. nøgle og kommer i headers: `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `Retry-After` ved en 429.
  • `Idempotency-Key` på ethvert POST gør et gentaget forsøg sikkert i 24 timer; første svar gentages ordret.
  • En `sk_test_`-nøgle gennemgår autentificering, scopes, grænser og validering og svarer så `simulated: true` uden at skrive eller bruge noget.
  • Fejl er typede: `invalid_request`, `insufficient_credits`, `rate_limit_exceeded`, `upstream_error` — forgren på koden, aldrig på teksten.

Når det går galt

Alle fejl har samme form: en stabil `code` at forgrene på, et `hint` der udpeger kaldet som retter det, og et `request_id` at henvise til. Hele ordlisten ligger på /docs/api/errors.

KodeHvad det betyder
401 missing_credentialsIngen nøgle på kaldet, eller en vi ikke kender.
403 insufficient_scopeNøglen mangler et scope; `details.required_scopes` nævner det.
422 validation_failedEt felt er forkert; `details.issues` lister dem alle.
402 insufficient_creditsFor få credits til renderingen. Fyld op, eller brug en testnøgle.
429 rate_limit_exceededFor mange kald. Vent `Retry-After` sekunder, og prøv igen.
502 upstream_errorEn leverandør vi afhænger af fejlede. Prøv igen med 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"
  }
}

Fejlkoder

Webhooks

Hold op med at polle

En rendering tager minutter. Lad den komme til dig.

Signerede, kan verificeres på ti linjer

`X-Autostud-Signature: t=<unix>,v1=<hex>` er en HMAC-SHA256 over `"<timestamp>.<rå body>"`. Verificér mod den rå body, før du parser den.

Gensendt for dig

Fem forsøg over cirka to timer, og leveringsrækken skrives før det første. En endpoint, der fejler tyve gange i træk, bliver slået fra i stedet for hamret.

Deduplikér på leverings-id

Hvert forsøg bærer `X-Autostud-Delivery`. Samme id betyder samme hændelse: behandl det som primærnøgle for det arbejde, du sætter i gang.

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')
})
Det du får

Hvorfor det er ét kald og ikke femten

De dele, der er svære at bygge selv, er dem vi driver.

Ordpræcis timing

Hvert ord bærer sin egen start og slutning, så fremhævningen lander på stavelsen. Sætningsbaseret timing er grunden til, at de fleste automatiske undertekster føles forsinkede.

116 stilarter, én streng

Fra rene broadcast-undertekster til det hoppende, fremhævede look. Forvalget sætter skrifttype, kontur, farve og animation samlet, skaleret til dit lærred.

AI'en vælger accenterne

En anden gennemgang læser transskriptionen og markerer ordene, der bærer meningen. Fyldordene får lov at være i fred. En boolean slår det fra.

To stemmer, to farver

Diarisering er et flag. Et interview kommer tilbage med hver stemme i sin egen farve, så seeren ved hvem der taler uden lyd.

De samme objekter som dashboardet

En video tekstet via API'et åbner i editoren som enhver anden. Ret et ord i hånden, render igen, bliv ved med at automatisere — begge veje skriver det samme dokument.

Bygget til at blive prøvet igen

Idempotensnøgler, typede fejl, testnøgler, signerede webhooks og en forespørgselslog på 30 dage. Den kedelige halvdel af et API, altså den man mærker klokken tre om natten.

FAQ

De spørgsmål udviklere rent faktisk sender

Nej, og det er med vilje: API'et tager en URL. Host videoen et sted, vores servere kan læse den — din egen bucket, et CDN, et signeret link — og send den som `media_url`. Filer uploadet via dashboardet har allerede en brugbar URL.

Ikke i dag. Underteksterne renderes ind i mp4'en, hvilket er det, kortformatplatformene har brug for. Ordlisten med timings ligger på videoen, så en separat fil er en omdannelse, du selv kan lave ud fra det, API'et returnerer.

En rendering er 20 credits, fast, uanset længde. Transskriptionen og accentgennemgangen logges i dit arbejdsområde som ethvert andet AI-kald, og `GET /api/v1/usage` viser, hvad en nøgle har brugt i perioden.

Minutter, ikke sekunder: den kører på en workerflåde, ikke inde i kaldet. Præcis derfor findes `render.completed` — abonnér i stedet for at holde en forbindelse åben.

Ja. Kald `subtitles.generate` igen med `restyle_only: true` og et andet `style_preset`: ordene, der allerede ligger på videoen, genbruges, så intet transskriberes to gange.

Whisper registrerer sproget selv, og underteksterne kommer på det, der bliver talt — intet at angive. Det registrerede sprog står i svaret, hvis du vil forgrene på det.

Lever tekstet video fra din egen backend

Opret en nøgle, kør hurtigstarten, og den første tekstede mp4 er minutter væk.

Sikker betaling
Adgang med det samme
Opsig når du vil