貼上、填入金鑰、執行。這裡沒有任何虛擬碼。
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)基底網址:https://autostud.ai/api/v1 — 以 `Authorization: Bearer sk_live_…` 驗證。`sk_test_` 金鑰會走完全相同的路徑,並在花費之前停下。
六個步驟,其中四個各只是一次請求。
後台 → 設定 → API 金鑰。不想自己挑 scope 就選「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 讀的是網址。你的儲存桶、你的 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` 把工作丟到 worker 叢集,並立刻回傳 `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`,完成的網址會帶簽名推給你。想自己拉也行:列出該影片的算圖並讀 `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.Scope 指的是金鑰必須具備的權限,不是你要傳的欄位。
| 呼叫 | Scope | 作用 |
|---|---|---|
| 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 | 把算圖排進 worker 叢集。20 點數。 |
| GET /api/v1/renders?video_id=… | renders:read | 某支影片的算圖列表(最新在前),含狀態與網址。 |
| 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 | 金鑰少了 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>.<原始 body>"` 做的 HMAC-SHA256。請在解析之前,用原始 body 驗證。
約兩小時內重試五次,而且投遞紀錄在第一次嘗試前就寫下了。連續失敗二十次的端點會被停用,而不是繼續被打。
每次嘗試都帶 `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 加過字幕的影片,在編輯器裡打開就跟其他影片一樣。手動改一個字、重新算圖、繼續自動化 — 兩條路寫的是同一份文件。
冪等金鑰、有型別的錯誤、測試金鑰、簽章 webhook,還有 30 天的請求紀錄。API 無聊的那一半,也就是凌晨三點會感謝的那一半。
不行,而且是刻意的:API 收的是網址。把影片放在我們伺服器讀得到的地方 — 你的儲存桶、CDN、簽名連結 — 再用 `media_url` 傳過來。從後台上傳的檔案本來就有可用的網址。
目前沒有。字幕是壓進 mp4 的,這正是短影音平台需要的形式。字詞與時間點會存在影片上,所以要另存字幕檔,可以用 API 回傳的資料在你這邊自行轉換。
一次算圖固定 20 點數,不論長度。轉寫與強調處理會像其他 AI 呼叫一樣記錄在你的工作區,`GET /api/v1/usage` 會告訴你某把金鑰這期用了多少。
是幾分鐘,不是幾秒:它跑在 worker 叢集上,不在請求裡。`render.completed` 的存在正是為此 — 請訂閱,而不是一直掛著連線。
可以。用 `restyle_only: true` 加上另一個 `style_preset` 再呼叫一次 `subtitles.generate`:它會重用影片上已存的字詞,不會再轉寫一遍。
Whisper 會自己偵測語言,字幕就用實際說的語言回來 — 不用宣告。偵測到的語言會在回應裡,方便你分支處理。