Kolbo.AIKolbo.AI Docs
Developer API

Music Generation

Generate music from text descriptions using the Kolbo API.

Generate music tracks with vocals or instrumentals using AI models like Suno. Browse the full model catalog for everything currently available, or fetch the live list at any time with GET /api/v1/models?type=music_gen.

All generation endpoints accept an optional project_id body field that routes the output into a specific project. See Projects.

Endpoint

POST /api/v1/generate/music

Request Body

FieldTypeRequiredDescription
promptstringYes*Description of the music (e.g., "upbeat pop song about summer"). Must be a non-empty string, otherwise 400 INVALID_PROMPT. *Optional only when video_url is supplied (see Score an existing video)
modelstringNoModel identifier from GET /api/v1/models?type=music_gen. Must be a string — an array returns 400 INVALID_MODEL. Omitted, blank, or any of auto / smart-select / smart_select / smartselect / auto-select resolves to the default suno-v5.5
stylestringNoMusic style / genre (e.g., "pop", "rock", "electronic", "jazz"). Fallback only — in custom mode the server generates a style from prompt (or from preset_id) and uses that; your style is used only if that generation fails. In simple mode (no lyrics) the style field is emptied and this value is never sent. See the custom-mode callout below
instrumentalbooleanNoInstrumental only, no vocals (default: false)
lyricsstringNoCustom lyrics. Supplying it switches the request out of simple mode — see the callout below
vocal_genderstringNo"m" or "f". On Suno the value is forwarded verbatim to the provider (which expects exactly those two), and only in custom mode. On fal-backed music models there is no native parameter, so only m / f append a ", male vocals" / ", female vocals" hint to the prompt (both modes) — any other value is ignored there. Not gated on instrumental
enhance_promptbooleanNoEnhance the prompt (default: true — only an explicit false, boolean or the string "false", disables it)
titlestringNoSong title. Auto-generated from the prompt when omitted. Always used for the track/session title; reaches Suno itself only in custom mode
negative_tagsstringNoStyles / sounds to exclude, comma-separated (e.g. "heavy metal, distortion"). Suno, custom mode only
preset_idstringNoApply a saved music style preset (GET /api/v1/presets?type=music)
duration_secondsnumberNoTarget track length in seconds for length-controllable models. Clamped to 5300. Without it those models default to a ~10s track. Models whose length is not caller-controlled ignore it. Check min_output_duration / max_output_duration on the model in GET /api/v1/models?type=music_gen
project_idstringNoRoute the output into a specific project. Omit for the default "API Generations" project

Suno fine-controls (optional)

Advanced controls honored by Suno models; ignored by others. Most reach the provider only in custom mode — see the callout below.

FieldTypeDescription
style_weightnumberHow strongly the style/genre is applied, 01. Custom mode only
weirdnessnumberCreativity / experimentation constraint, 01. Custom mode only
audio_weightnumberInfluence of an audio/persona reference, 01. Custom mode only
persona_idstringReuse a saved Suno persona / singing voice. Custom mode only
use_composition_planbooleanEnable structured composition planning (verse/chorus). Forwarded in both modes
singing_dna_idstringVisual DNA character whose singing voice to use. Must be owned by the caller, and the DNA must have a singing voice attached — otherwise it is silently ignored. Resolves to persona_id, so it takes effect in custom mode only
singing_voice_idstringCustom cloned singing-voice id. Must be owned by the caller and have voiceType: "singing" — otherwise silently ignored. Resolves to persona_id, so it takes effect in custom mode only
source_mediaobjectProvenance metadata stored on the generation record: { url, type, mimeType, originalFileName, fileSize }. Only persisted when url is present, and it does not influence the generated audio — to actually score a video use video_url below

Model identifiers are Kolbo-specific — always fetch available models from GET /api/v1/models?type=music_gen first (the legacy alias type=music also works). Omitting model uses suno-v5.5, which is recommended for most use cases.

