Subclip Logo

API Reference

AI Clipping API

Create AI-selected or exact-segment clips from a video URL, render them through Subclip, and download the final MP4.

For current credit costs, see API credit costs.

OpenAPI-style reference

API endpoints

Create AI-selected or exact-segment clips and return final rendered clip URLs.

API v1 storage handling: files uploaded or generated through /api/v1are auto-cleaned and do not count toward the user's storage quota.
POST/api/v1/ai-clipping/uploads

Create source or transcript upload URL

Creates a signed upload URL for source video or optional SRT transcript.

Bearer auth

Parameters

FieldTypeRequiredDetails
uploadTypesource | transcript_srtNo
Upload target type
bodydefault: sourceUpload source or SRT
fileNamestringYes
File name
body
contentTypestringNo
Video or SRT content type
body
fileSizenumberYes
Declared size in bytes
body

Examples

Request

{
  "uploadType": "source",
  "fileName": "interview.mp4",
  "contentType": "video/mp4",
  "fileSize": 52428800
}

Response

{
  "uploadUrl": "https://...",
  "sourceObjectKey": "user_.../ai-clipping-api/sources/abc123-interview.mp4",
  "expiresIn": 900
}

Responses

StatusDescription
200Request succeeded
400Invalid request body or unsupported parameter
401Missing, invalid, or revoked API key
429Rate limit exceeded
500Unexpected processing error
POST/api/v1/ai-clipping/jobs

Start AI clipping

Starts AI analysis mode or exact segment mode and renders final clip URLs.

Bearer auth

Parameters

FieldTypeRequiredDetails
videoUrlstringNo
YouTube, public, signed S3/R2, or downloadable URL. Required unless sourceObjectKey is provided
body
sourceObjectKeystringNo
Uploaded source object key. Required unless videoUrl is provided
languagestringNo
Required when no transcript input is provided
youtubeTranscriptbooleanNo
For YouTube URLs, try YouTube transcript first. Adds 1 credit and falls back to ASR
bodydefault: falseAI Clipping options
aiAnalysisbooleanNo
Let AI choose clips. Set false when sending exact segments
bodydefault: true when segments omittedAI Clipping options
presetdefault | face_track | customised | auraNo
Render preset
bodydefault: defaultAI Clipping options
aspectRatioportrait | squareNo
Square is not supported for face_track
bodydefault: portraitAI Clipping options
dynamicCaptionsbooleanNo
When false, no captions are rendered
bodydefault: trueAI Clipping options
numberOfClipsnumberNo
AI analysis mode only, 1-12
bodydefault: autoAI Clipping options
preferredDurationSecondsnumberNo
AI analysis hint, 10-120 seconds
bodydefault: autoAI Clipping options
styleInstructionsstringNo
AI analysis instructions, max 600 characters
segmentsarrayNo
Exact clipping segments when aiAnalysis is false

Examples

Request

{
  "videoUrl": "https://www.youtube.com/watch?v=...",
  "language": "en",
  "youtubeTranscript": true,
  "aiAnalysis": true,
  "preset": "default",
  "aspectRatio": "portrait",
  "dynamicCaptions": true,
  "numberOfClips": 6,
  "preferredDurationSeconds": 45
}

Response

{
  "projectId": "rproj_...",
  "status": "queued",
  "runId": "run_...",
  "estimatedCredits": 9,
  "statusUrl": "/api/v1/.../jobs/rproj_...",
  "downloadUrl": "/api/v1/.../jobs/rproj_.../download"
}

Responses

StatusDescription
200Request succeeded
400Invalid request body or unsupported parameter
401Missing, invalid, or revoked API key
429Rate limit exceeded
500Unexpected processing error
GET/api/v1/ai-clipping/jobs/{projectId}

Get AI clipping job status

Returns job progress and multiple rendered clip download URLs once complete.

Bearer auth

Parameters

FieldTypeRequiredDetails
projectIdstringYes
AI clipping project ID
path

Examples

Response

