Node.js SDK
~10 minSeparate 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/sdk2
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}`);
}
}| Exception | HTTP / When |
|---|---|
| AuthenticationError | 401 — invalid or missing API key |
| InsufficientCreditsError | 402 — exposes .requiredSeconds, .purchaseUrl |
| RateLimitError | 429 — exposes .retryAfter (seconds) |
| JobFailedError | Logical — thrown by waitForCompletion() |
| JobExpiredError | Logical — thrown by waitForCompletion() |
| SignatureVerificationError | Webhook HMAC mismatch |
| NetworkError | Transport-level (DNS, timeout, …) |
Output Types & Quality
| outputType | Stems returned |
|---|---|
VOCALS | Isolated vocals track only |
INSTRUMENTAL | Backing track (vocals removed) |
BOTH | Vocals + instrumental |
FOUR_STEMS | Vocals, drums, bass, other |
SIX_STEMS | Vocals, drums, bass, guitar, piano, other |
Requirements
| Node.js | 20 or newer |
| TypeScript | 4.9+ (optional) |
| Runtime dependencies | None — Node.js built-ins only |
| Install | npm install @stemsplit/sdk |
| Version | v0.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_STEMSwithBESTquality for guitar and piano isolation - • The SDK automatically retries transient errors and 429s with exponential backoff