> ## Documentation Index
> Fetch the complete documentation index at: https://docs.soundpiece.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Separate stems

> Split a mixed track into vocals, drums, bass, other and instrumental

`adapt.separate_stems` takes a finished, mixed track and splits it into its parts ("stems"), each as its own file. Use it to make an instrumental or a vocal-only version of a song, to remix or re-balance a track, or to pull out a drum loop.

***

## The request

| Field | Required | What it is |
| - | - | - |
| `source` | yes | The track: `{"url": "https://..."}`, a public link to a WAV, MP3 or FLAC file. |
| `idempotency_key` | yes | A UUID that identifies this job. The [reference client](/client) fills it in for you. |
| `callback_url` | no | An HTTPS address to receive a [webhook](/webhooks) when the work is finished. |

There is nothing else to choose: every request returns all five stems.

Every field, with its exact rules: [submit](/api-reference/adapt/separate_stems/submit) and [poll](/api-reference/adapt/separate_stems/poll) in the API Reference.

***

## Example

This separates a song and saves each stem as `<kind>.flac`. It uses the [reference client](/client); save `soundpiece.py` or `soundpiece.mjs` next to it first.

```python stems.py theme={null}
import soundpiece

stems = soundpiece.run("adapt.separate_stems", {
    "source": {"url": "https://example.com/audio/song.mp3"},
})

for stem in stems["outputs"]:
    soundpiece.download(stem["url"], f"{stem['kind']}.flac")
    print(f"Saved {stem['kind']}.flac")
```

```javascript stems.mjs theme={null}
import { run, download } from "./soundpiece.mjs";

const stems = await run("adapt.separate_stems", {
  source: { url: "https://example.com/audio/song.mp3" },
});

for (const stem of stems.outputs) {
  await download(stem.url, `${stem.kind}.flac`);
  console.log(`Saved ${stem.kind}.flac`);
}
```

```bash cURL theme={null}
#!/usr/bin/env bash
# Submit, wait and download by hand. Needs curl, jq and uuidgen.
set -euo pipefail
API=https://api.soundpiece.co/v1
AUTH="Authorization: Bearer $SOUNDPIECE_API_KEY"

# 1. Submit. Sending this exact body again (same idempotency_key) is safe.
body=$(jq -n --arg key "$(uuidgen)" '{
  idempotency_key: $key,
  source: { url: "https://example.com/audio/song.mp3" }
}')
response=$(curl --silent --show-error --max-time 30 --request PUT "$API/adapt.separate_stems" \
  --header "$AUTH" --header "Content-Type: application/json" --data "$body")
result=$(jq -e .stems <<<"$response") || { echo "$response"; exit 1; }
id=$(jq -r .id <<<"$result")

# 2. Wait. Each call returns as soon as the work is finished, or after 20 seconds.
while [ "$(jq -r .status <<<"$result")" = processing ]; do
  response=$(curl --silent --show-error --max-time 30 "$API/adapt.separate_stems?id=$id&wait=true" \
    --header "$AUTH")
  result=$(jq -e .stems <<<"$response") || { echo "$response"; exit 1; }
done

# 3. Download each stem, or show what went wrong.
if [ "$(jq -r .status <<<"$result")" = failed ]; then
  jq .error <<<"$result"
  exit 1
fi
jq -r '.outputs[] | "\(.kind) \(.url)"' <<<"$result" | while read -r kind url; do
  curl --silent --show-error --fail --output "$kind.flac" "$url"
  echo "Saved $kind.flac"
done
```

***

## The result

`run` returns the finished `stems` object, with one entry in `outputs` per stem:

```json theme={null}
{
  "id": "op_01ks2q4m7rfx2b9c3d5e8g1h6j",
  "status": "ready",
  "created_at": "2026-10-10T09:15:02Z",
  "idempotency_key": "d4e5f6a7-b8c9-4d0e-9f1a-2b3c4d5e6f7a",
  "outputs": [
    { "kind": "vocals", "url": "https://cdn.soundpiece.co/...signed...", "expires_at": "2026-10-10T09:31:40Z", "duration": 201.4 },
    { "kind": "drums", "url": "https://cdn.soundpiece.co/...signed...", "expires_at": "2026-10-10T09:31:40Z", "duration": 201.4 },
    { "kind": "bass", "url": "https://cdn.soundpiece.co/...signed...", "expires_at": "2026-10-10T09:31:40Z", "duration": 201.4 },
    { "kind": "other", "url": "https://cdn.soundpiece.co/...signed...", "expires_at": "2026-10-10T09:31:40Z", "duration": 201.4 },
    { "kind": "instrumental", "url": "https://cdn.soundpiece.co/...signed...", "expires_at": "2026-10-10T09:31:40Z", "duration": 201.4 }
  ],
  "error": null
}
```

| Field | What it means |
| - | - |
| `outputs[].kind` | Which stem the file is (see below). Find a stem by its `kind`, not by its position in the list. |
| `outputs[].url` | A signed link to the stem, as a FLAC file (44.1 kHz). It needs no API key. |
| `outputs[].expires_at` | When the link stops working. Poll the operation again for a fresh one. |
| `outputs[].duration` | The stem's length in seconds, the same as the source's. |

| `kind` | What's in it |
| - | - |
| `vocals` | The lead and backing vocals on their own. |
| `drums` | The drums and percussion. |
| `bass` | The bassline. |
| `other` | Everything else: guitars, keys, synths and other melodic instruments. |
| `instrumental` | The whole track without the vocals (everything except `vocals`, together). |

***

## Choosing parameters

There are none to choose, but a few things help:

* **Start from the best copy you have.** A WAV or FLAC separates more cleanly than a heavily compressed MP3.
* **Want only an instrumental?** Download just the `instrumental` stem; it is already the track minus its vocals, so you don't need to mix the others back together.
* **Want new music in the same style instead?** Look at [`create.remix_song`](/guides/create/remix_song) or [`create.remix_instrumental`](/guides/create/remix_instrumental).

***

## Errors

**When you submit** (the request is refused, nothing is charged):

| Status | `type` | Why |
| - | - | - |
| `422` | `source_too_large` | The source's server says the file is over 50 MB. (When it doesn't say, the size is checked during processing instead.) |
| `422` | `idempotency_key_reused` | The key was already used for a different endpoint. |
| `402` | `insufficient_credits` | Your balance is below 3 credits. |

**While processing** (the operation finishes as `failed`, with an `error.code`):

| Code | Why |
| - | - |
| `source_unreachable` | The source URL couldn't be fetched. |
| `source_format_unsupported` | The file isn't WAV, MP3 or FLAC audio. |
| `source_too_short` | The audio is shorter than 1 second. |
| `source_too_long` | The audio is longer than 5 minutes. |
| `source_too_large` | The file is larger than 50 MB. |
| `internal_error`, `generation_timeout` | Something went wrong on our side. Resubmit with the same idempotency key. |

**Source limits for this endpoint:** WAV, MP3 or FLAC, from 1 second to 5 minutes long, and at most 50 MB.

See [Errors](/errors) for the response shapes, and for the errors every endpoint can return (`401`, `403`, `429`).

***

## Cost

**3 credits** per request, for all five stems. Resending a request with the same idempotency key is not charged again.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.