DEV Community

StemSplit
StemSplit

Posted on

Announcing n8n-nodes-stemsplit: Stem Separation as a Native n8n Node (2026)

If you've ever wired an audio job into n8n with raw HTTP nodes, you know the shape: an HTTP Request to submit, a Wait node, an HTTP Request to poll, an IF to check status, a loop back to Wait, and finally a Set node to flatten the result. Five nodes, two branches, one fragile timeout you'll only discover at 2am.

n8n-nodes-stemsplit collapses all of that into one node.

The community node is now live on npm. It exposes the StemSplit API — vocal removal, full 4-stem and 6-stem separation, job status, and balance — as five typed operations with first-class binary support, presigned download URLs, and a built-in polling loop.

What You'll Learn

  • ✅ How to install n8n-nodes-stemsplit from the n8n Community Nodes UI
  • ✅ How to set up the StemSplit API credential (single field, sk_live_ key)
  • ✅ The five operations the node exposes and when to use each one
  • ✅ How to build a vocal remover in three nodes — Read Binary File → Separate Stems (Wait) → HTTP Request
  • ✅ Why Separate Stems (Wait) removes the HTTP + Wait + IF polling pattern entirely
  • ✅ A workflow JSON snippet you can paste into n8n today

Prerequisites

  • n8n v0.200 or later (self-hosted or n8n.cloud with community nodes enabled)
  • A StemSplit API key — sign up at stemsplit.io; the free tier is enough to run the workflow below

If you already have an HTTP Request + Wait pipeline using the StemSplit REST API, this node is a drop-in replacement — same auth, same job semantics, fewer nodes.


Installing the community node

In n8n, open Settings → Community Nodes → Install a community node, paste n8n-nodes-stemsplit, accept the risk prompt, and hit Install. n8n will install it from npm and register the new node automatically — no restart needed on most setups.

For self-hosted Docker users who prefer the CLI:

npm install n8n-nodes-stemsplit
# then restart your n8n container
Enter fullscreen mode Exit fullscreen mode

The package publishes with npm provenance, which is what n8n's verified-community-node program checks against.


Setting up the StemSplit credential

The credential type is StemSplit API. It has one required field — your API key — and the node attaches Authorization: Bearer <key> to every request against https://stemsplit.io/api/v1.

  1. Add a new credential of type StemSplit API
  2. Generate a key at stemsplit.io/app/settings/api (format: sk_live_...)
  3. Paste it in and save

n8n's built-in credential test calls GET /balance and surfaces the result, so you'll know immediately if the key is wrong before you wire it into a workflow.


The five operations

Operation When to use it
Separate Stems (Wait for Completion) Synchronous-feeling — submit a file, get presigned download URLs back in the same node execution
Separate Stems Fire-and-forget — submit many jobs in parallel and poll later with Get Job
Get Job Look up a single job by ID; returns the same fields as the wait variant
List Jobs Page through job history with optional status filter (PENDING, PROCESSING, COMPLETED, FAILED, EXPIRED)
Get Balance Check remaining credits before a batch run

