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_idbody field that routes the output into a specific project. See Projects.
Endpoint
POST /api/v1/generate/musicRequest Body
| Field | Type | Required | Description |
|---|---|---|---|
prompt | string | Yes* | 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) |
model | string | No | Model 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 |
style | string | No | Music 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 |
instrumental | boolean | No | Instrumental only, no vocals (default: false) |
lyrics | string | No | Custom lyrics. Supplying it switches the request out of simple mode — see the callout below |
vocal_gender | string | No | "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_prompt | boolean | No | Enhance the prompt (default: true — only an explicit false, boolean or the string "false", disables it) |
title | string | No | Song title. Auto-generated from the prompt when omitted. Always used for the track/session title; reaches Suno itself only in custom mode |
negative_tags | string | No | Styles / sounds to exclude, comma-separated (e.g. "heavy metal, distortion"). Suno, custom mode only |
preset_id | string | No | Apply a saved music style preset (GET /api/v1/presets?type=music) |
duration_seconds | number | No | Target track length in seconds for length-controllable models. Clamped to 5–300. 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_id | string | No | Route 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.
| Field | Type | Description |
|---|---|---|
style_weight | number | How strongly the style/genre is applied, 0–1. Custom mode only |
weirdness | number | Creativity / experimentation constraint, 0–1. Custom mode only |
audio_weight | number | Influence of an audio/persona reference, 0–1. Custom mode only |
persona_id | string | Reuse a saved Suno persona / singing voice. Custom mode only |
use_composition_plan | boolean | Enable structured composition planning (verse/chorus). Forwarded in both modes |
singing_dna_id | string | Visual 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_id | string | Custom 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_media | object | Provenance 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 onlyinstrumental, the model, the enhancedprompt, plususe_composition_planand theduration_seconds-derived length.style,title,negative_tags,vocal_gender,persona_id,singing_dna_id,singing_voice_id,style_weight,weirdnessandaudio_weightare 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 honourvocal_genderandnegative_tagsin either mode;styleis unused there too in simple mode. - Custom mode (
lyricspresent): 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 (fromprompt, or frompreset_idwhen given) and atitlewhen 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_offsetandduration_secondsapply. The URL must resolve to a video, otherwise the generation fails withThis 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.
| Field | Type | Required | Description |
|---|---|---|---|
video_url | string | Yes | Public 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_vocal | boolean | No | requires_video_input models only. Preserve the existing speech / vocals from the source video (default: false; enabled by true or the string "true") |
num_samples | number | No | requires_video_input models only. Number of scored variations the provider generates. Clamped 1–4 (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_offset | number | No | requires_video_input models only. Trim start, in seconds |
duration_seconds | number | No | requires_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
Simple (Recommended)
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"
}| Field | Type | Notes |
|---|---|---|
generation_id | string | Mongo ObjectId. This is what you poll and cancel with. |
type | string | Always "music", including for the video_url scoring path |
model | string | The identifier you sent, or the default suno-v5.5 |
credits_charged | null | Always null on this endpoint — music is priced from the finished track. Read credits_used from the completed status. |
poll_url | string | The status path without the /api prefix. Prepend https://api.kolbo.ai/api. |
poll_interval_hint | number | Suggested seconds between polls — 8 for music |
session_id / project_id | string | Where 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. Readmin_output_duration/max_output_durationfromGET /api/v1/models?type=music_gento see which models honour it - Poll at the cadence in
poll_interval_hint(8 seconds for music) POST /v1/generate/musicis rate-limited to the standard SDK generation bucket of 10 requests/minute per account
Related
Music Library
Search, browse, and import stock music tracks
Speech and Sound
Text-to-speech, sound effects, and custom voices
Models and Pricing
The music_gen catalog, credit costs, and duration limits
Projects
Route generations into a project with project_id
Polling and Cancellation
The state machine, the reference poll loop, failures and cancel