Kolbo.AIKolbo.AI Docs
Developer API

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:

  1. 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).
  2. Render — you pick moments, a visual style preset, and a mode, and the API restyles each moment into a finished short.

Key semantics:

  • accents mode restyles only the strongest beats of the moment — cheaper.
  • full mode 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

ItemCredits
AnalysisFlat 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
                                           → CANCELLED

Both 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/analyze

Request Body

FieldTypeRequiredDescription
video_urlstringYesKolbo media-library URL of the source video (e.g. from POST /v1/media/upload). External URLs are rejected.
project_idstringNoProject 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

StatusCodeMeaning
400VIDEO_REQUIREDMissing video_url
400SOURCE_TOO_LONGSource exceeds the 30-minute cap
400SOURCE_TOO_SHORTSource too short to extract moments
402INSUFFICIENT_CREDITSNot enough credits for the 15-credit analysis fee
429JOB_ALREADY_ACTIVEYou already have a running Shorts job — cancel it or wait

Get Job Status

Endpoint

GET /api/v1/generate/shorts/:jobId/status

Example

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/transcript

Example

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/presets

Also 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/estimate

Request Body

FieldTypeRequiredDescription
shortsarrayYes1-5 selection objects (see below)

Each selection object:

FieldTypeRequiredDescription
momentIndexnumberYesIndex into analysis.moments
modestringNo"accents" (restyle strongest beats only, cheaper) or "full" (restyle everything). Defaults to the preset's default_mode.
presetIdentifierstringYesStyle preset from the presets endpoint
subtitlesobjectNo{ "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 / endnumberNoOverride the moment's window (seconds). Final short must be 15-90s.
deleteRangesarrayNoRanges 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/render

Example

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:

  1. Fetch the transcriptGET /v1/generate/shorts/:jobId/transcript gives you every word with absolute source-second timings.
  2. 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.
  3. 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) with subtitles.enabled: true.
  4. Estimate — the free estimate reflects the shorter effective duration after cuts, so trimming filler directly lowers the chunk count and price.
  5. 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/cancel

Example

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_url must be a Kolbo media-library URL; pass a local/external file through POST /v1/media/upload and use the returned URL.
  • Show the estimate before rendering. The estimate endpoint is free and returns per-short chunk counts — no surprises.
  • accents is the budget default. It restyles only the highest-energy beats (1-3 chunks); full restyles 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 / end overrides to trim a moment into range if needed.
  • Cut the filler, pay less. deleteRanges removes 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 edited subtitles.srtContent for full editorial control.