붙여넣고, 키만 넣고, 실행하세요. 여기에 의사코드는 없습니다.
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: https://autostud.ai/api/v1 — 인증은 `Authorization: Bearer sk_live_…`. `sk_test_` 키는 완전히 같은 경로를 지나고 소모 직전에 멈춥니다.
여섯 단계, 그중 네 개는 요청 한 번입니다.
대시보드 → 설정 → API 키. 스코프를 직접 고르지 않을 거라면 "Automation" 프리셋이면 됩니다(영상·렌더·파일 포함). 시크릿은 한 번만 표시됩니다. `sk_live_`는 소모하고, `sk_test_`는 요청 전체를 검증한 뒤 쓰기 직전에 멈춥니다.
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"업로드 엔드포인트는 없습니다. API는 URL을 읽습니다. 자체 버킷, CDN, 서명된 링크 — 저희 서버가 가져올 수 있으면 무엇이든 됩니다. 워크스페이스 라이브러리 등록은 선택입니다.
# 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"
}'`videos.create`가 반환하는 `video_id`에 나머지 전부가 붙습니다. `Idempotency-Key`를 보내면 재시도해도 두 번째 프로젝트가 생기지 않고 첫 응답이 돌아옵니다.
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"
}'`subtitles.generate`가 서버에서 전부 처리합니다. Whisper가 단어 단위로 전사하고, AI 패스가 강조할 단어를 표시하고, 116가지 스타일 중 하나가 적용됩니다. 응답에는 단어 수, 정확한 길이, 감지된 언어가 담깁니다.
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
}'`renders.create`가 워커 플릿에 작업을 올리고 즉시 `render_id`를 돌려줍니다. 렌더 한 번은 길이와 무관하게 20크레딧입니다.
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"
}'`render.completed`를 구독하면 완성 URL이 서명된 채로 전달됩니다. 직접 가져오려면 해당 영상의 렌더 목록에서 `render_status`가 `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.스코프는 키가 가져야 할 권한이지, 보내는 값이 아닙니다.
| 호출 | 스코프 | 하는 일 |
|---|---|---|
| GET /api/v1 | - | 디스커버리: 이 키로 무엇이 가능한지, 한도와 사용량. |
| POST /api/v1/actions/videos.create | videos:write | 영상 프로젝트를 만들고 식별자를 반환합니다. |
| POST /api/v1/actions/subtitles.generate | videos:execute | 전사·강조·스타일을 적용하고 자막 트랙을 저장합니다. |
| POST /api/v1/actions/renders.create | renders:execute | 워커 플릿에 렌더를 큐잉합니다. 20크레딧. |
| GET /api/v1/renders?video_id=… | renders:read | 한 영상의 렌더 목록(최신순), 상태와 URL 포함. |
| POST /api/v1/webhooks | webhooks:write | https 엔드포인트를 등록하고 서명 시크릿을 한 번만 반환합니다. |
`style_preset`에 하나만 넘기면 됩니다. 각 프리셋이 폰트·외곽선·강조색·애니메이션을 한꺼번에 정하므로 따로 설정할 것이 없습니다.
default4 단어 · simplebold3 단어 · simpleimpact3 단어 · focus_on_one_wordbeast1 단어 · focus_on_one_wordkaraoke5 단어 · focus_on_one_wordkaraoke_fill5 단어 · karaokeneon4 단어 · progressively_visibleminimal6 단어 · simplepop5 단어 · progressively_visiblepill5 단어 · simplecinematic9 단어 · simple모든 오류는 같은 형태입니다. 분기용 고정 `code`, 고치는 호출을 짚어주는 `hint`, 문의할 때 쓰는 `request_id`. 전체 코드 목록은 /docs/api/errors 에 있습니다.
| 코드 | 의미 |
|---|---|
| 401 missing_credentials | 요청에 키가 없거나, 우리가 모르는 키입니다. |
| 403 insufficient_scope | 키에 스코프가 없습니다. `details.required_scopes` 가 알려줍니다. |
| 422 validation_failed | 필드가 잘못됐습니다. `details.issues` 에 전부 나옵니다. |
| 402 insufficient_credits | 렌더에 쓸 크레딧이 부족합니다. 충전하거나 테스트 키를 쓰세요. |
| 429 rate_limit_exceeded | 호출이 너무 잦습니다. `Retry-After` 초 기다린 뒤 재시도하세요. |
| 502 upstream_error | 우리가 의존하는 제공사가 실패했습니다. 백오프 후 재시도하세요. |
{
"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"
}
}렌더는 몇 분 걸립니다. 결과가 오게 하세요.
`X-Autostud-Signature: t=<unix>,v1=<hex>`는 `"<timestamp>.<원본 본문>"`에 대한 HMAC-SHA256입니다. 파싱하기 전에 원본 본문으로 검증하세요.
약 두 시간에 걸쳐 다섯 번 시도하고, 전송 레코드는 첫 시도 전에 기록됩니다. 스무 번 연속 실패한 엔드포인트는 계속 두드리지 않고 비활성화합니다.
모든 시도에 `X-Autostud-Delivery`가 붙습니다. 같은 ID면 같은 이벤트이니, 처리 작업의 기본 키로 쓰세요.
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')
})다시 만들기 어려운 부분이 바로 저희가 운영하는 부분입니다.
단어마다 시작과 끝이 있어 하이라이트가 음절에 붙습니다. 문장 단위 타이밍이 대부분의 자동 자막을 늦어 보이게 만듭니다.
깔끔한 방송용부터 튀는 하이라이트 스타일까지. 프리셋이 폰트·외곽선·색·애니메이션을 함께 잡고 캔버스에 맞춰 스케일합니다.
두 번째 패스가 전사를 읽고 의미를 지닌 단어를 표시합니다. 군더더기 말은 조용히 둡니다. 불리언 하나로 끌 수 있습니다.
화자 분리는 플래그 하나입니다. 인터뷰는 목소리마다 색이 달라져 돌아오므로, 소리 없이도 누가 말하는지 압니다.
API로 자막을 넣은 영상도 에디터에서 똑같이 열립니다. 단어를 손으로 고치고 다시 렌더하고, 자동화는 계속 — 두 경로가 같은 문서를 씁니다.
멱등 키, 타입 있는 에러, 테스트 키, 서명된 웹훅, 30일 요청 로그. API에서 지루한 절반, 즉 새벽 3시에 체감하는 절반입니다.
아니요, 의도한 설계입니다. API는 URL을 받습니다. 저희 서버가 읽을 수 있는 곳(자체 버킷, CDN, 서명된 링크)에 올리고 `media_url`로 넘기세요. 대시보드로 업로드한 파일에는 이미 쓸 수 있는 URL이 있습니다.
지금은 아닙니다. 자막은 mp4에 입혀집니다 — 숏폼 플랫폼이 필요로 하는 형태죠. 단어와 타이밍 목록은 영상에 저장되므로, 별도 파일은 API 응답으로 직접 만들 수 있습니다.
렌더는 길이와 상관없이 고정 20크레딧입니다. 전사와 강조 패스는 다른 AI 호출과 똑같이 워크스페이스에 기록되고, `GET /api/v1/usage`가 해당 기간 키 사용량을 알려줍니다.
초가 아니라 분 단위입니다. 요청 안이 아니라 워커 플릿에서 돌기 때문이죠. `render.completed`가 있는 이유가 정확히 이것입니다 — 연결을 붙잡지 말고 구독하세요.
네. `restyle_only: true`와 다른 `style_preset`으로 `subtitles.generate`를 다시 호출하면 영상에 저장된 단어를 재사용하므로 두 번 전사되지 않습니다.
Whisper가 언어를 스스로 감지하고, 자막은 실제로 말해진 언어로 나옵니다 — 선언할 것이 없습니다. 감지된 언어는 응답에 담겨 있어 분기에 쓸 수 있습니다.
모두 브라우저에서 동작하며, 체험에 계정이 필요 없습니다.