Skip to main content

Node.js SDK

~10 min

Separate audio stems from Node.js / TypeScript using @stemsplit/sdk

Prerequisites

  • Node.js 20 or newer
  • StemSplit account with API key
  • Credit balance for processing
1

Install

Install from npm. Zero runtime dependencies — uses Node.js built-ins only.

Install
npm install @stemsplit/sdk
2

Quick Start

Create a client, submit an audio URL, wait for completion, and download the stems — all in a few lines.

Quick start
import { StemSplit } from '@stemsplit/sdk';

const client = new StemSplit(); // reads STEMSPLIT_API_KEY

const job = await client.jobs.create({
  sourceUrl: 'https://example.com/song.mp3',
  outputType: 'FOUR_STEMS',
  quality: 'BEST',
});

const done = await job.waitForCompletion();
const { files } = await done.downloadAll('./stems/');
// stems/vocals.mp3  stems/drums.mp3  stems/bass.mp3  stems/other.mp3

console.log(files);

Output URLs expire after 1 hour. Call downloadAll() promptly or store the files yourself.

3

Upload a Local File

Pass a file path, Buffer, Uint8Array, or Blob as audio. The SDK gets a presigned URL, uploads your file, and creates the job automatically.

Upload a local file
const job = await client.jobs.create({
  audio: './song.mp3',       // file path — SDK reads and uploads
  outputType: 'BOTH',
  quality: 'BALANCED',
  outputFormat: 'WAV',
});

const done = await job.waitForCompletion();
await done.downloadAll('./out/');
4

Polling and Download

waitForCompletion() — built-in polling helperRecommended
const done = await job.waitForCompletion({
  timeoutMs: 600_000,     // default: 10 minutes
  pollIntervalMs: 5_000,  // default: 5 seconds
});

// Throws JobFailedError or JobExpiredError on bad outcomes
const { files } = await done.downloadAll('./stems/');
// { vocals: 'stems/vocals.mp3', drums: 'stems/drums.mp3', ... }
Manual polling — full control
let current = await client.jobs.get(job.id);

while (current.status !== 'COMPLETED') {
  if (current.status === 'FAILED') {
    throw new Error(current.errorMessage ?? 'Job failed');
  }
  await new Promise(r => setTimeout(r, 5000));
  current = await client.jobs.get(job.id);
}

console.log(current.outputs?.vocals?.url);
5

YouTube Jobs

Separate stems from any public YouTube URL — no download step required.

const job = await client.youtubeJobs.create(
  'https://youtube.com/watch?v=dQw4w9WgXcQ',
);

const done = await client.youtubeJobs.waitForCompletion(job.id);

console.log(done.videoTitle);
console.log(done.outputs?.vocals?.url);
console.log(done.outputs?.instrumental?.url);
6

Webhooks

Register a webhook in the dashboard, save the secret, then verify deliveries with one call.

Verify a webhook
import express from 'express';
import { webhooks } from '@stemsplit/sdk';

const app = express();
const SECRET = process.env.STEMSPLIT_WEBHOOK_SECRET!;

app.post(
  '/stemsplit-webhook',
  express.raw({ type: '*/*' }),
  (req, res) => {
    try {
      const event = webhooks.verifyAndParse(
        req.body,
        req.headers['x-webhook-signature'] as string,
        SECRET,
      );

      if (event.event === 'job.completed') {
        console.log('Stems ready:', event.data.outputs);
      }

      res.sendStatus(200);
    } catch {
      res.sendStatus(401);
    }
  },
);

Error Handling

import {
  StemSplit,
  AuthenticationError,
  InsufficientCreditsError,
  JobFailedError,
  RateLimitError,
} from '@stemsplit/sdk';

const client = new StemSplit();

try {
  const job = await client.jobs.create({ sourceUrl: 'https://example.com/song.mp3' });
  const done = await job.waitForCompletion();
} catch (err) {
  if (err instanceof AuthenticationError) {
    console.error('Check your STEMSPLIT_API_KEY:', err.message);
  } else if (err instanceof InsufficientCreditsError) {
    console.error(`Need ${err.requiredSeconds}s of credit. Buy more at ${err.purchaseUrl}`);
  } else if (err instanceof RateLimitError) {
    console.error(`Rate limited. Retry after ${err.retryAfter}s`);
  } else if (err instanceof JobFailedError) {
    console.error(`Job ${err.jobId} failed: ${err.errorMessage}`);
  }
}
ExceptionHTTP / When
AuthenticationError401 — invalid or missing API key
InsufficientCreditsError402 — exposes .requiredSeconds, .purchaseUrl
RateLimitError429 — exposes .retryAfter (seconds)
JobFailedErrorLogical — thrown by waitForCompletion()
JobExpiredErrorLogical — thrown by waitForCompletion()
SignatureVerificationErrorWebhook HMAC mismatch
NetworkErrorTransport-level (DNS, timeout, …)

Output Types & Quality

outputTypeStems returned
VOCALSIsolated vocals track only
INSTRUMENTALBacking track (vocals removed)
BOTHVocals + instrumental
FOUR_STEMSVocals, drums, bass, other
SIX_STEMSVocals, drums, bass, guitar, piano, other

Requirements

Node.js20 or newer
TypeScript4.9+ (optional)
Runtime dependenciesNone — Node.js built-ins only
Installnpm install @stemsplit/sdk
Versionv0.1.0

Tips

  • • Store your API key in STEMSPLIT_API_KEY — the SDK reads it automatically
  • • Output download URLs expire after 1 hour — call downloadAll() promptly
  • • Maximum file size is 100 MB; maximum duration is 60 minutes
  • • Use SIX_STEMS with BEST quality for guitar and piano isolation
  • • The SDK automatically retries transient errors and 429s with exponential backoff