Subclip Logo

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

FieldRequiredWhat it does
projectIdYesThe ID returned by the upload request.
languageNoASR language, for example en, hi, es, or auto.
templateIdNoDynamic caption template ID. Defaults to bold-clean.
aspectRatioNoFinal video canvas: 9:16, 16:9, or 1:1. Default is 9:16.
placementNotop, middle, or bottom. Default is bottom.
faceTrackNoWhen true, Subclip analyzes face position, shifts captions away from the face, and keeps the face inside the cropped canvas when possible.
outputFileNameNoFinal MP4 filename returned by the download endpoint.
Framing: if aspectRatio changes the canvas shape, Subclip uses the editor's default cover/crop behavior. If faceTrack is 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.

#PreviewtemplateIdNameBest for
1
bold-cleanBold CleanLarge clean words with punchy emphasis.
2
ivory-spotlightSpotlightElegant serif captions with strong focus words.
3
serif-storytellerStorytellerEditorial serif captions for narrative videos.
4
authorityAuthorityCompact premium captions for expert-style videos.
5
minimalistMinimalistTight, simple captions with restrained motion.
6
justifiedJustifiedWider readable lines for dense explanations.
7
minimalist-whiteMinimalist WhiteClean white captions for simple edits.
8
kineticKineticFast animated captions for energetic clips.
9
kinetic-yellowKinetic YellowKinetic captions with yellow emphasis.
10
one-wordOne WordOne 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.