● v1 spec in development
basehead Local API v1
We’re building a local API for basehead right now… so your own tools can search a library, import new files and transfer sounds while basehead keeps doing its thing.
How it works
Local onlyHTTP server inside basehead on
127.0.0.1. Off by default… enabled in Options → API Keys → Local API. Default port 12120.basehead must be runningThe API lives inside the app. If basehead is closed, requests fail to connect.
HeadlessCalls return data and never touch the UI… except
select (shows files in Results) and import (basehead’s normal import progress, then the new files in Results). Every call also pops a purple ‘Local API’ notice in basehead with a Turn Off button, so the user always sees outside traffic.MCP tooThe same commands are exposed as an MCP server so AI agents can call basehead directly. Connect your agent to
http://127.0.0.1:12120/mcp with the same token.One command layerbasehead’s own features are moving onto these same commands… one code path, no private shortcuts.
EditionUltra only. In development… not released yet.
Auth & requests
A token is shown in Options → API Keys → Local API. Copy it into your tool once and send it with every request. JSON in, JSON out. All paths are versioned under /api/v1/.
# example curl http://127.0.0.1:12120/api/v1/status \ -H "Authorization: Bearer <token>"
| HTTP | Meaning |
|---|---|
200 | OK |
202 | Job started (import)… poll for the result |
400 | Bad request… body has { "error": "...", "detail": "..." } |
401 | Missing or wrong token |
403 | Not Ultra, or not allowed for this user |
404 | Unknown endpoint, or the id / name wasn’t found |
409 | basehead is busy (e.g. another import is running) |
500 | Something failed inside basehead… body has error + detail |
MCP
Streamable HTTP at http://127.0.0.1:12120/mcp, same Bearer token. Claude Code example:
claude mcp add --transport http basehead http://127.0.0.1:12120/mcp \ --header "Authorization: Bearer <token>"
| Tool | Same as |
|---|---|
status | GET status |
capabilities | GET capabilities |
list_databases | GET databases |
ucs_tree | GET ucs |
list_imports | GET imports |
list_import_folders | GET imports/{id}/folders |
list_groups | GET groups |
create_group | POST groups |
add_to_group | POST groups/{id}/sounds |
list_collections | GET collections |
get_collection | GET collections/{name} |
add_to_collection | POST collections |
search | POST search |
get_sound | GET sounds/{id} |
get_selection | GET selection |
start_import | POST import |
get_import_job | GET import/{job_id} |
select_in_basehead | POST select |
transfer | POST transfer |
xfer_to_daw | POST xfer |
Connection
GET/api/v1/statusIs basehead up, what’s open
{
"version": "2026.10.27",
"database": "Main Library",
"target_app": "REAPER",
"target_running": true
}GET/api/v1/capabilitiesWhat this install can do
{
"api_version": 1,
"version": "2026.10.31",
"edition": "Ultra",
"commands": ["GET status", "POST search", "POST collections", "..."]
}GET/api/v1/databasesLocal + shared databases
- Lists every local and shared database and marks the one that’s open.
Library
GET/api/v1/ucsUCS tree with file counts
- Category → SubCategory tree, with how many files in the open database fall under each.
GET/api/v1/importsImports in the open DB
- Name, id and root path of each Import.
GET/api/v1/imports/{id}/folders?path=Browse one level, paged
- Child folders and file counts under
path. Leave it empty for the Import root. - The full folder path is the folder’s id… folders aren’t stored as records.
- Built lazily, one level at a time, so it stays fast on multi-million-file libraries.
limit/offsetpage the folders (default 100, max 1000).
Response
{ "path": "...", "total": 12, "folders": [ { "name": "Close", "path": "...", "count": 340 } ] }
GET/api/v1/groupsGroups in the open DB
- Name and id of each Group.
POST/api/v1/groups POST/api/v1/groups/{id}/soundsCreate a Group, add sounds
Request · POST groups
{ "name": "Car Pass-by" }
Response
{ "id": 7, "name": "Car Pass-by", "created": true }
Request · POST groups/7/sounds
{ "ids": [88123, 88130] }
Response
{ "added": 2, "skipped": 0, "missing": [] }
- If a Group with that name already exists you get it back with
"created": false. - Sounds already in the Group are skipped. Nothing is ever removed.
GET/api/v1/collections GET/api/v1/collections/{name}?path=Collection tree
- First call lists Collection names. Second returns one Collection’s folder tree with its sounds…
pathlimits it to one subfolder. - Collections work across databases. Each sound returns its path and cached metadata, plus its
idwhen it’s in the open database.
{
"name": "Car Pass-by",
"folders": [ { "name": "Exteriors", "folders": [...], "sounds": [...] } ],
"sounds": [...]
}POST/api/v1/collectionsCreate a Collection, add sounds
Request
{ "name": "Car Pass-by", "folder": "Exteriors/Close", "ids": [88123, 88130] }
Response
{
"name": "Car Pass-by", "folder": "Exteriors/Close", "created": true,
"added": 2, "skipped": 0, "missing": []
}
- The Collection and every folder in
folderare created if they don’t exist. - A Collection keeps its sounds in folders, so
folderis required when you sendids. - Sounds already in that folder are skipped. Nothing is ever removed.
Search & sounds
POST/api/v1/searchFull-text search + filters
Request
{
"query": "night",
"ucs_category": "AMBIENCE",
"ucs_subcategory": "URBAN",
"folder": "/Volumes/SFX/AMBIENCE/URBAN", // path starts-with
"import_id": 42,
"min_duration": 30,
"max_duration": 120,
"channels": 2,
"sample_rate": 48000,
"limit": 50,
"offset": 0
}
Response
{ "total": 312, "results": [ { sound }, ... ] }
Sound object
| Field | Notes |
|---|---|
id | Database id |
unique_id | Stable across databases |
path | Full file path |
online | File exists on disk right now |
cloud | true for CloudPack / Sound Swap files: no local path, can’t be transferred yet |
import_id, folder | Import + folder relative to its root |
catid, category, subcategory | UCS |
keywords, description | |
duration, channels, sample_rate, bit_depth | duration in seconds |
ucs_category/ucs_subcategorymatch the UCS name exactly (e.g.DOORS,METAL).folderis a full folder path as it is on disk (e.g. fromimports/{id}/folders); includes subfolders.queryuses basehead’s search syntax (e.g.car pass -truck).limit(default 50, max 500) /offsetpage the results so a broad search never returns hundreds of thousands of rows at once.
GET/api/v1/sounds/{id}One file, full details
- Everything in the sound object plus all metadata fields and markers (cue / region / loop / sync). Markers are read from WAV files.
GET/api/v1/selectionWhat the user selected
- The files currently selected in basehead’s Results… the user picks in basehead, your tool reads them.
Actions
POST/api/v1/import GET/api/v1/import/{job_id}Index new files
Request
{
"paths": ["/Volumes/SFX/Deliveries/Ep01"], // files and/or folders
"recursive": true, // true only for now (false returns 400)
"database": "Main Library", // optional: must be the open database (others are refused)
"import": { "id": 42 }, // or { "new": "Ep01 Deliveries" }
"group": { "new": "Car Pass-by" } // optional, or { "id": 7 }
}
Returns right away
{ "job_id": "imp-01", "status": "running" }
Poll GET import/{job_id}
{
"status": "done", "added": 212, "skipped": 3,
"failed": [ { "error": "..." } ],
"import_id": 42, "group_id": 7,
"assets": [ { "path": "...", "id": 88123 } ]
}
- basehead indexes files where they are… it never moves them.
- Leave out
"import"and a new Import is created, named after the folder. - One API import at a time… a second one returns
409until the first is done.
POST/api/v1/selectShow files in basehead
{ "ids": [88123, 88130], "foreground": true }
- Shows and selects those files in Results so the user can tag them with basehead’s own tools.
foreground(defaultfalse):truealso brings basehead to the front…falseleaves focus with your tool.- Up to 4000 ids per call.
POST/api/v1/transferCopy a sound to a folder
{
"id": 88123,
"dest": "/Projects/Ep01/SFX",
"sample_rate": 48000, // optional
"bit_depth": 24, // optional
"format": "wav" // optional: wav | flac
}
- Uses the user’s Transfer settings (rename, handles, etc.).
sample_rate/bit_depth/formatoverride for this call only… saved settings are never changed.- Returns the path of the written file.
- Cloud files (CloudPacks, Sound Swap) can’t be transferred yet.
destis created if it doesn’t exist.- One transfer at a time (
409if basehead is already transferring). sample_rate: 44100, 48000, 88200, 96000, 176400 or 192000.bit_depth: 16, 24 or 32. FLAC is max 24-bit and 8 channels.- Refused while Force Reference is on in Transfer options, and for split (virtual) records.
POST/api/v1/xferSend sounds to your DAW’s pool / bin
Request
{ "ids": [88123, 88130] }
Response
{
"daw": "REAPER", "sent": 2,
"files": ["/Volumes/SFX/...wav", "..."],
"skipped": [ { "id": 9, "reason": "offline (not found on disk)" } ]
}
- Same as pressing X in basehead: sends the sounds to the running target DAW (Options → Target Apps): REAPER, Pro Tools, Nuendo / Cubase, Logic, Premiere or Resolve.
- Always by reference: the DAW gets the original files and basehead makes no copy. (Pro Tools copies them into the session’s Audio Files folder itself.)
409if no target DAW is selected, it isn’t running, or basehead is busy transferring.- Cloud (CloudPack / Sound Swap), split and offline files are skipped with a reason. Pro Tools also skips FLAC files.
sentmeans handed to the DAW. If the DAW refuses something, basehead shows its usual message.- Up to 500 ids per call.
Later after v1
Already possible inside basehead, so good candidates once v1 is solid. Not promised… tell us which ones matter to you.
| Endpoint | Does |
|---|---|
GET fields/{name}/values | Distinct values for autocomplete |
GET sounds/missing · duplicates | Offline media and duplicates |
POST imports/{id}/rescan | Rescan an Import |
GET targets | Target DAWs, which is running, current project |
GET daw/session-files | Files used in the open DAW project |
GET searches/saved | Saved and recent searches |
POST reveal · open-in-editor | Finder / Explorer, wave editor |
GET sounds/{id}/stats | Peak, LUFS, DC offset |
POST convert · sausage | Format conversion, concatenate with cue markers |
POST ai/* | Generate SFX, variations, transcribe (uses credits) |
Not planned kept out
| What | Why |
|---|---|
| Spot to DAW | Outside Pro Tools, spot can only land at the DAW cursor… use xfer to put sounds in the DAW’s pool / bin and place them with your own tool. |
| Metadata writes | No DB or file metadata edits through the API for now. Use select and tag in basehead. |
| Playback | The API is headless… you get the full path and audition it yourself. |
| Deletes, licensing, secrets, settings writes | Too risky for an outside tool. |
New here?
basehead Local API · v1 spec · October 2026 · BaseHead, LLC. Subject to change before release.