API Reference
Viral Captions API
Upload one video, optionally upload an SRT file, choose a viral 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.
For current credit costs, see API credit costs.
OpenAPI-style reference
API endpoints
Render uploaded videos with viral caption templates, optional SRT input, and face tracking.
/api/v1 are auto-cleaned and do not count toward the user's storage quota./api/v1/dynamic-captions/uploadsCreate signed upload URLs
Creates upload URLs for the source video and optional SRT file.
Parameters
| Field | Type | Required | Details |
|---|---|---|---|
projectName | string | No | Optional project name body |
video.fileName | string | Yes | Video file name body |
video.contentType | string | Yes | video/mp4, video/webm, video/quicktime, video/mpeg, or video/x-matroska body |
video.fileSize | number | Yes | Declared source video size in bytes, max 5GB body |
srt.fileName | string | No | Optional SRT file name body |
srt.fileSize | number | No | Optional SRT size in bytes, max 2MB body |
Examples
Request
{
"projectName": "Captioned reel",
"video": {
"fileName": "source.mp4",
"contentType": "video/mp4",
"fileSize": 52428800
},
"srt": {
"fileName": "source.srt",
"contentType": "text/plain",
"fileSize": 18432
}
}Response
{
"projectId": "dcproj_...",
"uploadExpiresIn": 900,
"video": {
"uploadUrl": "https://...",
"objectKey": "user_.../dynamic-captions/dcproj_.../video-...mp4"
},
"srt": {
"uploadUrl": "https://...",
"objectKey": "user_.../dynamic-captions/dcproj_.../srt-...srt"
}
}Responses
| Status | Description |
|---|---|
200 | Request succeeded |
400 | Invalid request body or unsupported parameter |
401 | Missing, invalid, or revoked API key |
429 | Rate limit exceeded |
500 | Unexpected processing error |
/api/v1/dynamic-captions/jobsStart a Viral Captions render
Starts rendering after the uploaded video is available.
Parameters
| Field | Type | Required | Details |
|---|---|---|---|
projectId | string | Yes | Project ID returned by the upload endpoint body |
language | string | No | Transcription language code |
templateId | string | No | Viral caption template ID bodyTemplate IDs |
placement | top | middle | bottom | No | Caption placement |
faceTrack | boolean | No | Enable face tracking crop where supported |
aspectRatio | 9:16 | 16:9 | 1:1 | No | Output aspect ratio |
Examples
Request
{
"projectId": "dcproj_...",
"language": "en",
"templateId": "bold-clean",
"placement": "bottom",
"faceTrack": false,
"aspectRatio": "9:16"
}Response
{
"projectId": "dcproj_...",
"status": "queued",
"runId": "run_...",
"estimatedCredits": 9,
"statusUrl": "/api/v1/.../jobs/dcproj_...",
"downloadUrl": "/api/v1/.../jobs/dcproj_.../download"
}Responses
| Status | Description |
|---|---|
200 | Request succeeded |
400 | Invalid request body or unsupported parameter |
401 | Missing, invalid, or revoked API key |
429 | Rate limit exceeded |
500 | Unexpected processing error |
/api/v1/dynamic-captions/jobs/{projectId}Get Viral Captions job status
Returns render progress and output metadata.
Parameters
| Field | Type | Required | Details |
|---|---|---|---|
projectId | string | Yes | Viral Captions project ID path |
Examples
Response
{
"projectId": "dcproj_...",
"status": "queued | processing | completed | failed",
"progress": 100,
"outputReady": true,
"creditsUsed": 9,
"errorMessage": null,
"updatedAt": "2026-06-19T14:20:00.000Z"
}Responses
| Status | Description |
|---|---|
200 | Request succeeded |
400 | Invalid request body or unsupported parameter |
401 | Missing, invalid, or revoked API key |
429 | Rate limit exceeded |
500 | Unexpected processing error |
/api/v1/dynamic-captions/jobs/{projectId}/downloadCreate Viral Captions download URL
Returns a signed URL for the rendered MP4 when the job is complete.
Parameters
| Field | Type | Required | Details |
|---|---|---|---|
projectId | string | Yes | Viral Captions project ID path |
Examples
Response
{
"projectId": "dcproj_...",
"downloadUrl": "https://signed-download-url...",
"expiresAt": "2026-06-19T15:00:00.000Z",
"expiresIn": 3600,
"contentType": "video/mp4",
"fileSize": 18345678
}Responses
| Status | Description |
|---|---|
200 | Request succeeded |
400 | Invalid request body or unsupported parameter |
401 | Missing, invalid, or revoked API key |
429 | Rate limit exceeded |
500 | Unexpected processing error |
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 | Viral 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.
Supported ASR languages
Use language only when no SRT is uploaded. auto lets Subclip detect the spoken language.
| Code | Language |
|---|---|
| auto | Auto-detect |
| af | Afrikaans |
| sq | Albanian |
| am | Amharic |
| ar | Arabic |
| hy | Armenian |
| as | Assamese |
| az | Azerbaijani |
| ba | Bashkir |
| eu | Basque |
| be | Belarusian |
| bn | Bengali |
| bs | Bosnian |
| br | Breton |
| bg | Bulgarian |
| ca | Catalan |
| zh | Chinese |
| hr | Croatian |
| cs | Czech |
| da | Danish |
| nl | Dutch |
| en | English |
| et | Estonian |
| fo | Faroese |
| fi | Finnish |
| fr | French |
| gl | Galician |
| ka | Georgian |
| de | German |
| el | Greek |
| gu | Gujarati |
| ht | Haitian Creole |
| ha | Hausa |
| haw | Hawaiian |
| he | Hebrew |
| hi | Hindi |
| hu | Hungarian |
| is | Icelandic |
| id | Indonesian |
| it | Italian |
| ja | Japanese |
| jw | Javanese |
| kn | Kannada |
| kk | Kazakh |
| km | Khmer |
| ko | Korean |
| lo | Lao |
| la | Latin |
| lv | Latvian |
| ln | Lingala |
| lt | Lithuanian |
| lb | Luxembourgish |
| mk | Macedonian |
| mg | Malagasy |
| ms | Malay |
| ml | Malayalam |
| mt | Maltese |
| mi | Maori |
| mr | Marathi |
| mn | Mongolian |
| my | Myanmar |
| ne | Nepali |
| no | Norwegian |
| nn | Nynorsk |
| oc | Occitan |
| ps | Pashto |
| fa | Persian |
| pl | Polish |
| pt | Portuguese |
| pa | Punjabi |
| ro | Romanian |
| ru | Russian |
| sa | Sanskrit |
| sr | Serbian |
| sn | Shona |
| sd | Sindhi |
| si | Sinhala |
| sk | Slovak |
| sl | Slovenian |
| so | Somali |
| es | Spanish |
| su | Sundanese |
| sw | Swahili |
| sv | Swedish |
| tl | Tagalog |
| tg | Tajik |
| ta | Tamil |
| tt | Tatar |
| te | Telugu |
| th | Thai |
| bo | Tibetan |
| tr | Turkish |
| tk | Turkmen |
| uk | Ukrainian |
| ur | Urdu |
| uz | Uzbek |
| vi | Vietnamese |
| cy | Welsh |
| yi | Yiddish |
| yo | Yoruba |
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 | composite | Composite | Large bold captions that invert the video beneath them. | |
| 6 | minimalist | Minimalist | Tight, simple captions with restrained motion. | |
| 7 | minimalist-white | Minimalist White | Clean white captions for simple edits. | |
| 8 | kinetic | Kinetic | Fast animated captions for energetic clips. | |
| 9 | kinetic-yellow | Cinematic | Kinetic captions with yellow emphasis. | |
| 10 | composite-one-word | Composite One Word | Large bold one-word captions with Difference compositing. | |
| 11 | one-word | One Word | One word at a time for strong hook moments. | |
| 12 | justified | Justified | Wider readable lines for dense explanations. |
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 Viral 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.