Blog

Send local files to the Audial API without an upload step

Every Audial function accepts multipart/form-data with files inline. The field rules, why we added it, and curl examples for each endpoint.

October 9, 2026 5 min readAudial

The usual shape of an audio API is two calls. Upload the file, get a URL back, then post a JSON body that references that URL. It is a reasonable design and it is tedious in practice: two round trips, an intermediate value to carry around, and a JSON body you have to assemble before you can test anything.

Every Audial function also accepts multipart/form-data with local files sent inline. One call, one command, no upload step. This post covers the field rules, because they are simple but not obvious, and then the examples per endpoint.

The base URL is https://api.audialmusic.ai/api. Authentication is two headers, X-API-Key and X-User-ID. Every function requires an active subscription; without one the API answers HTTP 402 with code SUBSCRIPTION_REQUIRED.

Why we built it

The upload endpoint, POST /files/{userId}/upload, still exists and is still the right tool when you are reusing one file across several calls or already have assets in storage.

But most API use is not that. Most API use is somebody with a file on their laptop who wants to know what the endpoint does. For that person the two-step flow is pure friction: they have to understand our file object before they can run the function they came for, and the first thing they try, posting the file at the endpoint, does not work.

It also made the client libraries worse. Both the Python SDK and the MCP server take local paths and only local paths, because that is what the caller has. If the API has no inline path, every client has to reimplement upload-then-reference, and every client has to decide how to report a failure that happened in the upload rather than in the function.

So the rule became: the body is either the function's JSON request, or the same fields as form parts with files inline. Nothing else changes. Same endpoints, same field names, same responses.

The field rules

Six rules, and they cover every function.

1. A file part is stored like an upload. Posting a file inline is not a different code path. The file is stored exactly as the upload endpoint would store it, then placed into the request body under its own field name. You get the same behaviour either way.

2. A field whose name ends in URL, Url or _url receives the file's URL. So fileURL on the analysis endpoint becomes the string URL, matching what the JSON body expects.

3. Any other field receives a {filename, url, metadata} object. This covers original, reference, text2vox.midi and the rest. Those fields take an object in the JSON API, so they get an object here.

4. The repeatable field files becomes a list of {name, url}. Used by generate-midi. Repeat the part once per file.

5. Dotted field names set nested keys. fileUrls.vocals sets fileUrls.vocals in the body. text2vox.lyrics sets lyrics inside the text2vox object. This is what keeps nested requests expressible as flat form parts.

6. Text parts that parse as JSON are decoded. So an options object can go in as one part containing a JSON string rather than a dozen dotted fields. splitStemsRequest={"stemsRequested":["vocals","drums"]} works.

One convenience on top: userId defaults to the X-User-ID header, so you can leave the part out. The examples below include it because being explicit is cheaper than debugging.

Examples

Analysis. The simplest case, and the one that shows rule 2:

curl -X POST "https://api.audialmusic.ai/api/functions/run/primary-analysis" \
  -H "X-API-Key: your_api_key" -H "X-User-ID: your_user_id" \
  -F "userId=your_user_id" \
  -F "fileURL=@/path/to/track.mp3"

Returns BPM, key, energy, danceability and loudness.

Stem splitting. A file part plus a JSON text part:

curl -X POST "https://api.audialmusic.ai/api/functions/run/stem-splitter" \
  -H "X-API-Key: your_api_key" -H "X-User-ID: your_user_id" \
  -F "userId=your_user_id" \
  -F "original=@/path/to/track.mp3" \
  -F 'splitStemsRequest={"stemsRequested":["vocals","drums","bass","other"],"modelName":"primaudio"}'

BPM and key are detected automatically for stem splitting, segmentation, sample packs and MIDI, so you do not need to supply them.

Mastering with a reference. Two file parts, two objects in the body:

curl -X POST "https://api.audialmusic.ai/api/functions/run/mastering" \
  -H "X-API-Key: your_api_key" -H "X-User-ID: your_user_id" \
  -F "userId=your_user_id" \
  -F "original=@/path/to/track.mp3" \
  -F "reference=@/path/to/reference.mp3"