lyrics is the switch between simple mode and custom mode, and custom mode is what unlocks the Suno controls.

Server-side, useSimpleMode is !lyrics and customMode is its inverse — so passing lyrics puts the request in custom mode, and omitting it leaves you in simple mode.

  • Simple mode (no lyrics): the Suno request carries only instrumental, the model, the enhanced prompt, plus use_composition_plan and the duration_seconds-derived length. style, title, negative_tags, vocal_gender, persona_id, singing_dna_id, singing_voice_id, style_weight, weirdness and audio_weight are not sent to Suno — they are still stored on the generation record, but they do not shape the audio. The non-Suno (fal-backed) music models take a different route and still honour vocal_gender and negative_tags in either mode; style is unused there too in simple mode.
  • Custom mode (lyrics present): all of the above are forwarded, and your text is treated as the exact lyric sheet rather than a brief for generated lyrics. The server also auto-generates a style (from prompt, or from preset_id when given) and a title when you omit them.

Leave lyrics out when you want the model to write the words; supply it (even a rough draft) when you need the fine-controls to actually reach Suno.

Score an Existing Video

Supplying video_url routes the request through the Vision Music pipeline instead of the regular text-to-music flow. prompt becomes optional in this mode (it is forwarded as free-form guidance). The result is still an audio track — this endpoint always produces a music file, never a video muxed with the score.

Which pipeline runs depends on the model's requires_video_input flag in GET /api/v1/models?type=music_gen:

  • requires_video_input: true — the model scores the clip natively. keep_speech_vocal, num_samples, start_offset and duration_seconds apply. The URL must resolve to a video, otherwise the generation fails with This model requires a video upload.
  • any other music model — the media is analysed first (prompt, style and lyrics are derived from it), then normal music generation runs. The four fields above are ignored on this path.
FieldTypeRequiredDescription
video_urlstringYesPublic URL of the source media. Must be a string, and is SSRF-validated — private/internal hosts return 400 INVALID_VIDEO_URL. The server downloads it; max 50 MB, otherwise 400 FILE_TOO_LARGE
keep_speech_vocalbooleanNorequires_video_input models only. Preserve the existing speech / vocals from the source video (default: false; enabled by true or the string "true")
num_samplesnumberNorequires_video_input models only. Number of scored variations the provider generates. Clamped 14 (default: 1). Models with per-second billing are charged per sample, but only the first returned track is stored and surfaced in result.urls — raising this costs more without returning more. Leave it at 1 unless you have a reason
start_offsetnumberNorequires_video_input models only. Trim start, in seconds
duration_secondsnumberNorequires_video_input models only. Trim duration, in seconds — not clamped to 5–300 here. When omitted, the credit pre-check estimates from the model's default_duration (falling back to 60s)

vocal_gender, enhance_prompt, preset_id, title, negative_tags, persona_id, use_composition_plan, singing_dna_id, singing_voice_id, source_media and the Suno fine-controls are not forwarded when video_url is present. The only fields that carry over are prompt (as free-form guidance), model, style, instrumental, lyrics, project_id and the four video-mode fields in the table above.

curl -X POST https://api.kolbo.ai/api/v1/generate/music \
  -H "X-API-Key: kolbo_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "video_url": "https://example.com/clip.mp4",
    "prompt": "tense, driving percussion under the chase",
    "num_samples": 2,
    "duration_seconds": 30
  }'

Examples

Omit model to use the server default — the simplest way to get started:

curl -X POST https://api.kolbo.ai/api/v1/generate/music \
  -H "X-API-Key: kolbo_live_..." \
  -H "Content-Type: application/json" \
  -d '{"prompt": "Relaxing lo-fi hip hop beat for studying"}'

With Specific Model

To choose a specific model, first fetch identifiers from GET /api/v1/models?type=music_gen, then pass the identifier value:

curl -X POST https://api.kolbo.ai/api/v1/generate/music \
  -H "X-API-Key: kolbo_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "Relaxing lo-fi hip hop beat for studying",
    "model": "suno-v5.5"
  }'