{
  "projectId": "rproj_...",
  "status": "completed",
  "outputReady": true,
  "clips": [
    {
      "startTimeSeconds": 83.2,
      "endTimeSeconds": 126.8,
      "textHook": "This changed the launch",
      "reason": "Clear reveal",
      "downloadUrl": "https://..."
    },
    {
      "startTimeSeconds": 242.5,
      "endTimeSeconds": 289.1,
      "textHook": "Most teams miss this",
      "reason": "Practical payoff",
      "downloadUrl": "https://..."
    }
  ]
}

Responses

StatusDescription
200Request succeeded
400Invalid request body or unsupported parameter
401Missing, invalid, or revoked API key
429Rate limit exceeded
500Unexpected processing error
GET/api/v1/ai-clipping/jobs/{projectId}/download

Create AI clipping download URLs

Returns refreshed signed URLs for all rendered clips.

Bearer auth

Parameters

FieldTypeRequiredDetails
projectIdstringYes
AI clipping project ID
path

Examples

Response

{
  "projectId": "rproj_...",
  "clips": [
    {
      "downloadUrl": "https://...",
      "expiresAt": "2026-06-19T15:00:00.000Z"
    }
  ]
}

Responses

StatusDescription
200Request succeeded
400Invalid request body or unsupported parameter
401Missing, invalid, or revoked API key
429Rate limit exceeded
500Unexpected processing error

Upload source video to Subclip

This step is optional. It is useful when you do not want to manage S3 storage infrastructure and want Subclip to handle direct client-side uploads.

1. Create an upload URL

curl -X POST https://www.subclip.app/api/v1/ai-clipping/uploads \
  -H "Authorization: Bearer $SUBCLIP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "uploadType": "source",
    "fileName": "interview.mp4",
    "contentType": "video/mp4",
    "fileSize": 52428800
  }'
{
  "uploadUrl": "https://...",
  "method": "PUT",
  "sourceObjectKey": "user_.../ai-clipping-api/sources/abc123-interview.mp4",
  "sourceObjectName": "interview.mp4",
  "sourceObjectMimeType": "video/mp4",
  "expiresIn": 900
}

2. Upload the file

curl -X PUT "$SOURCE_UPLOAD_URL" \
  -H "Content-Type: video/mp4" \
  -H "Content-Length: 52428800" \
  --data-binary "@interview.mp4"

3. Start the job with sourceObjectKey

curl -X POST https://www.subclip.app/api/v1/ai-clipping/jobs \
  -H "Authorization: Bearer $SUBCLIP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "sourceObjectKey": "user_.../ai-clipping-api/sources/abc123-interview.mp4",
    "sourceObjectName": "interview.mp4",
    "sourceObjectMimeType": "video/mp4",
    "language": "en",
    "preset": "default",
    "aspectRatio": "portrait",
    "numberOfClips": 6
  }'

Start a job

Use a public or signed source URL. YouTube URLs are imported by Subclip before processing. If you do not provide a transcript, pass language so Subclip can transcribe the source audio.

For YouTube URLs, set youtubeTranscript to true when you want Subclip to try YouTube captions first. If that fetch fails, the job falls back to Subclip transcription.

Use aiAnalysis true to let Subclip choose clips. Omit numberOfClips and preferredDurationSeconds for automatic selection, or pass numberOfClips from 1-12 and preferredDurationSeconds from 10-120 when you want a specific count and duration hint.

curl -X POST https://www.subclip.app/api/v1/ai-clipping/jobs \
  -H "Authorization: Bearer $SUBCLIP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "videoUrl": "https://www.youtube.com/watch?v=...",
    "projectName": "Podcast clips",
    "language": "en",
    "youtubeTranscript": true,
    "aiAnalysis": true,
    "preset": "default",
    "aspectRatio": "portrait",
    "dynamicCaptions": true,
    "numberOfClips": 6,
    "preferredDurationSeconds": 45,
    "styleInstructions": "Prioritize controversial takes, quick emotional hooks, and complete payoff endings.",
    "addTextHook": true,
    "addCtaText": true
  }'
{
  "projectId": "rproj_...",
  "status": "queued",
  "runId": "run_...",
  "statusUrl": "/api/v1/ai-clipping/jobs/rproj_...",
  "downloadUrl": "/api/v1/ai-clipping/jobs/rproj_.../download"
}