Segmentation with stems. This is rule 5 doing the work. Four dotted parts build the nested fileUrls object:

curl -X POST "https://api.audialmusic.ai/api/functions/run/segmentation" \
  -H "X-API-Key: your_api_key" -H "X-User-ID: your_user_id" \
  -F "userId=your_user_id" \
  -F "original=@/path/to/track.mp3" \
  -F "fileUrls.vocals=@/path/to/vocals.mp3" -F "fileUrls.drums=@/path/to/drums.mp3" \
  -F "fileUrls.bass=@/path/to/bass.mp3" -F "fileUrls.other=@/path/to/other.mp3" \
  -F 'segmentationParameters={"components":["bass","beat","melody","vocal"],"analysisType":"select_features","featuresToExtract":["energy","loudness"],"genre":"Tech House"}'

Audio to MIDI. Rule 4, with files repeated:

curl -X POST "https://api.audialmusic.ai/api/functions/run/generate-midi" \
  -H "X-API-Key: your_api_key" -H "X-User-ID: your_user_id" \
  -F "userId=your_user_id" \
  -F "files=@/path/to/bass.mp3" \
  -F "files=@/path/to/other.mp3" \
  -F "bpm=120"

Sample pack.

curl -X POST "https://api.audialmusic.ai/api/functions/run/sample-pack" \
  -H "X-API-Key: your_api_key" -H "X-User-ID: your_user_id" \
  -F "userId=your_user_id" \
  -F "original=@/path/to/track.mp3" \
  -F 'samplePackParameters={"jobType":"sample_pack","components":["drums","bass","melody"],"genre":"Tech House"}'

Music generation. A cover needs a reference file; text to music needs no file at all and can be sent as JSON or as form parts:

curl -X POST "https://api.audialmusic.ai/api/functions/run/music-generator" \
  -H "X-API-Key: your_api_key" -H "X-User-ID: your_user_id" \
  -F "userId=your_user_id" \
  -F "task_type=cover" \
  -F "prompt=acoustic folk cover, fingerpicked guitar, warm male vocal" \
  -F "referenceFile=@/path/to/reference.mp3"

Resynthesis. One short clip:

curl -X POST "https://api.audialmusic.ai/api/functions/run/sound2vital" \
  -H "X-API-Key: your_api_key" -H "X-User-ID: your_user_id" \
  -F "userId=your_user_id" \
  -F "original=@/path/to/clip.wav"

Sung vocals. Rule 5 again, mixing a nested file part with nested text parts:

curl -X POST "https://api.audialmusic.ai/api/functions/run/text2vox" \
  -H "X-API-Key: your_api_key" -H "X-User-ID: your_user_id" \
  -F "userId=your_user_id" \
  -F "original=@/path/to/voice.wav" \
  -F "text2vox.midi=@/path/to/song.mid" \
  -F "text2vox.lyrics=la la la la" \
  -F "text2vox.lyricsMode=auto"

Notes for client authors

A few things worth knowing if you are wrapping this.

Long-running functions return an execution immediately rather than the finished result. sound2vital and text2vox return {"exeId": "...", "state": "created"}, and you poll GET /api/db/{userId}/execution/{exeId} until state is completed or failed. Build the polling loop with a sane timeout and surface elapsed time to the caller, because these jobs take minutes.

Accepted input formats are MP3, WAV, FLAC, OPUS, OGG, AAC, M4A and AIFF, up to 100 MB.

Do not hand-roll the multipart body if your language has a library for it. Boundary handling and filename encoding are the two things that go wrong, and both are solved problems.

And if you are working in Python, or inside an AI assistant, this is already done for you. The Python SDK and the MCP server both take local paths and handle the rest.

Where to go next

Every endpoint's reference page shows both forms side by side: the JSON body, and the same call with a local file as multipart.

See the API reference, which also offers the whole endpoint set as Markdown and as an OpenAPI spec, and the MCP server if you would rather call these functions from an assistant than from a terminal.

apicurlfiles