With Style and Lyrics

curl -X POST https://api.kolbo.ai/api/v1/generate/music \
  -H "X-API-Key: kolbo_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "Emotional ballad",
    "style": "pop",
    "lyrics": "Walking through the rain\nThinking of you again",
    "vocal_gender": "f"
  }'

Instrumental

curl -X POST https://api.kolbo.ai/api/v1/generate/music \
  -H "X-API-Key: kolbo_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "Epic orchestral trailer music",
    "instrumental": true,
    "style": "cinematic"
  }'

Response

This endpoint is asynchronous and fire-and-forget: the POST returns as soon as the job is queued and never contains audio.

Polling is the only completion mechanism. Kolbo never calls you back: there is no webhook, no callback_url field on this request body, and no route delivers outbound notifications. Socket.IO events are the web app's internal transport and are not part of the API contract — API-key generations are registered so the shared progress emitter drops their events, and the few emit sites that bypass that check are undocumented, unversioned and unsafe to build on. After the POST, loop against GET /api/v1/generate/{generation_id}/status until the job is terminal. Full contract: Polling & Cancellation.

Generation Started

{
  "success": true,
  "generation_id": "68f2c1a9b4e5d6f7a8b9c0d1",
  "type": "music",
  "model": "suno-v5.5",
  "credits_charged": null,
  "poll_url": "/v1/generate/68f2c1a9b4e5d6f7a8b9c0d1/status",
  "poll_interval_hint": 8,
  "session_id": "68f2c1a9b4e5d6f7a8b9c0d2",
  "project_id": "68f2c1a9b4e5d6f7a8b9c0d3"
}
FieldTypeNotes
generation_idstringMongo ObjectId. This is what you poll and cancel with.
typestringAlways "music", including for the video_url scoring path
modelstringThe identifier you sent, or the default suno-v5.5
credits_chargednullAlways null on this endpoint — music is priced from the finished track. Read credits_used from the completed status.
poll_urlstringThe status path without the /api prefix. Prepend https://api.kolbo.ai/api.
poll_interval_hintnumberSuggested seconds between polls — 8 for music
session_id / project_idstringWhere the generation lives in the Kolbo app

Completed Status

GET /api/v1/generate/{generation_id}/status

{
  "success": true,
  "generation_id": "68f2c1a9b4e5d6f7a8b9c0d1",
  "type": "music",
  "state": "completed",
  "progress": 100,
  "result": {
    "urls": [
      "https://media.kolbo.ai/music/.../track-1.mp3",
      "https://media.kolbo.ai/music/.../track-2.mp3"
    ],
    "tracks": [
      { "title": "Summer Vibes", "duration": 187.2, "thumbnail_url": null, "model": "suno-v5.5" },
      { "title": "Summer Vibes", "duration": 191.0, "thumbnail_url": null, "model": "suno-v5.5" }
    ],
    "title": "Summer Vibes",
    "duration": 187.2,
    "lyrics": "[Verse 1]\n...",
    "prompt_used": "Relaxing lo-fi hip hop beat for studying",
    "model": "suno-v5.5",
    "created_at": "2026-07-20T14:20:00Z"
  },
  "credits_used": 24,
  "credits_breakdown": [
    { "model": "suno-v5.5", "amount": 24, "base": 20, "final": 24, "duration_multiplier": 1, "pricing": null }
  ]
}

The output lives in result.urls — an array of strings. Suno normally returns two variations per request, so do not assume a single track. result.tracks is index-aligned with urls and carries title, duration (seconds), thumbnail_url and model per track.

result (and credits_used / credits_breakdown) appear only under a completed state. tracks[].thumbnail_url is always null today — cover art is stored under a field the API does not surface, so do not build on it.

Music has an intermediate completed. Suno hands back temporary provider stream URLs before the permanent files are stored, and that intermediate status normalises to state: "completed" with progress: 70. Those URLs play, but they are not on the Kolbo CDN and they expire.

