API Reference
Dynamic Captions API
Upload one video, optionally upload an SRT file, choose a dynamic caption template, and Subclip renders a final MP4 with animated captions.
Simple rule: if you upload SRT, Subclip uses it. If you do not upload SRT, Subclip runs ASR on the video audio using the selected language.
1. Create an upload request
Send video metadata. Add srt only when you want to provide your own captions.
curl -X POST https://www.subclip.app/api/v1/dynamic-captions/uploads \
-H "Authorization: Bearer $SUBCLIP_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"projectName": "captioned-launch-video",
"video": {
"fileName": "launch.mp4",
"contentType": "video/mp4",
"fileSize": 52428800,
"durationSeconds": 42,
"width": 1080,
"height": 1920
},
"srt": {
"fileName": "launch.srt",
"contentType": "text/plain",
"fileSize": 18200
}
}'{
"projectId": "dcproj_...",
"uploadExpiresIn": 900,
"video": {
"uploadUrl": "https://...",
"objectKey": "user/dynamic-captions/dcproj_.../video-...",
"contentType": "video/mp4",
"expiresIn": 900
},
"srt": {
"uploadUrl": "https://...",
"objectKey": "user/dynamic-captions/dcproj_.../srt-...",
"expiresIn": 900
}
}2. Upload the video and optional SRT
Upload each file to its returned signed URL. Content-Length must match the actual file size. cURL usually sets it automatically, but Node streams need it explicitly.
curl -X PUT "$VIDEO_UPLOAD_URL" \ -H "Content-Type: video/mp4" \ -H "Content-Length: 52428800" \ --data-binary "@launch.mp4" curl -X PUT "$SRT_UPLOAD_URL" \ -H "Content-Type: text/plain" \ -H "Content-Length: 18200" \ --data-binary "@launch.srt"
// Node streamed uploads need Content-Length and duplex.
await fetch(upload.video.uploadUrl, {
method: "PUT",
headers: {
"Content-Type": "video/mp4",
"Content-Length": String(fileStats.size),
},
body: createReadStream("./launch.mp4"),
duplex: "half",
});3. Start the caption render
Choose language, template, placement, and face tracking. Credits are checked before starting and deducted only after successful render.
curl -X POST https://www.subclip.app/api/v1/dynamic-captions/jobs \
-H "Authorization: Bearer $SUBCLIP_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"projectId": "dcproj_...",
"language": "en",
"templateId": "bold-clean",
"aspectRatio": "9:16",
"placement": "bottom",
"faceTrack": true,
"outputFileName": "launch-captions.mp4"
}'4. Poll status
Poll every 5 seconds for normal videos. For longer videos, poll every 15 seconds. Do not poll in a tight loop; if you receive 429 rate_limited, wait until X-RateLimit-Reset before trying again. The output is ready when outputReady is true.
curl https://www.subclip.app/api/v1/dynamic-captions/jobs/dcproj_... \ -H "Authorization: Bearer $SUBCLIP_API_KEY"
5. Download the result
The Subclip endpoint returns JSON with a short-lived signed download URL. Download that URL to get the MP4 bytes.
DOWNLOAD_JSON=$(curl -s https://www.subclip.app/api/v1/dynamic-captions/jobs/dcproj_.../download \ -H "Authorization: Bearer $SUBCLIP_API_KEY") DOWNLOAD_URL=$(echo "$DOWNLOAD_JSON" | jq -r '.downloadUrl') curl -L "$DOWNLOAD_URL" -o captioned.mp4
Options
| Field | Required | What it does |
|---|---|---|
| projectId | Yes | The ID returned by the upload request. |
| language | No | ASR language, for example en, hi, es, or auto. |
| templateId | No | Dynamic caption template ID. Defaults to bold-clean. |
| aspectRatio | No | Final video canvas: 9:16, 16:9, or 1:1. Default is 9:16. |
| placement | No | top, middle, or bottom. Default is bottom. |
| faceTrack | No | When true, Subclip analyzes face position, shifts captions away from the face, and keeps the face inside the cropped canvas when possible. |
| outputFileName | No | Final MP4 filename returned by the download endpoint. |
Framing: ifaspectRatiochanges the canvas shape, Subclip uses the editor's default cover/crop behavior. IffaceTrackis enabled and a face is detected, the crop is nudged to keep that face visible. If no face is found, the normal centered crop is used.
Template IDs
Pass one of these values in templateId.
| # | Preview | templateId | Name | Best for |
|---|---|---|---|---|
| 1 | bold-clean | Bold Clean | Large clean words with punchy emphasis. | |
| 2 | ivory-spotlight | Spotlight | Elegant serif captions with strong focus words. | |
| 3 | serif-storyteller | Storyteller | Editorial serif captions for narrative videos. | |
| 4 | authority | Authority | Compact premium captions for expert-style videos. | |
| 5 | minimalist | Minimalist | Tight, simple captions with restrained motion. | |
| 6 | justified | Justified | Wider readable lines for dense explanations. | |
| 7 | minimalist-white | Minimalist White | Clean white captions for simple edits. | |
| 8 | kinetic | Kinetic | Fast animated captions for energetic clips. | |
| 9 | kinetic-yellow | Kinetic Yellow | Kinetic captions with yellow emphasis. | |
| 10 | one-word | One Word | One word at a time for strong hook moments. |
Errors
400 invalid_request: unsupported field, wrong template, bad filename, or malformed JSON.
401 invalid_api_key: missing, invalid, revoked, or missing dynamic_captions permission. Regenerate the key if it was created before Dynamic Captions access existed.
402 not_enough_credits: the user does not have enough AI credits.
409 output_not_ready: poll status before downloading.
507 storage_quota_exceeded: not enough storage for the uploaded video/SRT.
Inputs and outputs are scheduled for deletion after completion so storage quota is freed. Get your key from Developer Portal.