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

# Stem Separation

> Extract individual audio components from mixed recordings using AI-powered audio source separation.

## Overview

AudioPod AI's Stem Separation API uses state-of-the-art AI models to separate mixed audio recordings into individual components (stems). Extract vocals, drums, bass, guitar, piano, and more from any song.

### Key Features

* **Granular Separation**: `two`, `four`, `six`, plus the detailed `producer`, `studio`, and `mastering` decompositions
* **Single-Instrument Isolation**: Target one instrument from a 45-instrument catalog and get it back alongside an everything-else track
* **File Upload & URL Support**: Process local files or YouTube/SoundCloud URLs
* **Quality Scores**: Get AI-assessed quality metrics for each stem
* **Presigned Downloads**: Direct S3 URLs for fast stem downloads

## Installation

<Tabs>
  <Tab title="Python">
    ```bash theme={null}
    pip install audiopod
    ```
  </Tab>

  <Tab title="Node.js">
    ```bash theme={null}
    npm install audiopod
    ```
  </Tab>
</Tabs>

## Authentication

Get your API key from the [API Keys Dashboard](https://www.audiopod.ai/dashboard/account/api-keys).

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    from audiopod import AudioPod

    client = AudioPod(api_key="ap_your_api_key")
    # Or set AUDIOPOD_API_KEY environment variable
    ```
  </Tab>

  <Tab title="Node.js">
    ```typescript theme={null}
    import AudioPod from 'audiopod';

    const client = new AudioPod({ apiKey: 'ap_your_api_key' });
    // Or set AUDIOPOD_API_KEY environment variable
    ```
  </Tab>

  <Tab title="cURL">
    ```bash theme={null}
    export AUDIOPOD_API_KEY="ap_your_api_key"
    # Use -H "X-API-Key: $AUDIOPOD_API_KEY" in requests
    ```
  </Tab>
</Tabs>

## Separation Modes

Pass one of these as `mode` to `POST /api/v1/stem-extraction/api/extract`.

| Mode        | Output                                                       | Plan    | Use Case                         |
| ----------- | ------------------------------------------------------------ | ------- | -------------------------------- |
| `single`    | One named stem + everything-else                             | Basic   | Vocal isolation, drum extraction |
| `two`       | vocals + instrumental                                        | Basic   | Karaoke tracks                   |
| `four`      | vocals, drums, bass, other                                   | Basic   | Standard remixing                |
| `six`       | + guitar, piano                                              | Basic   | Full instrument separation       |
| `producer`  | Drums split into kick, snare, hi-hat — up to 8               | Creator | Beat production                  |
| `studio`    | Full drum kit plus a bass frequency split — up to 12         | Pro     | Professional mixing              |
| `mastering` | Full kit, bass split, and vocal stereo separation — up to 16 | Pro     | Maximum detail                   |

<Info>
  **Counts are a ceiling, not a promise.** `producer`, `studio`, and `mastering`
  decompose whatever is actually in the recording, so the stem count depends on
  the track — a song with no cymbals yields no cymbal stem. Plan for "up to N".
</Info>

<Warning>
  **Plan gating.** `producer` requires **Creator** or above; `studio` and
  `mastering` require **Pro** or above. A request from a lower plan is rejected —
  call `GET /api/v1/stem-extraction/packages/available` to see what the
  authenticated key can use before you dispatch a job.
</Warning>

For `mode="single"` this endpoint accepts the six core stems — `vocals`,
`drums`, `bass`, `guitar`, `piano`, `other`. To reach the full instrument
catalog, use the advanced endpoint below.

***

## The 45-Instrument Catalog

Beyond the fixed modes, you can name **one** instrument out of a 45-entry
catalog and get that instrument back plus an everything-else track. The 45 is
the size of the menu you can choose from — not the number of files a job
returns.

Use `POST /api/v1/stem-extraction/extract/advanced` with `single_stem` set to a
catalog id. The service resolves the right processing path and plan check for
you, so you never pick an engine.

<Tabs>
  <Tab title="cURL">
    ```bash theme={null}
    # Isolate the saxophone from a track
    curl -X POST https://api.audiopod.ai/api/v1/stem-extraction/extract/advanced \
      -H "X-API-Key: $AUDIOPOD_API_KEY" \
      -F "url=https://youtube.com/watch?v=VIDEO_ID" \
      -F "single_stem=saxophone"
    ```
  </Tab>

  <Tab title="Exclude one instrument">
    ```bash theme={null}
    # Everything EXCEPT the saxophone (JSON array, one id)
    curl -X POST https://api.audiopod.ai/api/v1/stem-extraction/extract/advanced \
      -H "X-API-Key: $AUDIOPOD_API_KEY" \
      -F "url=https://youtube.com/watch?v=VIDEO_ID" \
      -F 'exclude_stems=["saxophone"]'
    ```
  </Tab>
</Tabs>

### Discover the catalog at runtime

Don't hard-code the list — plan gating and catalog contents change. `GET
/api/v1/stem-extraction/catalog` returns every stem with the plan it needs and
whether the authenticated key can use it right now:

```bash theme={null}
curl -s https://api.audiopod.ai/api/v1/stem-extraction/catalog \
  -H "X-API-Key: $AUDIOPOD_API_KEY"
```

Each entry carries an `id` (what you send as `single_stem`), a `label` and
`description` for your UI, a `group`, the `public_tier` it requires, an
`allowed` boolean for the current key, and `excludable` (whether it can be used
with `exclude_stems`).

### Catalog contents

**Core — available on every plan including Basic (7)**

`vocals` · `backing_track` · `drums` · `bass` · `guitar` · `piano` · `other`

**Specialty vocals — Creator and above (4)**

`lead_vocal` · `backing_vocal` · `male_vocal` · `female_vocal`

**Instruments — Creator and above (34)**

`acoustic_guitar` · `electric_guitar` · `lead_guitar` · `rhythm_guitar` ·
`bagpipes` · `banjo` · `bassoon` · `brass` · `braam` · `celesta` · `cello` ·
`choir` · `clarinet` · `double_bass` · `flute` · `french_horn` · `fx` · `harp` ·
`harmonica` · `keys` · `mandolin` · `oboe` · `organ` · `percussion` ·
`plucked_strings` · `strings` · `saxophone` · `trombone` · `trumpet` · `tuba` ·
`viola` · `violin` · `woodwind` · `xylophone`

<Warning>
  Requesting a Creator-tier stem with a Basic key is rejected. Check `allowed` on
  the catalog response first, or handle the error and prompt for an upgrade.
</Warning>

<Info>
  **Pricing.** Catalog isolation is metered per minute of audio like every other
  stem job. A few premium selections carry a multiplier — see
  [API Wallet & Pricing](/account/api-wallet).
</Info>

***

## Quick Start

### Six-Stem Separation

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    from audiopod import AudioPod

    client = AudioPod(api_key="ap_your_api_key")

    # Extract and wait for completion
    result = client.stems.separate(
        url="https://youtube.com/watch?v=VIDEO_ID",
        mode="six"
    )

    # Download stems
    print("Stems ready:")
    for stem, url in result["download_urls"].items():
        print(f"  {stem}: {url[:60]}...")
    ```
  </Tab>

  <Tab title="Node.js">
    ```typescript theme={null}
    import AudioPod from 'audiopod';

    const client = new AudioPod({ apiKey: 'ap_your_api_key' });

    // Extract and wait for completion
    const result = await client.stems.separate({
      url: 'https://youtube.com/watch?v=VIDEO_ID',
      mode: 'six'
    });

    // Download stems
    console.log('Stems ready:');
    for (const [stem, url] of Object.entries(result.download_urls)) {
      console.log(`  ${stem}: ${url.substring(0, 60)}...`);
    }
    ```
  </Tab>

  <Tab title="cURL">
    ```bash theme={null}
    # Submit job
    curl -X POST "https://api.audiopod.ai/api/v1/stem-extraction/api/extract" \
      -H "X-API-Key: $AUDIOPOD_API_KEY" \
      -F "url=https://youtube.com/watch?v=VIDEO_ID" \
      -F "mode=six"

    # Check status (replace JOB_ID)
    curl -X GET "https://api.audiopod.ai/api/v1/stem-extraction/status/JOB_ID" \
      -H "X-API-Key: $AUDIOPOD_API_KEY"
    ```
  </Tab>
</Tabs>

***

## Examples by Mode

### Four-Stem Separation (Standard)

Extract vocals, drums, bass, and other.

<Tabs>
  <Tab title="Python (URL)">
    ```python theme={null}
    from audiopod import AudioPod

    client = AudioPod(api_key="ap_your_api_key")

    result = client.stems.separate(
        url="https://youtube.com/watch?v=VIDEO_ID",
        mode="four"
    )

    print(f"Vocals: {result['download_urls']['vocals']}")
    print(f"Drums: {result['download_urls']['drums']}")
    print(f"Bass: {result['download_urls']['bass']}")
    print(f"Other: {result['download_urls']['other']}")
    ```
  </Tab>

  <Tab title="Python (File)">
    ```python theme={null}
    from audiopod import AudioPod

    client = AudioPod(api_key="ap_your_api_key")

    result = client.stems.separate(
        file="/path/to/song.mp3",
        mode="four"
    )

    for stem, url in result["download_urls"].items():
        print(f"{stem}: {url}")
    ```
  </Tab>

  <Tab title="Node.js (URL)">
    ```typescript theme={null}
    import AudioPod from 'audiopod';

    const client = new AudioPod({ apiKey: 'ap_your_api_key' });

    const result = await client.stems.separate({
      url: 'https://youtube.com/watch?v=VIDEO_ID',
      mode: 'four'
    });

    console.log(`Vocals: ${result.download_urls.vocals}`);
    console.log(`Drums: ${result.download_urls.drums}`);
    console.log(`Bass: ${result.download_urls.bass}`);
    console.log(`Other: ${result.download_urls.other}`);
    ```
  </Tab>

  <Tab title="Node.js (File)">
    ```typescript theme={null}
    import AudioPod from 'audiopod';

    const client = new AudioPod({ apiKey: 'ap_your_api_key' });

    const result = await client.stems.separate({
      file: '/path/to/song.mp3',
      mode: 'four'
    });

    for (const [stem, url] of Object.entries(result.download_urls)) {
      console.log(`${stem}: ${url}`);
    }
    ```
  </Tab>

  <Tab title="cURL (URL)">
    ```bash theme={null}
    curl -X POST "https://api.audiopod.ai/api/v1/stem-extraction/api/extract" \
      -H "X-API-Key: $AUDIOPOD_API_KEY" \
      -F "url=https://youtube.com/watch?v=VIDEO_ID" \
      -F "mode=four"
    ```
  </Tab>

  <Tab title="cURL (File)">
    ```bash theme={null}
    curl -X POST "https://api.audiopod.ai/api/v1/stem-extraction/api/extract" \
      -H "X-API-Key: $AUDIOPOD_API_KEY" \
      -F "file=@/path/to/song.mp3" \
      -F "mode=four"
    ```
  </Tab>
</Tabs>

***

### Single Stem Extraction (Vocals Only)

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    from audiopod import AudioPod

    client = AudioPod(api_key="ap_your_api_key")

    # Extract only vocals
    result = client.stems.separate(
        url="https://youtube.com/watch?v=VIDEO_ID",
        mode="single",
        stem="vocals"  # Options: vocals, drums, bass, guitar, piano, other
    )

    print(f"Vocals: {result['download_urls']['vocals']}")
    ```
  </Tab>

  <Tab title="Node.js">
    ```typescript theme={null}
    import AudioPod from 'audiopod';

    const client = new AudioPod({ apiKey: 'ap_your_api_key' });

    // Extract only vocals
    const result = await client.stems.separate({
      url: 'https://youtube.com/watch?v=VIDEO_ID',
      mode: 'single',
      stem: 'vocals' // Options: vocals, drums, bass, guitar, piano, other
    });

    console.log(`Vocals: ${result.download_urls.vocals}`);
    ```
  </Tab>

  <Tab title="cURL">
    ```bash theme={null}
    curl -X POST "https://api.audiopod.ai/api/v1/stem-extraction/api/extract" \
      -H "X-API-Key: $AUDIOPOD_API_KEY" \
      -F "url=https://youtube.com/watch?v=VIDEO_ID" \
      -F "mode=single" \
      -F "stem=vocals"
    ```
  </Tab>
</Tabs>

***

### Two-Stem Separation (Karaoke)

Separate vocals from instrumental.

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    from audiopod import AudioPod

    client = AudioPod(api_key="ap_your_api_key")

    result = client.stems.separate(
        url="https://youtube.com/watch?v=VIDEO_ID",
        mode="two"
    )

    # Returns: vocals + no_vocals (instrumental)
    print(f"Vocals: {result['download_urls']['vocals']}")
    print(f"Instrumental: {result['download_urls']['no_vocals']}")
    ```
  </Tab>

  <Tab title="Node.js">
    ```typescript theme={null}
    import AudioPod from 'audiopod';

    const client = new AudioPod({ apiKey: 'ap_your_api_key' });

    const result = await client.stems.separate({
      url: 'https://youtube.com/watch?v=VIDEO_ID',
      mode: 'two'
    });

    // Returns: vocals + no_vocals (instrumental)
    console.log(`Vocals: ${result.download_urls.vocals}`);
    console.log(`Instrumental: ${result.download_urls.no_vocals}`);
    ```
  </Tab>

  <Tab title="cURL">
    ```bash theme={null}
    curl -X POST "https://api.audiopod.ai/api/v1/stem-extraction/api/extract" \
      -H "X-API-Key: $AUDIOPOD_API_KEY" \
      -F "url=https://youtube.com/watch?v=VIDEO_ID" \
      -F "mode=two"
    ```
  </Tab>
</Tabs>

***

### Producer Mode (8 Stems with Drum Kit)

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    from audiopod import AudioPod

    client = AudioPod(api_key="ap_your_api_key")

    result = client.stems.separate(
        url="https://youtube.com/watch?v=VIDEO_ID",
        mode="producer"
    )

    # Returns: vocals, kick, snare, hihat, bass, guitar, piano, other
    for stem, url in result["download_urls"].items():
        print(f"{stem}: {url[:60]}...")
    ```
  </Tab>

  <Tab title="Node.js">
    ```typescript theme={null}
    import AudioPod from 'audiopod';

    const client = new AudioPod({ apiKey: 'ap_your_api_key' });

    const result = await client.stems.separate({
      url: 'https://youtube.com/watch?v=VIDEO_ID',
      mode: 'producer'
    });

    // Returns: vocals, kick, snare, hihat, bass, guitar, piano, other
    for (const [stem, url] of Object.entries(result.download_urls)) {
      console.log(`${stem}: ${url.substring(0, 60)}...`);
    }
    ```
  </Tab>

  <Tab title="cURL">
    ```bash theme={null}
    curl -X POST "https://api.audiopod.ai/api/v1/stem-extraction/api/extract" \
      -H "X-API-Key: $AUDIOPOD_API_KEY" \
      -F "url=https://youtube.com/watch?v=VIDEO_ID" \
      -F "mode=producer"
    ```
  </Tab>
</Tabs>

***

## Advanced Usage

### Async Job Handling

For more control, use `extract()` and `wait_for_completion()` separately:

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    from audiopod import AudioPod

    client = AudioPod(api_key="ap_your_api_key")

    # Submit job (returns immediately)
    job = client.stems.extract(
        url="https://youtube.com/watch?v=VIDEO_ID",
        mode="six"
    )
    print(f"Job submitted: {job['id']}")

    # Do other work...

    # Wait for completion when ready
    result = client.stems.wait_for_completion(job["id"], timeout=600)

    if result["status"] == "COMPLETED":
        print("Download URLs:")
        for stem, url in result["download_urls"].items():
            print(f"  {stem}: {url}")
    ```
  </Tab>

  <Tab title="Node.js">
    ```typescript theme={null}
    import AudioPod from 'audiopod';

    const client = new AudioPod({ apiKey: 'ap_your_api_key' });

    // Submit job (returns immediately)
    const job = await client.stems.extract({
      url: 'https://youtube.com/watch?v=VIDEO_ID',
      mode: 'six'
    });
    console.log(`Job submitted: ${job.id}`);

    // Do other work...

    // Wait for completion when ready
    const result = await client.stems.waitForCompletion(job.id, 600000);

    if (result.status === 'COMPLETED') {
      console.log('Download URLs:');
      for (const [stem, url] of Object.entries(result.download_urls)) {
        console.log(`  ${stem}: ${url}`);
      }
    }
    ```
  </Tab>
</Tabs>

### Get Available Modes

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    from audiopod import AudioPod

    client = AudioPod(api_key="ap_your_api_key")

    modes = client.stems.modes()

    print("Available modes:")
    for m in modes["modes"]:
        print(f"  {m['mode']}: {m['description']} ({m['stem_count']} stems)")
    ```
  </Tab>

  <Tab title="Node.js">
    ```typescript theme={null}
    import AudioPod from 'audiopod';

    const client = new AudioPod({ apiKey: 'ap_your_api_key' });

    const modes = await client.stems.modes();

    console.log('Available modes:');
    modes.modes.forEach(m => {
      console.log(`  ${m.mode}: ${m.description} (${m.stem_count} stems)`);
    });
    ```
  </Tab>

  <Tab title="cURL">
    ```bash theme={null}
    curl -X GET "https://api.audiopod.ai/api/v1/stem-extraction/modes"
    ```
  </Tab>
</Tabs>

### Check Job Status

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    from audiopod import AudioPod

    client = AudioPod(api_key="ap_your_api_key")

    status = client.stems.status(5512)

    print(f"Status: {status['status']}")

    if status["status"] == "COMPLETED":
        print("Quality scores:")
        for stem, score in status["quality_scores"].items():
            print(f"  {stem}: {score:.2f}")
    ```
  </Tab>

  <Tab title="Node.js">
    ```typescript theme={null}
    import AudioPod from 'audiopod';

    const client = new AudioPod({ apiKey: 'ap_your_api_key' });

    const status = await client.stems.status(5512);

    console.log(`Status: ${status.status}`);

    if (status.status === 'COMPLETED') {
      console.log('Quality scores:');
      for (const [stem, score] of Object.entries(status.quality_scores)) {
        console.log(`  ${stem}: ${score.toFixed(2)}`);
      }
    }
    ```
  </Tab>

  <Tab title="cURL">
    ```bash theme={null}
    curl -X GET "https://api.audiopod.ai/api/v1/stem-extraction/status/5512" \
      -H "X-API-Key: $AUDIOPOD_API_KEY"
    ```
  </Tab>
</Tabs>

***

## Response Format

**Completed Job Response:**

```json theme={null}
{
  "id": 5512,
  "status": "COMPLETED",
  "source_type": "URL",
  "stem_paths": {
    "vocals": "stems/5512/vocals.wav",
    "drums": "stems/5512/drums.wav",
    "bass": "stems/5512/bass.wav",
    "guitar": "stems/5512/guitar.wav",
    "piano": "stems/5512/piano.wav",
    "other": "stems/5512/other.wav"
  },
  "quality_scores": {
    "vocals": 0.64,
    "drums": 0.35,
    "bass": 0.87,
    "guitar": 0.38,
    "piano": 0.0,
    "other": 0.36
  },
  "download_urls": {
    "vocals": "https://media.audiopod.ai/...",
    "drums": "https://media.audiopod.ai/...",
    "bass": "https://media.audiopod.ai/...",
    "guitar": "https://media.audiopod.ai/...",
    "piano": "https://media.audiopod.ai/...",
    "other": "https://media.audiopod.ai/..."
  },
  "completed_at": "2025-12-11T12:39:37.379848"
}
```

***

## Pricing

**Base rate:** \$0.10 per minute of audio processed for every standard mode —
`single`, `two`, `four`, `six`, `producer`, `studio`, `mastering`.

| Duration   | Cost (standard modes) |
| ---------- | --------------------- |
| 3 minutes  | \$0.30                |
| 5 minutes  | \$0.50                |
| 10 minutes | \$1.00                |
| 1 hour     | \$6.00                |

### Premium catalog selections

Some catalog selections run on a higher-fidelity engine and bill at a multiplier
of the base rate:

| Selection                                                  | Multiplier | Effective rate |
| ---------------------------------------------------------- | ---------- | -------------- |
| Lead / backing vocal split (`lead_vocal`, `backing_vocal`) | **1.5×**   | \$0.15/min     |
| Vocal gender split (`male_vocal`, `female_vocal`)          | **1.5×**   | \$0.15/min     |
| Single-instrument isolation from the 45-instrument catalog | **2.0×**   | \$0.20/min     |

<Info>
  Check your balance and usage at [API Wallet](/account/api-wallet).
</Info>

***

## Error Handling

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    from audiopod import AudioPod, InsufficientBalanceError, AuthenticationError

    try:
        client = AudioPod(api_key="ap_...")
        result = client.stems.separate(url="...", mode="six")
    except AuthenticationError:
        print("Invalid API key")
    except InsufficientBalanceError as e:
        print(f"Need ${e.required_cents / 100:.2f}, have ${e.available_cents / 100:.2f}")
    except ValueError as e:
        print(f"Invalid input: {e}")
    ```
  </Tab>

  <Tab title="Node.js">
    ```typescript theme={null}
    import AudioPod, { InsufficientBalanceError, AuthenticationError } from 'audiopod';

    try {
      const client = new AudioPod({ apiKey: 'ap_...' });
      const result = await client.stems.separate({ url: '...', mode: 'six' });
    } catch (error) {
      if (error instanceof AuthenticationError) {
        console.log('Invalid API key');
      } else if (error instanceof InsufficientBalanceError) {
        console.log(`Need $${error.requiredCents / 100}, have $${error.availableCents / 100}`);
      } else {
        console.log(`Error: ${error.message}`);
      }
    }
    ```
  </Tab>
</Tabs>

***

## Status Codes

| Status       | Description                           |
| ------------ | ------------------------------------- |
| `PENDING`    | Job queued, waiting for worker        |
| `PROCESSING` | Stem separation in progress           |
| `COMPLETED`  | Success - download URLs available     |
| `FAILED`     | Error occurred - check error\_message |

***

## Next Steps

<Columns cols={2}>
  <Card title="API Wallet" icon="wallet" href="/account/api-wallet">
    Manage billing and view usage history.
  </Card>

  <Card title="Noise Reduction" icon="volume-xmark" href="/api-reference/noise-reduction">
    Clean up stems for better quality.
  </Card>
</Columns>