Input and output schema

The API returns the rendered clip montage and individual rendered MP4 URLs for each selected segment. It does not return the original source video URL.

Input

{
  "videoUrl": "string optional, required unless sourceObjectKey",
  "sourceObjectKey": "string optional, required unless videoUrl",
  "sourceObjectName": "string optional",
  "sourceObjectMimeType": "string optional",
  "projectName": "string optional",
  "language": "string required when no transcript input is provided",
  "youtubeTranscript": "boolean optional, YouTube URLs only, default false",
  "aiAnalysis": "boolean optional, inferred from segments when omitted",
  "preset": "face_track | default | customised | aura",
  "aspectRatio": "portrait | square",
  "dynamicCaptions": "boolean optional, default true",
  "numberOfClips": "number optional, 1-12, AI analysis mode only",
  "maxClips": "number optional legacy alias for numberOfClips, 1-12",
  "preferredDurationSeconds": "number optional, 10-120, AI analysis mode only",
  "styleInstructions": "string optional, max 600 chars, AI analysis mode only",
  "textHook": "string optional",
  "ctaText": "string optional",
  "addTextHook": "boolean optional",
  "addCtaText": "boolean optional",
  "transcript": {
    "text": "string optional",
    "segments": [
      {
        "start": "number",
        "end": "number",
        "text": "string",
        "words": [
          { "word": "string", "start": "number", "end": "number" }
        ]
      }
    ]
  },
  "transcriptSrtUrl": "string optional",
  "transcriptSrtObjectKey": "string optional",
  "segments": [
    {
      "startTimeSeconds": "number",
      "endTimeSeconds": "number",
      "textHook": "string optional",
      "ctaText": "string optional",
      "addTextHook": "boolean optional",
      "addCtaText": "boolean optional"
    }
  ]
}

Status response

{
  "projectId": "rproj_...",
  "projectName": "Podcast clips",
  "status": "queued | processing | completed | failed",
  "progress": 100,
  "label": "AI clipping video ready",
  "outputReady": true,
  "clips": [
    {
      "startTimeSeconds": 83.2,
      "endTimeSeconds": 126.8,
      "outputStartTimeSeconds": 0,
      "outputEndTimeSeconds": 43.6,
      "textHook": "This changed the whole launch",
      "ctaText": "Follow for the full breakdown",
      "reason": "Clear pain point with a strong reveal",
      "score": 92,
      "downloadUrl": "https://...",
      "downloadUrlExpiresAt": "2026-06-19T15:00:00.000Z",
      "fileSize": 18345678,
      "contentType": "video/mp4"
    },
    {
      "startTimeSeconds": 242.5,
      "endTimeSeconds": 289.1,
      "outputStartTimeSeconds": 44.4,
      "outputEndTimeSeconds": 91,
      "textHook": "Most teams miss this step",
      "ctaText": "Save this before your next launch",
      "reason": "Practical takeaway with a clear before and after",
      "score": 88,
      "downloadUrl": "https://...",
      "downloadUrlExpiresAt": "2026-06-19T15:00:00.000Z",
      "fileSize": 20123456,
      "contentType": "video/mp4"
    }
  ],
  "creditsUsed": 6,
  "aiAnalysis": true,
  "transcriptSource": "youtube | asr | provided | srt_upload | srt_url | null",
  "youtubeTranscript": true,
  "youtubeTranscriptStatus": "queued | fetching | fetched | failed_fallback_asr | disabled | null",
  "youtubeTranscriptCreditsUsed": 1,
  "youtubeTranscriptError": null,
  "errorMessage": null,
  "runId": "run_...",
  "renderId": "render_...",
  "createdAt": "2026-06-19T14:00:00.000Z",
  "updatedAt": "2026-06-19T14:20:00.000Z"
}

