Clipora API
Programmatically turn long videos into face-aware, captioned 9:16 reels. REST endpoints for upload, job status, and clip download.
https://your-host/api
JSON over HTTP
Cookie auth
Quickstart
Three calls: generate a key → upload → fetch clips.
-
STEP 1 · Generate an API key
Sign in, then visit /keys to create a key. Format:
ck_live_…. The plaintext is shown ONCE — store it as$CLIPORA_API_KEY.# Export the key into your shell so the examples below pick it up export CLIPORA_API_KEY="ck_live_AbCdEf...your-key-here"
-
STEP 2 · Upload a video
curl -X POST https://your-host/api/upload \ -H "X-API-Key: $CLIPORA_API_KEY" \ -F "video=@/path/to/video.mp4" \ -F 'options={"numClips":3,"captionPreset":"mozi","aspectRatio":"9:16"}' # → {"jobId":"abc123…","wsUrl":"/ws?jobId=abc123…",...}
-
STEP 3 · Wait for completion, then fetch clips
# Poll job status, then fetch clips when status is completed curl https://your-host/api/jobs/abc123 \ -H "X-API-Key: $CLIPORA_API_KEY" curl https://your-host/api/clips/abc123 \ -H "X-API-Key: $CLIPORA_API_KEY"
Authentication
Protected API routes require authentication. Use an API key
for server-to-server calls (Business plan), or the session cookie
from the login pages for browser apps. Public routes: /api/billing/plans
and auth signup/login.
API key (recommended for server-to-server)
Create a key at /keys. Pass it as either of:
X-API-Key: ck_live_AbCdEf...your-key-here # or, equivalently: Authorization: Bearer ck_live_AbCdEf...your-key-here
- Keys are 256-bit random tokens, SHA-256 hashed at rest — the plaintext is shown once at creation
- Up to 10 active keys per account; revoke unused ones from /keys
- Revoked or wrong keys return
401immediately (no fallback) - API keys cannot manage other keys, billing checkout, or billing portal — session only
- Upload/jobs/clips/downloads are scoped to your account; clip files cannot be read without auth
/ws?jobId=…requires the same auth (cookie orX-API-Key) and job ownership- Keep keys secret — never commit them to git or expose them in frontend JavaScript
Cookie session (browser-side apps)
The login pages set a httpOnly cookie named reels_session, automatically
sent by any browser on same-origin requests. Use this for in-browser apps and dashboards;
use API keys for everything else.
Errors
All errors return JSON with an error field:
{ "error": "video file required" }
Upload API
Upload a video file (multipart/form-data). The response includes a
jobId — poll GET /api/jobs/{jobId} until the job completes.
| Field | Type | Required | Description |
|---|---|---|---|
| video | file | Required | Video file. Up to 1 GB by default. MP4 / MOV / MKV / WEBM accepted. |
| options | string (JSON) | Optional | JSON-encoded ProcessOptions (see table below). |
| Key | Type | Default | Description |
|---|---|---|---|
| mode | "ai" | "none" | "ai" | ai = pick viral moments; none = one full-source clip. |
| numClips | integer | 3 | Clips to generate. Range 1–10. |
| clipLength | "short" | "medium" | "long" | "medium" | 3–15 s · 20–45 s · 45–90 s. |
| aspectRatio | "9:16" | "1:1" | "4:5" | "16:9" | "9:16" | Output dimensions. |
| captionPreset | "none" | "beasty" | "karaoke" | "mozi" | "none" | Burned-in caption style. See presets reference. |
| removeAiVoice | boolean | true | Splice out AI-host/interviewer turns when the source is an interview. |
| autoHook | boolean | true | Let Gemini write a punchy hook + title + hashtags. |
| prompt | string | "" | Free-form instruction, e.g. "Focus on moments about hiring." |
| genre | string | "auto" | "podcast" | "interview" | "vlog" | "tutorial" | "auto". |
| trimStart | number (seconds) | 0 | Cut the source before this timestamp before analyzing. |
| trimEnd | number (seconds) | null | Cut the source after this timestamp. |
{
"jobId": "K3pX9aB2mZ",
"originalName": "interview.mp4",
"sizeBytes": 82345677,
"wsUrl": "/ws?jobId=K3pX9aB2mZ",
"options": { /* what the server actually used */ }
}
Submit a video by URL. Direct media links (.mp4 / .mov) stream-download;
site-hosted links (YouTube, TikTok, Instagram, Twitter, Twitch — ~1000 sites)
are fetched via yt-dlp. Returns immediately with a
jobId while the video downloads in the background — poll
GET /api/jobs/{jobId} for status.
| Field | Type | Required | Description |
|---|---|---|---|
| url | string | Required | Public HTTP/HTTPS URL to the video. |
| options | object | Optional | Same shape as /api/upload's options. |
curl -X POST https://your-host/api/upload/url \ -H "Content-Type: application/json" -H "X-API-Key: $CLIPORA_API_KEY" \ -d '{"url":"https://youtube.com/watch?v=…","options":{"numClips":4}}'
Jobs API
List all jobs for the current user, newest first.
{
"jobs": [
{
"id": "K3pX9aB2mZ",
"originalName": "interview.mp4",
"status": "completed",
"createdAt": 1734567890000,
"clipCount": 3,
"posterUrl": "/api/clips/K3pX9aB2mZ/clip_01.jpg"
}
]
}
queued · downloading · processing · completed · failed
Fetch a single job's status + metadata.
Clips API
Get the generated-clips manifest for a job. Returns 404 until the job is completed.
{
"jobId": "K3pX9aB2mZ",
"duration": 312.4,
"clipCount": 3,
"clips": [
{
"index": 1,
"start": 52.4,
"end": 71.1,
"duration": 18.7,
"score": 9.1,
"transcript": "Here's the thing nobody tells you…",
"downloadUrl": "/api/clips/K3pX9aB2mZ/clip_01.mp4",
"posterUrl": "/api/clips/K3pX9aB2mZ/clip_01.jpg",
"aspectRatio": "9:16",
"metadata": {
"title": "The hiring myth nobody talks about",
"viral_hook": "Stop interviewing for skills.",
"key_insight": "Hire for slope, not intercept.",
"dominant_emotion": "excitement",
"hashtags": ["hiring", "startups", "interview"],
"viral_score": 9.1
}
}
]
}
Stream a generated artifact. Allowed filenames: clip_NN.mp4 (rendered clip),
clip_NN.jpg (poster), clip_NN.ass (caption track),
manifest.json (the manifest above). Served with long-lived cache headers.