Dodaj napisy do każdego wideo
trzema wywołaniami API
Wyślij URL wideo, odbierz mp4 z napisami wypalonymi słowo po słowie. Whisper synchronizuje każde słowo, przebieg AI wybiera te do podkreślenia, a render idzie na naszą flotę workerów.
POSThttps://autostud.ai/api/v1/actions/subtitles.generateCała ścieżka w jednym pliku
Wklej, ustaw klucz, uruchom. Nic tu nie jest pseudokodem.
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)Adres bazowy: https://autostud.ai/api/v1 — uwierzytelnianie przez `Authorization: Bearer sk_live_…`. Klucz `sk_test_` przechodzi dokładnie tę samą ścieżkę i zatrzymuje się przed wydaniem czegokolwiek.
Co naprawdę musisz zrobić
Sześć kroków, cztery z nich to jedno żądanie.
- 1
Utwórz klucz API
Panel, Ustawienia, Klucze API. Wybierz preset „Automation”, jeśli nie chcesz ustawiać scope’ów ręcznie: obejmuje wideo, rendery i pliki. Sekret pokazuje się raz. `sk_live_` wydaje, `sk_test_` waliduje całe żądanie i zatrzymuje się przed zapisem.
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
Umieść wideo pod adresem URL
Nie ma endpointu do wysyłki pliku: API czyta URL. Twój bucket, twoje CDN, podpisany link — cokolwiek nasze serwery pobiorą. Zapis w bibliotece przestrzeni jest opcjonalny i niczego nie zmienia.
# 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
Utwórz projekt wideo
`videos.create` zwraca `video_id`, do którego podpina się reszta. Wyślij `Idempotency-Key`, a ponowione żądanie zwróci pierwszą odpowiedź zamiast tworzyć drugi projekt.
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
Dodaj napisy
`subtitles.generate` robi całą robotę po stronie serwera: Whisper transkrybuje z dokładnością do słowa, przebieg AI zaznacza słowa do podkreślenia, a jeden z 116 stylów zostaje nałożony. W odpowiedzi masz liczbę słów, dokładny czas i wykryty język.
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
Zakolejkuj render
`renders.create` wrzuca zadanie na flotę workerów i od razu odpowiada `render_id`. Render kosztuje 20 kredytów, niezależnie od długości.
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
Odbierz mp4
Subskrybuj `render.completed`, a gotowy URL przyjdzie do ciebie podpisany. Jeśli wolisz odpytywać, wylistuj rendery wideo i czytaj `render_status`, aż pokaże `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.
Wszystkie wywołania z tej strony
Scope to to, co musi mieć klucz, a nie to, co wysyłasz.
| Wywołanie | Scope | Co robi |
|---|---|---|
| GET /api/v1 | - | Discovery: co potrafi ten klucz, jakie ma limity i zużycie. |
| POST /api/v1/actions/videos.create | videos:write | Tworzy projekt wideo i zwraca jego identyfikator. |
| POST /api/v1/actions/subtitles.generate | videos:execute | Transkrybuje, podkreśla, styluje i zapisuje ścieżkę napisów. |
| POST /api/v1/actions/renders.create | renders:execute | Kolejkuje render na flocie workerów. 20 kredytów. |
| GET /api/v1/renders?video_id=… | renders:read | Rendery danego wideo, od najnowszego, ze statusem i URL-em. |
| POST /api/v1/webhooks | webhooks:write | Rejestruje endpoint https i raz zwraca jego sekret podpisujący. |
Dostępne style
Podaj jeden w `style_preset`. Każdy ustawia naraz font, obrys, kolor akcentu i animację — nie ma nic więcej do konfigurowania.
default4 słowa · simplebold3 słowa · simpleimpact3 słowa · focus_on_one_wordbeast1 słowa · focus_on_one_wordkaraoke5 słowa · focus_on_one_wordkaraoke_fill5 słowa · karaokeneon4 słowa · progressively_visibleminimal6 słowa · simplepop5 słowa · progressively_visiblepill5 słowa · simplecinematic9 słowa · simpleCo warto wiedzieć przed kodowaniem
- Limity są per klucz i wracają w nagłówkach: `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `Retry-After` przy 429.
- `Idempotency-Key` przy każdym POST czyni ponowienie bezpiecznym przez 24 godziny; pierwsza odpowiedź wraca bez zmian.
- Klucz `sk_test_` przechodzi uwierzytelnianie, scope’y, limity i walidację, po czym odpowiada `simulated: true` bez zapisu i bez wydatku.
- Błędy są typowane: `invalid_request`, `insufficient_credits`, `rate_limit_exceeded`, `upstream_error` — rozgałęziaj po kodzie, nigdy po treści.
Kiedy się nie uda
Każdy błąd ma ten sam kształt: stabilny `code` do rozgałęzień, `hint` wskazujący wywołanie, które to naprawi, i `request_id` do zacytowania. Pełny słownik jest na /docs/api/errors.
| Kod | Co oznacza |
|---|---|
| 401 missing_credentials | Brak klucza w żądaniu albo klucz nieznany. |
| 403 insufficient_scope | Kluczowi brakuje scope; `details.required_scopes` go nazywa. |
| 422 validation_failed | Pole jest niepoprawne; `details.issues` wylicza każde z nich. |
| 402 insufficient_credits | Za mało kredytów na render. Doładuj albo użyj klucza testowego. |
| 429 rate_limit_exceeded | Za dużo wywołań. Odczekaj `Retry-After` sekund i ponów. |
| 502 upstream_error | Dostawca, od którego zależymy, zawiódł. Ponów z backoffem. |
{
"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"
}
}Przestań odpytywać
Render trwa minuty. Niech sam do ciebie przyjdzie.
Podpisane, do sprawdzenia w dziesięciu linijkach
`X-Autostud-Signature: t=<unix>,v1=<hex>` to HMAC-SHA256 z `"<timestamp>.<surowe ciało>"`. Weryfikuj na surowym ciele, zanim je sparsujesz.
Ponawiane za ciebie
Pięć prób w ciągu około dwóch godzin, a wiersz dostawy zapisuje się przed pierwszą. Endpoint, który zawiedzie dwadzieścia razy z rzędu, zostaje wyłączony, a nie zasypywany.
Deduplikuj po id dostawy
Każda próba niesie `X-Autostud-Delivery`. To samo id to to samo zdarzenie: traktuj je jak klucz główny pracy, którą uruchamiasz.
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')
})Dlaczego to jedno wywołanie, a nie piętnaście
Trudne do odtworzenia kawałki to te, które my utrzymujemy.
Synchronizacja co do słowa
Każde słowo ma własny początek i koniec, więc podświetlenie trafia w sylabę. Synchronizacja zdaniami sprawia, że większość automatycznych napisów wydaje się spóźniona.
116 stylów, jeden string
Od czystych napisów telewizyjnych po skaczący, podświetlany look. Preset ustawia font, obrys, kolor i animację razem, w skali twojego kadru.
AI wybiera akcenty
Drugi przebieg czyta transkrypcję i zaznacza słowa niosące sens. Wypełniacze zostają spokojne. Wyłączasz to jednym booleanem.
Dwa głosy, dwa kolory
Diaryzacja to flaga. Wywiad wraca z każdym głosem w swoim kolorze, więc widz wie, kto mówi, nawet bez dźwięku.
Te same obiekty co w panelu
Wideo otytułowane przez API otwiera się w edytorze jak każde inne. Popraw słowo ręcznie, zrenderuj ponownie, automatyzuj dalej — obie drogi piszą ten sam dokument.
Zbudowane pod ponowienia
Klucze idempotencji, typowane błędy, klucze testowe, podpisane webhooki i 30-dniowy log żądań. Nudna połowa API — czyli ta, którą czuć o trzeciej w nocy.
Pytania, które naprawdę dostajemy od programistów
Nie i jest to celowe: API przyjmuje URL. Postaw wideo tam, gdzie nasze serwery je odczytają — twój bucket, CDN, podpisany link — i podaj je w `media_url`. Pliki wgrane z panelu mają już gotowy URL.
Dziś nie. Napisy są wypalane w mp4, bo tego wymagają platformy krótkiego wideo. Lista słów z czasami zostaje zapisana na wideo, więc osobny plik to przekształcenie, które zrobisz u siebie z tego, co zwraca API.
Render to 20 kredytów, na stałe, niezależnie od długości. Transkrypcja i przebieg podkreśleń są logowane w twojej przestrzeni jak każde inne wywołanie AI, a `GET /api/v1/usage` pokazuje, ile klucz wydał w okresie.
Minuty, nie sekundy: idzie na flotę workerów, nie do żądania. Dokładnie po to jest `render.completed` — subskrybuj zamiast trzymać otwarte połączenie.
Tak. Wywołaj `subtitles.generate` ponownie z `restyle_only: true` i innym `style_preset`: użyje słów już zapisanych na wideo, więc nic nie jest transkrybowane dwa razy.
Whisper sam wykrywa język, a napisy wracają w tym, którym się mówi — niczego nie deklarujesz. Wykryty język jest w odpowiedzi, jeśli chcesz się na nim rozgałęzić.
Więcej darmowych narzędzi
Wszystkie działają w przeglądarce, bez konta do wypróbowania.
Generator Wideo Quizów TikTok
Narzędzie na żywoZamień dowolny temat w wideo quiz
Generator Wideo Quizów YouTube
Narzędzie na żywoWideo quizy w 16:9 albo w Shorts
Generator Wideo Quizów Językowych
Narzędzie na żywoZamień listę słówek w wideo do nauki języka
Tweet na Wideo
Narzędzie na żywoZamień tweety w wideo
YouTube na Wideo Karaoke
Narzędzie na żywoZamień utwór w wideo karaoke
Post Reddit na Wideo
Narzędzie na żywoZamień wątek z Reddita w shorta
CSV na Wideo Ranking
Narzędzie na żywoZamień arkusz w wideo z rankingiem
Komentarze TikTok na Wideo Piosenkę
Narzędzie na żywoZamień komentarze z TikToka w wideo piosenkę
Filmy AI Owoce
Twórz wirusowe filmy o owocach z AI
Wypuszczaj wideo z napisami ze swojego backendu
Utwórz klucz, uruchom quickstart — pierwsze mp4 z napisami dzieli od ciebie kilka minut.