For /v1/generate/music only, treat the result as final when state === "completed" and progress === 100. Otherwise keep polling until the permanent media.kolbo.ai URLs arrive. Every genuine completion writes progress: 100, so progress is a reliable discriminator here.

Failure and cancellation

A failed generation is still an HTTP 200 with success: true — the failure is in state, with an error string carrying the provider message. cancelled carries neither result nor error, so treat all three terminal states explicitly. Music credits are deducted only after a track is delivered, so a failed generation costs nothing and POST /v1/generate/{id}/cancel reports credits_refunded: 0. See Polling & Cancellation.

JavaScript Example

const KOLBO_API_KEY = "kolbo_live_..."; // Replace with your API key
const BASE_URL = "https://api.kolbo.ai/api";

async function generateMusic() {
  const response = await fetch(`${BASE_URL}/v1/generate/music`, {
    method: "POST",
    headers: {
      "X-API-Key": KOLBO_API_KEY,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      prompt: "Upbeat pop song about summer adventures",
      style: "pop",
      instrumental: false,
    }),
  });

  const started = await response.json();
  if (!started.success) throw new Error(started.error);

  // Minimal loop. A production client should also handle transient HTTP errors and
  // an overall timeout — see /docs/developer-api/polling-and-cancellation.
  while (true) {
    await new Promise((r) => setTimeout(r, started.poll_interval_hint * 1000));
    const status = await fetch(
      `${BASE_URL}/v1/generate/${started.generation_id}/status`,
      { headers: { "X-API-Key": KOLBO_API_KEY } }
    ).then((r) => r.json());

    // Music only: "completed" at progress 70 means temporary streaming URLs.
    // Keep polling until progress is 100 for the permanent CDN files.
    if (status.state === "completed" && status.progress === 100) {
      console.log("Music URLs:", status.result.urls); // usually two tracks
      return status.result;
    }
    if (status.state === "failed") throw new Error(status.error || "Generation failed");
    if (status.state === "cancelled") throw new Error("Generation was cancelled");
  }
}

generateMusic().catch(console.error);

Python Example

import requests
import time

KOLBO_API_KEY = "kolbo_live_..."  # Replace with your API key
BASE_URL = "https://api.kolbo.ai/api"

response = requests.post(
    f"{BASE_URL}/v1/generate/music",
    headers={"X-API-Key": KOLBO_API_KEY},
    json={
        "prompt": "Cinematic orchestral theme",
        "instrumental": True,
        "style": "cinematic",
    },
)
started = response.json()
if not started.get("success"):
    raise Exception(started.get("error", "Request failed"))

# Minimal loop. Add transient-error retries and an overall timeout for production
# — see /docs/developer-api/polling-and-cancellation.
while True:
    time.sleep(started["poll_interval_hint"])
    status = requests.get(
        f"{BASE_URL}/v1/generate/{started['generation_id']}/status",
        headers={"X-API-Key": KOLBO_API_KEY},
    ).json()

    # Music only: "completed" at progress 70 means temporary streaming URLs.
    # Keep polling until progress is 100 for the permanent CDN files.
    if status["state"] == "completed" and status["progress"] == 100:
        print("URLs:", status["result"]["urls"])  # usually two tracks
        break
    if status["state"] == "failed":
        raise Exception(status.get("error", "Generation failed"))
    if status["state"] == "cancelled":
        raise Exception("Generation was cancelled")

Tips

  • Use descriptive prompts for better results: include genre, mood, tempo, and instruments
  • Need a specific track length? Pass duration_seconds (clamped 5–300) on a length-controllable model — without it those models default to ~10 seconds. Read min_output_duration / max_output_duration from GET /api/v1/models?type=music_gen to see which models honour it
  • Poll at the cadence in poll_interval_hint (8 seconds for music)
  • POST /v1/generate/music is rate-limited to the standard SDK generation bucket of 10 requests/minute per account