Both Separate Stems variants accept input as a Binary File (any upstream binary-capable node) or a public URL (StemSplit fetches it server-side, so you don't need to hold the file in n8n memory).

The Output Type parameter controls which stems you get back:

Value Output URLs
VOCALS vocalsUrl
INSTRUMENTAL instrumentalUrl
BOTH (default) vocalsUrl + instrumentalUrl
FOUR_STEMS vocalsUrl, drumsUrl, bassUrl, otherUrl
SIX_STEMS vocalsUrl, drumsUrl, bassUrl, pianoUrl, guitarUrl, otherUrl (requires Quality: Best)

Each presigned URL is valid for 1 hour after job completion. Output files are deleted 14 days after creation, so this is not a long-term storage layer — pipe the URLs into S3, Drive, or your own bucket if you want them around longer.


Three-node vocal remover

The simplest workflow that does something useful:

[Read Binary File: song.mp3]
  → [StemSplit: Separate Stems (Wait), Output Type: VOCALS]
  → [HTTP Request: GET vocalsUrl → Write Binary File]
Enter fullscreen mode Exit fullscreen mode

Concretely, here's the workflow as JSON — paste it into Workflows → Import from File / URL → Paste:

{
  "name": "StemSplit Vocal Remover",
  "nodes": [
    {
      "parameters": {
        "filePath": "/data/input/song.mp3"
      },
      "name": "Read Binary File",
      "type": "n8n-nodes-base.readBinaryFile",
      "typeVersion": 1,
      "position": [240, 300]
    },
    {
      "parameters": {
        "operation": "separateStemsWait",
        "inputSource": "binary",
        "binaryPropertyName": "data",
        "outputType": "VOCALS",
        "quality": "BEST",
        "outputFormat": "MP3",
        "timeoutSeconds": 600,
        "pollIntervalSeconds": 5
      },
      "name": "StemSplit",
      "type": "n8n-nodes-stemsplit.stemSplit",
      "typeVersion": 1,
      "position": [480, 300],
      "credentials": {
        "stemSplitApi": "StemSplit account"
      }
    },
    {
      "parameters": {
        "url": "={{$json.vocalsUrl}}",
        "options": { "response": { "response": { "responseFormat": "file" } } }
      },
      "name": "Download Vocals",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [720, 300]
    }
  ],
  "connections": {
    "Read Binary File": { "main": [[{ "node": "StemSplit", "type": "main", "index": 0 }]] },
    "StemSplit":        { "main": [[{ "node": "Download Vocals", "type": "main", "index": 0 }]] }
  }
}
Enter fullscreen mode Exit fullscreen mode

That's it. The Separate Stems (Wait) operation handles the polling internally — default 5-second interval, 600-second timeout, both configurable. Job fails or times out → the node throws and your workflow's error branch picks it up.


Why this beats raw HTTP + Wait + IF

Before this node, the canonical n8n recipe for any async API looked roughly like this:

  1. HTTP Request → submit job, capture id
  2. Wait → 5 seconds
  3. HTTP Request → poll GET /jobs/{id}
  4. IFstatus === "COMPLETED"? branch out, else loop back to step 2
  5. Set → flatten the response

That works, but it has three problems that hit you in production:

  • Loop-back execution semantics in n8n are subtle. A wait-loop counts as separate executions if you use a sub-workflow trigger, fine if you use the in-node loop, and "depends on your version" if you mix paradigms. You'll debug this once and never want to again.
  • Timeout handling is manual. You have to track wall-clock yourself and break the loop. Easy to get wrong, easy to leave running for hours on a stuck job.
  • The output is a polling response, not a result. You still need a Set node to extract data.stems.vocals, data.stems.drums, etc., and you have to remember which API version returned which shape.

Separate Stems (Wait) collapses all five nodes into one. Timeout and poll interval are typed parameters. Output fields are flat — vocalsUrl, drumsUrl, bassUrl, vocalsExpiresAt, etc. — so downstream nodes can use n8n expressions directly: {{$json.vocalsUrl}}.

If you've already built the HTTP Request + Wait + IF version and you're worried about migration: the underlying API is identical. The node just calls POST /jobs and GET /jobs/{id} on your behalf, with the same Authorization: Bearer header, against the same https://stemsplit.io/api/v1 base URL. Anything you've logged or persisted by id keeps working.


Fire-and-forget for batch jobs

For batch processing where you don't want to block per-item, use Separate Stems (no wait) plus Get Job later:

[Schedule Trigger: every 1 min]
  → [Postgres: SELECT pending_song_urls]
  → [StemSplit: Separate Stems, Input: URL]   ← returns job ID instantly
  → [Postgres: UPDATE songs SET job_id, status='SUBMITTED']

# separate workflow
[Schedule Trigger: every 30s]
  → [Postgres: SELECT job_id WHERE status='SUBMITTED']
  → [StemSplit: Get Job]
  → [IF: status === 'COMPLETED']
  → [HTTP Request: download URLs to S3]
  → [Postgres: UPDATE songs SET status='DONE']
Enter fullscreen mode Exit fullscreen mode

Same polling shape as before — but the n8n state is now in your database, not in a long-running workflow execution, which is the right boundary for anything that runs longer than a couple of minutes.


Supported formats and limits

Spec Value
Input formats mp3, wav, flac, m4a, ogg, webm, aac, wma
Max file size 50 MB per submission
Output formats mp3, wav, flac
Quality tiers Fast, Balanced, Best
Presigned URL TTL 1 hour after completion
File retention 14 days
Credit model 1 credit = 1 second of audio, deducted at submission

If you submit a job and your balance is short, the node throws a 402 INSUFFICIENT_CREDITS error — wire that to a Slack or email alert and you'll never silently drop a batch.


Where this fits in the StemSplit ecosystem

  • The community node (this post) — for n8n workflow builders
  • The REST API directly — if you'd rather call it from Python, Node, Go, or anything else
  • The hosted StemSplit vocal remover — same model, browser UI, no plumbing
  • Self-hosted via HT-Demucs FT ONNX — if you want the model on your own GPU and zero API dependency

All four ship the same HTDemucs FT weights end-to-end, so the separation quality you get in n8n matches what you'd get running the model locally.


Wrapping up

Source code, issue tracker, and version history live at github.com/StemSplit/n8n-stemsplit. The package is MIT-licensed and published with provenance, so it should pass any community node policy that checks for it.

If you build something interesting with the node — a Telegram-to-stems bot, a YouTube karaoke pipeline, a Spotify-trigger acapella extractor — drop a link in the comments. I'll add the best ones to the README's example workflows section.

Bug reports and feature requests are welcome on the GitHub repo. Pull requests are even more welcome.

Top comments (0)