Download response

{
  "projectId": "rproj_...",
  "downloadUrl": "https://...",
  "expiresAt": "2026-06-19T15:00:00.000Z",
  "expiresIn": 3600,
  "mediaType": "video",
  "contentType": "video/mp4",
  "fileSize": 73456789,
  "fileName": "Podcast clips.mp4",
  "clips": [
    {
      "startTimeSeconds": 83.2,
      "endTimeSeconds": 126.8,
      "outputStartTimeSeconds": 0,
      "outputEndTimeSeconds": 43.6,
      "textHook": "This changed the whole launch",
      "ctaText": "Follow for the full breakdown",
      "reason": "Clear pain point with a strong reveal",
      "score": 92,
      "downloadUrl": "https://...",
      "downloadUrlExpiresAt": "2026-06-19T15:00:00.000Z",
      "fileSize": 18345678,
      "contentType": "video/mp4"
    },
    {
      "startTimeSeconds": 242.5,
      "endTimeSeconds": 289.1,
      "outputStartTimeSeconds": 44.4,
      "outputEndTimeSeconds": 91,
      "textHook": "Most teams miss this step",
      "ctaText": "Save this before your next launch",
      "reason": "Practical takeaway with a clear before and after",
      "score": 88,
      "downloadUrl": "https://...",
      "downloadUrlExpiresAt": "2026-06-19T15:00:00.000Z",
      "fileSize": 20123456,
      "contentType": "video/mp4"
    }
  ]
}

Supported ASR languages

When no transcript input is provided, language is required. auto is accepted when you want language detection.

CodeLanguage
autoAuto-detect
afAfrikaans
sqAlbanian
amAmharic
arArabic
hyArmenian
asAssamese
azAzerbaijani
baBashkir
euBasque
beBelarusian
bnBengali
bsBosnian
brBreton
bgBulgarian
caCatalan
zhChinese
hrCroatian
csCzech
daDanish
nlDutch
enEnglish
etEstonian
foFaroese
fiFinnish
frFrench
glGalician
kaGeorgian
deGerman
elGreek
guGujarati
htHaitian Creole
haHausa
hawHawaiian
heHebrew
hiHindi
huHungarian
isIcelandic
idIndonesian
itItalian
jaJapanese
jwJavanese
knKannada
kkKazakh
kmKhmer
koKorean
loLao
laLatin
lvLatvian
lnLingala
ltLithuanian
lbLuxembourgish
mkMacedonian
mgMalagasy
msMalay
mlMalayalam
mtMaltese
miMaori
mrMarathi
mnMongolian
myMyanmar
neNepali
noNorwegian
nnNynorsk
ocOccitan
psPashto
faPersian
plPolish
ptPortuguese
paPunjabi
roRomanian
ruRussian
saSanskrit
srSerbian
snShona
sdSindhi
siSinhala
skSlovak
slSlovenian
soSomali
esSpanish
suSundanese
swSwahili
svSwedish
tlTagalog
tgTajik
taTamil
ttTatar
teTelugu
thThai
boTibetan
trTurkish
tkTurkmen
ukUkrainian
urUrdu
uzUzbek
viVietnamese
cyWelsh
yiYiddish
yoYoruba

Upload an SRT transcript

If your transcript is a local SRT file, upload it to Subclip storage first, then pass the returned transcriptSrtObjectKey when starting the job.

curl -X POST https://www.subclip.app/api/v1/ai-clipping/uploads \
  -H "Authorization: Bearer $SUBCLIP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "uploadType": "transcript_srt",
    "fileName": "podcast.srt",
    "contentType": "text/plain",
    "fileSize": 18432
  }'
{
  "uploadUrl": "https://...",
  "method": "PUT",
  "transcriptSrtObjectKey": "user_.../ai-clipping-api/transcripts/abc123-podcast.srt",
  "expiresIn": 900
}
curl -X PUT "$TRANSCRIPT_UPLOAD_URL" \
  -H "Content-Type: text/plain" \
  --data-binary "@podcast.srt"

