Shorts Creator
Turn a long video into restyled, viral-ready vertical shorts — AI-picked moments, style presets, and burned-in subtitles.
The Shorts Creator turns one long video (up to 30 minutes) into up to 5 short-form vertical clips. It is a two-phase, job-based flow:
- Analyze — the AI watches your video and picks the best moments (each with a title, hook, virality score, and "accent beats" — the strongest sub-sections).
- Render — you pick moments, a visual style preset, and a mode, and the API restyles each moment into a finished short.
Key semantics:
accentsmode restyles only the strongest beats of the moment — cheaper.fullmode restyles the entire clip — pricier.- The restyle never adds text to the video. On-screen text comes only from the optional subtitles step (burned-in VEED subtitles, style preset default
"glass"). - The source video must be a Kolbo media-library URL — upload local/external files first with
POST /v1/media/upload.
Pricing
| Item | Credits |
|---|---|
| Analysis | Flat 15 credits per job |
| Restyled chunk (Gemini Omni) | Flat 200 credits per chunk |
| Burned-in subtitles (VEED) | 40 credits/min, 60-second minimum per short |
Chunk counts per short: accents mode = 1-3 chunks (one per accent beat); full mode = one chunk per ~10 seconds of clip length. Use the free estimate endpoint to get the exact total before rendering. Render credits are reserved up-front; any short that fails is automatically refunded.
Job Lifecycle
ANALYZING → AWAITING_SELECTION → RENDERING → COMPLETED
→ PARTIALLY_COMPLETED (some shorts failed, refunded)
→ FAILED
→ CANCELLEDBoth phases are asynchronous — poll GET /v1/generate/shorts/:jobId/status. Analysis typically takes 1-3 minutes; rendering can take 5-20 minutes.
Start an Analysis
Endpoint
POST /api/v1/generate/shorts/analyzeRequest Body
| Field | Type | Required | Description |
|---|---|---|---|
video_url | string | Yes | Kolbo media-library URL of the source video (e.g. from POST /v1/media/upload). External URLs are rejected. |
project_id | string | No | Project to associate the job with. |
Example
curl -X POST https://api.kolbo.ai/api/v1/generate/shorts/analyze \
-H "X-API-Key: kolbo_live_..." \
-H "Content-Type: application/json" \
-d '{
"video_url": "https://cdn.kolbo.ai/media/my-podcast-episode.mp4"
}'Response
{
"status": true,
"data": {
"jobId": "6701a1b2c3d4e5f6a7b8c9d0",
"generationId": "6701a1b2c3d4e5f6a7b8c9d1",
"phase": "ANALYZING",
"source": {
"videoUrl": "https://cdn.kolbo.ai/media/my-podcast-episode.mp4",
"duration": 1264.5
},
"analysisCredits": 15
}
}Errors
| Status | Code | Meaning |
|---|---|---|
| 400 | VIDEO_REQUIRED | Missing video_url |
| 400 | SOURCE_TOO_LONG | Source exceeds the 30-minute cap |
| 400 | SOURCE_TOO_SHORT | Source too short to extract moments |
| 402 | INSUFFICIENT_CREDITS | Not enough credits for the 15-credit analysis fee |
| 429 | JOB_ALREADY_ACTIVE | You already have a running Shorts job — cancel it or wait |
Get Job Status
Endpoint
GET /api/v1/generate/shorts/:jobId/statusExample
curl https://api.kolbo.ai/api/v1/generate/shorts/6701a1b2c3d4e5f6a7b8c9d0/status \
-H "X-API-Key: kolbo_live_..."Response — awaiting selection (analysis done)
When phase is AWAITING_SELECTION, data.analysis.moments contains the AI-picked moments. Times are seconds in the source video.
{
"status": true,
"data": {
"jobId": "6701a1b2c3d4e5f6a7b8c9d0",
"phase": "AWAITING_SELECTION",
"analysis": {
"moments": [
{
"start": 312.4,
"end": 358.9,
"title": "The pricing mistake everyone makes",
"hook": "You're losing money the second you say this number...",
"score": 92,
"accentBeats": [
{ "start": 318.0, "end": 326.5, "reason": "core claim, high energy" },
{ "start": 344.2, "end": 352.0, "reason": "punchline reveal" }
]
},
{
"start": 741.0,
"end": 803.5,
"title": "Why we fired our biggest client",
"hook": "Our biggest client was killing the company.",
"score": 87,
"accentBeats": [
{ "start": 748.3, "end": 760.1, "reason": "story turning point" }
]
}
]
}
}
}Response — rendering / done
When rendering (or finished), data.shorts tracks each short:
{
"status": true,
"data": {
"jobId": "6701a1b2c3d4e5f6a7b8c9d0",
"phase": "COMPLETED",
"shorts": [
{
"momentIndex": 0,
"generationId": "6701a1b2c3d4e5f6a7b8c9e2",
"mode": "accents",
"presetIdentifier": "neon-anime",
"status": "completed",
"finalUrl": "https://cdn.kolbo.ai/shorts/....mp4",
"duration": 46.5,
"error_message": null
}
]
}
}On PARTIALLY_COMPLETED, some shorts have finalUrl and others have error_message — the failed shorts' credits are automatically refunded.
Get the Transcript
Returns the word-level transcript of the source video, produced during analysis (Scribe) — no extra credits. Each word carries its start/end times in absolute source-video seconds. This is the raw material for the Review & Edit workflow: cutting dead air with deleteRanges and building an edited SRT for subtitles.srtContent.
Endpoint
GET /api/v1/generate/shorts/:jobId/transcriptExample
curl https://api.kolbo.ai/api/v1/generate/shorts/6701a1b2c3d4e5f6a7b8c9d0/transcript \
-H "X-API-Key: kolbo_live_..."Response
{
"status": true,
"data": {
"words": [
{ "text": "You're", "start": 312.42, "end": 312.61 },
{ "text": "losing", "start": 312.61, "end": 312.90 },
{ "text": "money", "start": 312.90, "end": 313.22 }
],
"language": "en",
"sourceDuration": 1264.5
}
}List Style Presets
Endpoint
GET /api/v1/generate/shorts/presetsAlso available through the aggregate presets route: GET /api/v1/presets?type=shorts.
Example
curl https://api.kolbo.ai/api/v1/generate/shorts/presets \
-H "X-API-Key: kolbo_live_..."Response
{
"status": true,
"data": [
{
"identifier": "neon-anime",
"name": "Neon Anime",
"description": "High-contrast anime restyle with neon accents",
"thumbnail_url": "https://cdn.kolbo.ai/shorts-presets/neon-anime.jpg",
"preview_video_url": "https://cdn.kolbo.ai/shorts-presets/neon-anime.mp4",
"default_mode": "accents",
"default_veed_preset": "glass",
"category": "stylized"
}
]
}Estimate a Selection
Free — no credits charged. Use it to show the exact cost before rendering.
Endpoint
POST /api/v1/generate/shorts/:jobId/estimateRequest Body
| Field | Type | Required | Description |
|---|---|---|---|
shorts | array | Yes | 1-5 selection objects (see below) |
Each selection object:
| Field | Type | Required | Description |
|---|---|---|---|
momentIndex | number | Yes | Index into analysis.moments |
mode | string | No | "accents" (restyle strongest beats only, cheaper) or "full" (restyle everything). Defaults to the preset's default_mode. |
presetIdentifier | string | Yes | Style preset from the presets endpoint |
subtitles | object | No | { "enabled": true, "veedPreset": "glass", "srtContent": "1\n00:00:00,000 --> ..." } — burned-in subtitles (40cr/min, 60s min). srtContent (optional, ≤200KB) replaces the auto-generated captions with your own edited SRT — timestamps must be in the cut timeline (after deleteRanges are applied), starting at 0. |
start / end | number | No | Override the moment's window (seconds). Final short must be 15-90s. |
deleteRanges | array | No | Ranges to cut from the short: [{ "start": 318.0, "end": 321.5 }] — times in absolute source-video seconds (same timeline as the moment window). The server enforces at least 8 seconds of remaining footage after cuts. Cuts shorten the effective duration, so the estimate reflects a cheaper chunk count. |
Example
curl -X POST https://api.kolbo.ai/api/v1/generate/shorts/6701a1b2c3d4e5f6a7b8c9d0/estimate \
-H "X-API-Key: kolbo_live_..." \
-H "Content-Type: application/json" \
-d '{
"shorts": [
{
"momentIndex": 0,
"mode": "accents",
"presetIdentifier": "neon-anime",
"subtitles": { "enabled": true, "veedPreset": "glass" }
},
{
"momentIndex": 1,
"mode": "full",
"presetIdentifier": "claymation"
}
]
}'Response
{
"status": true,
"data": {
"totalCredits": 1840,
"perShort": [
{ "momentIndex": 0, "credits": 440, "chunkCount": 2 },
{ "momentIndex": 1, "credits": 1400, "chunkCount": 7 }
]
}
}In this example: short 0 = 2 accent chunks × 200 + 40 subtitle credits (46.5s rounds up to the 60s minimum); short 1 = 62.5s / 10s ≈ 7 full-restyle chunks × 200.
Render Shorts
Same body as estimate. Credits are reserved up-front; failed shorts auto-refund. Max 5 shorts per render, each 15-90 seconds.
Endpoint
POST /api/v1/generate/shorts/:jobId/renderExample
curl -X POST https://api.kolbo.ai/api/v1/generate/shorts/6701a1b2c3d4e5f6a7b8c9d0/render \
-H "X-API-Key: kolbo_live_..." \
-H "Content-Type: application/json" \
-d '{
"shorts": [
{
"momentIndex": 0,
"mode": "accents",
"presetIdentifier": "neon-anime",
"subtitles": { "enabled": true, "veedPreset": "glass" }
}
]
}'Then poll the status endpoint until phase is COMPLETED, PARTIALLY_COMPLETED, or FAILED. Each finished short's finalUrl is the deliverable.
Review & Edit workflow
For full control over what ends up in the short — cutting filler and fine-tuning captions — combine the transcript endpoint with the two optional selection fields:
- Fetch the transcript —
GET /v1/generate/shorts/:jobId/transcriptgives you every word with absolute source-second timings. - Decide the cuts — find dead air, filler words, or tangents inside the moment's window and express them as
deleteRanges(absolute source seconds). At least 8 seconds must remain after cuts. - Build the edited SRT — assemble captions from the transcript words, shifting timestamps into the cut timeline (subtract deleted time; the first kept frame is 0). Pass it as
subtitles.srtContent(≤200KB) withsubtitles.enabled: true. - Estimate — the free estimate reflects the shorter effective duration after cuts, so trimming filler directly lowers the chunk count and price.
- Render — send the same selection to the render endpoint.
{
"shorts": [
{
"momentIndex": 0,
"mode": "full",
"presetIdentifier": "neon-anime",
"deleteRanges": [
{ "start": 318.0, "end": 321.5 },
{ "start": 340.2, "end": 342.0 }
],
"subtitles": {
"enabled": true,
"veedPreset": "glass",
"srtContent": "1\n00:00:00,000 --> 00:00:02,400\nYou're losing money\n\n2\n00:00:02,400 --> 00:00:05,100\nthe second you say this number\n"
}
}
]
}Cancel a Job
Cancels the job (analysis or render) and refunds all unused reserved credits.
Endpoint
POST /api/v1/generate/shorts/:jobId/cancelExample
curl -X POST https://api.kolbo.ai/api/v1/generate/shorts/6701a1b2c3d4e5f6a7b8c9d0/cancel \
-H "X-API-Key: kolbo_live_..."Full Workflow Example (JavaScript)
const API_KEY = "kolbo_live_YOUR_API_KEY";
const BASE = "https://api.kolbo.ai/api/v1";
const HEADERS = { "X-API-Key": API_KEY, "Content-Type": "application/json" };
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
async function pollJob(jobId, doneWhen) {
while (true) {
await sleep(10000);
const res = await fetch(`${BASE}/generate/shorts/${jobId}/status`, { headers: HEADERS });
const { data: job } = await res.json();
if (doneWhen(job.phase)) return job;
if (["FAILED", "CANCELLED"].includes(job.phase)) throw new Error(job.error_message || job.phase);
}
}
async function main() {
// 0. Upload the source video (must live in the Kolbo media library)
// See /docs/developer-api/media-library — POST /v1/media/upload
// 1. Analyze (flat 15 credits)
const analyzeRes = await fetch(`${BASE}/generate/shorts/analyze`, {
method: "POST",
headers: HEADERS,
body: JSON.stringify({ video_url: "https://cdn.kolbo.ai/media/my-episode.mp4" })
});
const { data: { jobId } } = await analyzeRes.json();
// 2. Wait for the moments (1-3 min)
const analyzed = await pollJob(jobId, (p) => p === "AWAITING_SELECTION");
const moments = analyzed.analysis.moments;
console.log("Top moment:", moments[0].title, moments[0].score);
// 3. Pick a style preset
const presetsRes = await fetch(`${BASE}/generate/shorts/presets`, { headers: HEADERS });
const { data: presets } = await presetsRes.json();
const preset = presets[0].identifier;
// 4. Price it (free)
const selection = {
shorts: [{
momentIndex: 0,
mode: "accents",
presetIdentifier: preset,
subtitles: { enabled: true, veedPreset: "glass" }
}]
};
const estRes = await fetch(`${BASE}/generate/shorts/${jobId}/estimate`, {
method: "POST", headers: HEADERS, body: JSON.stringify(selection)
});
console.log("Total credits:", (await estRes.json()).data.totalCredits);
// 5. Render (5-20 min)
await fetch(`${BASE}/generate/shorts/${jobId}/render`, {
method: "POST", headers: HEADERS, body: JSON.stringify(selection)
});
const done = await pollJob(jobId, (p) => ["COMPLETED", "PARTIALLY_COMPLETED"].includes(p));
for (const short of done.shorts) {
console.log(short.status === "completed" ? `✅ ${short.finalUrl}` : `❌ ${short.error_message} (refunded)`);
}
}
main();Tips
- Always upload first.
video_urlmust be a Kolbo media-library URL; pass a local/external file throughPOST /v1/media/uploadand use the returned URL. - Show the estimate before rendering. The estimate endpoint is free and returns per-short chunk counts — no surprises.
accentsis the budget default. It restyles only the highest-energy beats (1-3 chunks);fullrestyles every ~10s chunk of the clip.- Subtitles are the only text. The restyle never adds text to the video — if you want captions, enable
subtitles(VEED style presets, default"glass"). - One active job at a time. A second analyze while a job is running returns
429 JOB_ALREADY_ACTIVE— cancel or wait. - 15-90 seconds per short. Use
start/endoverrides to trim a moment into range if needed. - Cut the filler, pay less.
deleteRangesremoves dead air/tangents (absolute source seconds, ≥8s must remain) and the estimate drops with the shorter effective duration. Pair with the transcript endpoint and an editedsubtitles.srtContentfor full editorial control.