Kolbo.AIKolbo.AI Docs
Developer API

Music Library

Search Kolbo's licensed production-music catalog, resolve preview URLs, and acquire clean, unwatermarked masters.

The Music Library is Kolbo's licensed production-music catalog. Use it to score a video, ad, or voiceover with an existing, rights-cleared track.

Every endpoint is authenticated with your X-API-Key.

Discovery is free. Final use is a paid vendor action. search, catalog, facets, analyze-script, and the three track/:id/* lookups cost no credits. POST /music-library/clean/:trackId and POST /music-library/import spend one vendor credit per track and require an API key with write permission.

This is the paid licensed catalog — it is not the default place to look for music. Kolbo also ships its own large AI-generated music library, which is free and consumes no vendor credit: GET /api/v1/stock/search?source=kolbo-ai&mediaType=music in the Stock Library, including natural-language "vibe" search. Reach for the Music Library when you specifically need the licensed catalog, a named commercial cue, or stems.

Library vs generation. These endpoints find an existing track. To compose a brand-new original song, use music generation instead.

Rate limits

RoutesLimit
All read routes (search, analyze-script, catalog, facets, track/:id/*)60 requests / minute / user
clean/:trackId and import5 requests / minute / user, shared with the in-app music routes

Relevance search over the catalog with optional filters and sort.

Endpoint

POST /api/v1/music-library/search

Request Body

ParameterTypeRequiredConstraints
querystringNoKeyword search, e.g. "uplifting corporate". Max 200 chars (longer → 400); a non-string → 400. Omit for a filter-only browse.
moodstringNoSingle mood value. Use a value from Facets — free text may match nothing. Matched case-insensitively as a substring of the track's moods when the catalog index serves the request.
genrestringNoSingle genre value. Use a value from Facets. Same matching rules as mood.
instrumentstringNoSingle instrument value from Facets. Applied only when the catalog index is serving the request — the upstream catalog has no instrument filter.
bpmMin / bpmMaxnumberNoBeats-per-minute range. Must be a real JSON number — a numeric string ("120") is silently ignored, not coerced.
durationMin / durationMaxnumberNoTrack duration range, in seconds. Same rule: a numeric string is silently ignored.
hasStemsbooleanNoOnly tracks that have separated stems. Must be a real JSON boolean — the string "true" is silently ignored. true forces the live vendor path; false does not — see the callout below.
hasLyricsbooleanNoOnly tracks that have lyrics. Same boolean rule, and the same true-only path switch.
sortstringNoOne of duration-asc, duration-desc, bpm-asc, bpm-desc, title. Any other value is ignored (relevance order).
shuffleSeednumber | stringNoStable shuffle seed — the same seed returns the same random order across pages. Applies only to a filter-only browse (no query), only when sort is absent, and only on the indexed path (the live vendor fallback ignores it entirely).
limitnumberNoResults per page. Default 20, clamped to 1–40.
offsetnumberNoPagination offset. Default 0.

Examples

curl -X POST https://api.kolbo.ai/api/v1/music-library/search \
  -H "X-API-Key: kolbo_live_..." \
  -H "Content-Type: application/json" \
  -d '{ "query": "uplifting corporate", "genre": "Corporate", "durationMax": 120, "limit": 5 }'

Response

{
  "success": true,
  "tracks": [
    {
      "id": "12345",
      "title": "Bright Horizons",
      "artist": "Studio Collective (ASCAP)",
      "artistName": "Studio Collective",
      "artistId": "778",
      "artists": [{ "id": "778", "name": "Studio Collective", "role": "primary" }],
      "primaryArtist": { "id": "778", "name": "Studio Collective" },
      "album": "Corporate Vol. 3",
      "albumId": "91",
      "albumCode": "CORP3",
      "durationSeconds": 118,
      "bpm": 120,
      "musicalKey": "C major",
      "genre": "Corporate",
      "genres": ["Corporate"],
      "moodTags": ["Uplifting", "Hopeful"],
      "instruments": ["Piano", "Strings"],
      "tags": ["build", "positive"],
      "artworkUrl": "https://.../artwork.jpg",
      "audioUrl": "https://.../audio_128.mp3",
      "audioUrl128": "https://.../audio_128.mp3",
      "audioUrl320": "https://.../320.mp3",
      "audioUrlWav": "https://.../master.wav",
      "previewAudioUrl": "https://.../audio_128.mp3",
      "previewDownloadUrl": "https://.../download.mp3",
      "hqAvailable": true,
      "watermarked": false,
      "hasStems": true,
      "hasLyrics": false,
      "versionsCount": 3
    }
  ],
  "count": 5,
  "total": 87,
  "sorted": false,
  "hasMore": true
}

Never persist a track URL. artworkUrl and every audio*Url are signed URLs — audio expires roughly 1 hour after it is issued, artwork after 7 days. Store the track id and re-resolve at use time via Track Audio.

count is the number of tracks in this page, total the size of the whole result set, and hasMore whether another page exists (all three as described on the indexed path — see the callout below for the live fallback). sorted is true only when you passed a recognized sort value.

The per-track watermarked flag tells you whether the URLs on that track are a watermarked preview or already clean — read it rather than assuming. Regardless of its value, a licensed final use should go through Acquire Clean Track or Import, which return a file explicitly stamped watermarked: false and record the licence.

hasMore can be false even when more matches exist upstream. Two paths serve this endpoint. The indexed path (a local catalog mirror) filters, ranks and paginates everything and returns a real total / hasMore. The live vendor path is used while that mirror is still warming, and always when you send hasStems: true or hasLyrics: true (the mirror does not carry those two flags); it fetches exactly one upstream page of limit rows (hard-capped at 40) and always returns hasMore: false. Without sort, offset is ignored entirely, count is the raw upstream row count and total the usable count after rows with no id or audio URL are dropped. With a sort, that single page is sorted, cached, then sliced by offset (itself clamped to 40) — so count becomes the slice length and total the size of the whole page. Use GET /music-library/catalog for stable pagination.

Analyze Script

AI helper that turns a video or voiceover script into a music search you can feed straight into Search.

Endpoint

POST /api/v1/music-library/analyze-script

Request Body

ParameterTypeRequiredConstraints
scriptstringYesThe script or scene description. Trimmed; empty → 400. Clipped server-side to the first 8000 characters.

Examples

curl -X POST https://api.kolbo.ai/api/v1/music-library/analyze-script \
  -H "X-API-Key: kolbo_live_..." \
  -H "Content-Type: application/json" \
  -d '{ "script": "A founder walks through an empty office at dawn, reflecting on the journey before the team arrives." }'

Response

{
  "success": true,
  "query": "inspiring cinematic",
  "mood": "Emotional",
  "genre": "Soundtrack",
  "keywords": ["reflective", "hopeful", "piano", "build", "cinematic"]
}

The model is asked for a 1–3 word query and single-word mood / genre; what is enforced is the clipping — query to 80 chars, mood and genre to 40 chars each (either can come back ""), and keywords to at most 5 tags of 40 chars. When the model returns no query, the first keyword is used; if there is neither you get 422 with code MUSIC_LIBRARY_SCRIPT_EMPTY.

Browse Catalog

Stable paginated browse with no query.

Endpoint

GET /api/v1/music-library/catalog

Query Parameters

ParameterTypeRequiredConstraints
limitnumberNoResults per page. Default 50, clamped to 1–100.
offsetnumberNoPagination offset. Default 0.
sortstringNoSame values as Search: duration-asc, duration-desc, bpm-asc, bpm-desc, title.

This route reads only limit, offset, and sort. Genre / mood / BPM / duration filters passed here are ignored — send them to POST /music-library/search instead.

curl "https://api.kolbo.ai/api/v1/music-library/catalog?limit=20&sort=bpm-desc" \
  -H "X-API-Key: kolbo_live_..."

Returns { success, tracks, count, total, sorted } with the same track shape as Search. total is omitted on the unsorted live fallback (when the local catalog mirror is not ready) — treat a missing total as "unknown", not zero.

Facets

Distinct genres, moods, and instruments plus the BPM and duration ranges — use these to build precise filtered searches. Cached server-side for 10 minutes.

Endpoint

GET /api/v1/music-library/facets

Response

{
  "success": true,
  "genres": ["Corporate", "Soundtrack", "Hip Hop"],
  "moods": ["Uplifting", "Tense", "Emotional"],
  "instruments": ["Piano", "Guitar", "Drums"],
  "bpmRange": { "min": 60, "max": 180 },
  "durationRange": { "min": 30, "max": 360 }
}

bpmRange and durationRange are null when the catalog does not report them.

Track Audio

Re-resolve the playable URLs for a single track. Call this instead of caching URLs from a search response.

Endpoint

GET /api/v1/music-library/track/:id/audio
ParameterTypeRequiredConstraints
:idstringYesThe track id from Search / Catalog.
curl https://api.kolbo.ai/api/v1/music-library/track/12345/audio \
  -H "X-API-Key: kolbo_live_..."

Response

{
  "success": true,
  "id": "12345",
  "previewAudioUrl": "https://.../128.mp3",
  "previewDownloadUrl": "https://.../download.mp3",
  "watermarked": false,
  "urls": {
    "128": "https://.../128.mp3",
    "320": "https://.../320.mp3",
    "wav": "https://.../master.wav"
  }
}

urls["320"] and urls.wav are null when the catalog does not expose that variant for the track (hqAvailable on the search result tells you in advance whether a WAV exists). These URLs are signed and short-lived.

Stems and alternate versions (instrumental, cutdown, minus-mix) of a master track.

Endpoint

GET /api/v1/music-library/track/:id/related

Response

{
  "success": true,
  "stems": [
    {
      "id": "12345-drums",
      "name": "Drums",
      "variantLabel": "Drums",
      "type": "stem",
      "element": "drums",
      "stemRef": "stem_ab12cd",
      "audioUrl": "https://.../drums.mp3",
      "downloadUrl": "https://.../drums-download.mp3",
      "watermarked": false,
      "audioUrl128": "https://.../drums_128.mp3",
      "audioUrl320": "https://.../drums_320.mp3",
      "audioUrlWav": null
    }
  ],
  "versions": [
    { "id": "12345-30s", "name": "30s Cut", "type": "cutdown", "stemRef": null, "audioUrl": "https://.../30s.mp3" }
  ]
}

type is one of stem, cutdown, minus_mix, alternative. Pass a row's stemRef as the stem field of Acquire Clean Track to buy a clean version of that stem instead of the master.

Track Lyrics

Lyrics text, lyrical theme, and explicit flag for a single track.

Endpoint

GET /api/v1/music-library/track/:id/lyrics

Response

{
  "success": true,
  "hasLyrics": true,
  "lyrics": "Verse 1...",
  "lyricalTheme": "perseverance",
  "explicit": false
}

lyrics and lyricalTheme are null when the track has none.

Acquire Clean Track

Spend one vendor credit and resolve a clean, unwatermarked MP3 or WAV master (or a single stem) for final use — download or editing-timeline use.

Paid and immediate. There is no confirmation step: a successful call has spent the credit. The API key must have write permission (403 / API_KEY_READ_ONLY otherwise). requestId is a required idempotency key — reuse the same value when retrying the same intended action, and a new value for a genuinely new acquisition.

Endpoint

POST /api/v1/music-library/clean/:trackId

Request Body

ParameterTypeRequiredConstraints
:trackId (path)stringYesTrack id. Max 64 chars.
requestIdstringYesIdempotency key. Must match ^[A-Za-z0-9_-]{8,80}$. Reusing it for the same track/format/purpose/project returns the cached result (reused: true); reusing it for a different action → 409 SYNCI_IDEMPOTENCY_MISMATCH.
formatstringNomp3 (default) or wav. Anything else → 400. Request wav only when the track reports hqAvailable: true.
purposestringNodownload (default) or timeline. Anything else → 400.
stemstringNoA stemRef from Track Related to acquire that stem instead of the master. Max 512 chars. The response kind comes back "stem".
projectIdstringNoProject to attribute the acquisition to. Must be a valid ObjectId (400 INVALID_PROJECT_ID) and you need edit access (403 PROJECT_ACCESS_DENIED). "all" and omission both mean "no project". Note the camelCase name.
licenseTierstringNopersonal_creator or monetized_commercial. Any other value (including omission) is recorded as monetized_commercial. Stamped on the compliance record only — it does not gate the request.
trackobjectNoOptional track snapshot from the search response, stored on the audit record for reporting. Unrecognized fields are dropped.

termsVersion / termsAccepted are not read from the request — the terms version and acceptance timestamp are stamped server-side.

Examples

curl -X POST https://api.kolbo.ai/api/v1/music-library/clean/12345 \
  -H "X-API-Key: kolbo_live_..." \
  -H "Content-Type: application/json" \
  -d '{ "format": "wav", "purpose": "timeline", "requestId": "score-hero-cut-01" }'

Response

{
  "success": true,
  "trackId": "12345",
  "format": "wav",
  "kind": "master",
  "audioUrl": "https://.../clean.wav",
  "downloadUrl": "https://.../clean-download.wav",
  "watermarked": false,
  "creditsRemaining": 412
}

kind is master or stem. creditsRemaining is the vendor's remaining allowance (may be null), not your Kolbo credit balance. reused: true is added only when the call replayed a still-valid cached result for the same requestId; the field is absent on a fresh acquisition. The returned URLs are signed and expire — download promptly or re-acquire with the same requestId.

Import Track to Media Library

Acquire one clean file and copy it into your media library as project-scoped stock audio, on a stable Kolbo CDN URL. Same paid vendor action as Acquire Clean Track, plus the CDN copy.

Endpoint

POST /api/v1/music-library/import

Request Body

ParameterTypeRequiredConstraints
trackIdstringYesTrack id (in the body here, not the path). Max 64 chars.
formatstringNomp3 (default) or wav.
qualitystringNoLegacy alias — "wav" selects WAV; anything else falls back to MP3. Ignored when format is present.
requestIdstringNoIdempotency key, ^[A-Za-z0-9_-]{8,80}$. When omitted the server generates one, so a retry without it can re-spend a credit. Always send your own.
projectIdstringNoProject to file the media item under. Requires edit access.
licenseTierstringNoSame values and default as Acquire Clean Track.
trackobjectNoTrack snapshot from search — supplies originalName (title), authorName (artist) and durationSeconds on the created media item. Only whitelisted scalar fields are kept; URLs (including artworkUrl) are dropped, so the imported item carries no thumbnail.

Examples

curl -X POST https://api.kolbo.ai/api/v1/music-library/import \
  -H "X-API-Key: kolbo_live_..." \
  -H "Content-Type: application/json" \
  -d '{ "trackId": "12345", "format": "mp3", "requestId": "import-12345-01" }'

Response

{
  "success": true,
  "libraryItem": {
    "_id": "...",
    "mediaType": "audio",
    "url": "https://media.kolbo.ai/kolboai-media/stock-imports/music/...",
    "filename": "...mp3"
  },
  "alreadyImported": false,
  "format": "mp3",
  "creditsRemaining": 411
}

Dedup is per track, per user. If the same track is already in your library the call returns the existing item with alreadyImported: true and no credit is spentformat and creditsRemaining are omitted in that case.

The created item is sourceType: "stock" with metadata.stockSource: "music", so it shows up alongside other stock imports in the Media Library.

Typical Flow

  1. From a scriptPOST /music-library/analyze-script to derive query / mood / genre.
  2. SearchPOST /music-library/search with that query plus filters from Facets. Play previewAudioUrl for the user.
  3. InspectGET /music-library/track/:id/related for stems and cutdowns, .../lyrics for vocal tracks.
  4. CommitPOST /music-library/import (with your own requestId) to land a clean file on Kolbo's CDN, or POST /music-library/clean/:trackId when you only need a short-lived download URL.

Errors

All errors return { "success": false, "error": "...", "code": "..." }.

CodeStatusMeaning
MUSIC_LIBRARY_UNAVAILABLE503 / 502Not configured on this environment, or temporarily unreachable (read routes).
SYNCI_NOT_CONFIGURED503Same condition on the two paid routes, which report the raw code.
MUSIC_LIBRARY_TIMEOUT504Upstream timed out — retry.
MUSIC_LIBRARY_RATE_LIMITED429Catalog rate limit reached.
MUSIC_LIBRARY_NOT_FOUND404Track not found.
MUSIC_LIBRARY_ERROR502Any other catalog failure.
MUSIC_LIBRARY_SCRIPT_EMPTY422analyze-script could not derive a search.
MUSIC_LIBRARY_SCRIPT_ERROR502analyze-script failed.
API_KEY_READ_ONLY403The paid routes need a write-enabled API key.
SYNCI_BAD_REQUEST400Invalid clean-music request (bad format, purpose, requestId, or stem).
INVALID_PROJECT_ID400projectId is not a valid id.
PROJECT_ACCESS_DENIED403You need edit access on that project.
SYNCI_CLEAN_IN_PROGRESS409That requestId is already running.
SYNCI_CLEAN_REQUEST_USED409That requestId has already been used.
SYNCI_IDEMPOTENCY_MISMATCH409That requestId belongs to a different action.
SYNCI_CLEAN_CREDITS_EXHAUSTED402Clean downloads are temporarily unavailable (vendor licence balance empty). Carries creditsRemaining when the vendor reported one.
Any other SYNCI_* code502The vendor returned an unusable clean-audio response, or the resolved file was on an untrusted host.