Exact segments

Pass aiAnalysis false and segments when you already know the clips. Omit AI-only selection fields such as numberOfClips, preferredDurationSeconds, and styleInstructions in this mode.

curl -X POST https://www.subclip.app/api/v1/ai-clipping/jobs \
  -H "Authorization: Bearer $SUBCLIP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "videoUrl": "https://your-bucket.s3.amazonaws.com/interview.mp4?X-Amz-Signature=...",
    "aiAnalysis": false,
    "preset": "customised",
    "aspectRatio": "portrait",
    "transcriptSrtObjectKey": "user_.../ai-clipping-api/transcripts/abc123-podcast.srt",
    "ctaText": "Follow for the full breakdown",
    "segments": [
      {
        "startTimeSeconds": 83.2,
        "endTimeSeconds": 126.8,
        "textHook": "This changed the whole launch"
      }
    ]
  }'

Options

FieldRequiredNotes
videoUrlYes, unless sourceObjectKeyPublic or signed HTTP(S) video URL, public S3/R2 URL, or YouTube URL.
sourceObjectKeyYes, unless videoUrlObject key returned by POST /api/v1/ai-clipping/uploads with uploadType source.
sourceObjectNameNoOriginal uploaded source filename.
sourceObjectMimeTypeNoSource video MIME type.
languageConditionalRequired when no transcript, transcriptSrtUrl, or transcriptSrtObjectKey is provided.
youtubeTranscriptNoYouTube videoUrl only. When true, Subclip tries YouTube captions first. If fetched, the completed job includes 1 extra API credit. If fetch fails, Subclip falls back to ASR.
aiAnalysisNotrue lets Subclip choose clips. false requires segments. When omitted, Subclip infers the mode from whether segments are provided.
presetNoface_track, default, customised, or aura. Defaults to default.
aspectRatioNoportrait or square. Defaults to portrait. Square is rejected for face_track.
dynamicCaptionsNoDefaults to true.
numberOfClipsNoDesired AI-selected clip count, 1-12. Omit for automatic selection.
maxClipsNoLegacy alias for numberOfClips. Pass only one of them.
preferredDurationSecondsNoPreferred duration per AI-selected clip, 10-120 seconds. This is a hint, not a hard trim. Omit for automatic duration.
styleInstructionsNoCustom instructions for how AI should select clips, max 600 characters.
transcriptNoOptional structured transcript segments to avoid ASR.
transcriptSrtUrlNoPublic or signed HTTP(S) URL for an SRT transcript.
transcriptSrtObjectKeyNoObject key returned by POST /api/v1/ai-clipping/uploads after uploading an SRT to Subclip storage.
segmentsNoExact clips with startTimeSeconds and endTimeSeconds.
textHookNoTop-level hook for the first selected segment.
ctaTextNoCTA text used in CTA-capable presets.
addTextHookNoGenerate missing hooks for selected segments.
addCtaTextNoGenerate missing CTA text for clips.

Poll status

Poll every 10 to 15 seconds. The output is ready when outputReady is true. Completed responses include rendered MP4 URLs in clips[].downloadUrl.

curl https://www.subclip.app/api/v1/ai-clipping/jobs/rproj_... \
  -H "Authorization: Bearer $SUBCLIP_API_KEY"

Download

The download endpoint returns JSON with a short-lived signed URL for the full rendered montage plus one signed URL per rendered clip segment.

DOWNLOAD_JSON=$(curl -s https://www.subclip.app/api/v1/ai-clipping/jobs/rproj_.../download \
  -H "Authorization: Bearer $SUBCLIP_API_KEY")

DOWNLOAD_URL=$(echo "$DOWNLOAD_JSON" | jq -r '.downloadUrl')
curl -L "$DOWNLOAD_URL" -o ai-clips.mp4