Skip to main content

Getting Started

Everything you need to start using the StemSplit API. Build vocal removers, stem splitters, and karaoke apps with our AI-powered API.

Quick Start

Separate stems from any audio file in 3 simple steps:

  1. 1. Get an API key from Settings → API Keys
  2. 2. Create a job with your audio URL
  3. 3. Poll for results or receive webhooks
curl -X POST https://stemsplit.io/api/v1/jobs \
  -H "Authorization: Bearer sk_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{"sourceUrl": "https://example.com/song.mp3", "outputType": "BOTH"}'

1. Authentication

All API requests require authentication using a Bearer token. Get your API key from Settings → API Keys in your dashboard. New users get 10 free minutes to try the API.

HTTP Header
Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxx

Keep your API key secret

Never expose your API key in client-side code or public repositories. Rotate keys if compromised.

2. Upload a File

You have two options for providing audio: presigned upload for local files or source URL for files already hosted online.

Option A: Presigned Upload

For local files, first request an upload URL:

Request upload URL
curl -X POST https://stemsplit.io/api/v1/upload \
  -H "Authorization: Bearer sk_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{"filename": "my-song.mp3"}'
Response
{
  "uploadUrl": "https://storage.example.com/presigned-url...",
  "uploadKey": "uploads/api_xxx/input.mp3",
  "expiresAt": "2024-01-05T12:15:00Z",
  "maxFileSizeMb": 50
}

Then PUT your file to the presigned URL:

Upload file
curl -X PUT "https://storage.example.com/presigned-url..." \
  -H "Content-Type: audio/mpeg" \
  --data-binary @my-song.mp3

Option B: Source URL

For files already hosted online, skip the upload step and provide a sourceUrl directly when creating the job.

3. Create a Job

Create a stem separation job with your upload key or source URL. See pricing for credit costs.

Create job
curl -X POST https://stemsplit.io/api/v1/jobs \
  -H "Authorization: Bearer sk_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "uploadKey": "uploads/api_xxx/input.mp3",
    "outputType": "BOTH",
    "quality": "BEST",
    "outputFormat": "MP3"
  }'
Response
{
  "id": "clxxx123...",
  "status": "PENDING",
  "progress": 0,
  "creditsRequired": 180,
  "input": {
    "fileName": "my-song.mp3",
    "durationSeconds": 180,
    "fileSizeBytes": 4500000
  },
  "options": {
    "outputType": "BOTH",
    "quality": "BEST",
    "outputFormat": "MP3"
  }
}

Job Options

ParameterValuesDescription
outputType
VOCALSINSTRUMENTALBOTHFOUR_STEMSSIX_STEMS
What stems to extract
quality
FASTBALANCEDBEST
Processing quality (6-stem requires BEST)
outputFormat
MP3WAVFLAC
Output file format

4. Get Results

Processing time: Typically 1-3 minutes depending on audio length and quality setting.

Poll the job status until completion, or use webhooks for async notification. For automation platforms, see our n8n and Zapier guides.

Poll job status
curl https://stemsplit.io/api/v1/jobs/clxxx123 \
  -H "Authorization: Bearer sk_live_xxx"
Completed job response
{
  "id": "clxxx123...",
  "status": "COMPLETED",
  "progress": 100,
  "audioMetadata": {
    "bpm": 120.0,
    "key": "Gm"
  },
  "outputs": {
    "vocals": {
      "url": "https://storage.example.com/vocals.mp3?signature=...",
      "expiresAt": "2024-01-05T13:00:00Z"
    },
    "instrumental": {
      "url": "https://storage.example.com/instrumental.mp3?signature=...",
      "expiresAt": "2024-01-05T13:00:00Z"
    }
  },
  "creditsCharged": 180
}

Download URLs expire

URLs are valid for 1 hour. Call the endpoint again to get fresh URLs.

5. YouTube Jobs

Extract vocals and instrumental directly from YouTube videos. No need to download first. Perfect for building karaoke apps or content creator tools.

Create YouTube job
curl -X POST https://stemsplit.io/api/v1/youtube-jobs \
  -H "Authorization: Bearer sk_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{"youtubeUrl": "https://youtube.com/watch?v=dQw4w9WgXcQ"}'
Response
{
  "id": "clxxx123...",
  "status": "PENDING",
  "videoId": "dQw4w9WgXcQ",
  "videoTitle": "Rick Astley - Never Gonna Give You Up",
  "videoDuration": 213,
  "videoThumbnail": "https://i.ytimg.com/vi/...",
  "channelName": "Rick Astley",
  "creditsRequired": 213,
  "outputs": ["vocals", "instrumental"],
  "createdAt": "2024-01-05T12:00:00Z"
}

Get YouTube Job Status

Poll the job status or use webhooks (same as regular jobs):

GET /api/v1/youtube-jobs/:id
{
  "id": "clxxx123...",
  "status": "COMPLETED",
  "progress": 100,
  "videoTitle": "Rick Astley - Never Gonna Give You Up",
  "audioMetadata": {
    "bpm": 113.0,
    "key": "Am"
  },
  "outputs": {
    "fullAudio": {
      "url": "https://storage.example.com/full.mp3?sig=...",
      "expiresAt": "2024-01-05T13:00:00Z"
    },
    "vocals": {
      "url": "https://storage.example.com/vocals.mp3?sig=...",
      "expiresAt": "2024-01-05T13:00:00Z"
    },
    "instrumental": {
      "url": "https://storage.example.com/instrumental.mp3?sig=...",
      "expiresAt": "2024-01-05T13:00:00Z"
    }
  }
}

YouTube Terms

Only process videos you have rights to. YouTube jobs output vocals + instrumental as MP3. Maximum video duration is 60 minutes.

6. Webhooks

Instead of polling, register a webhook to receive notifications when jobs complete or fail. Webhooks work great with n8n, Zapier, and Make for building automated workflows.

Register a Webhook

Create webhook
curl -X POST https://stemsplit.io/api/v1/webhooks \
  -H "Authorization: Bearer sk_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-server.com/webhook",
    "events": ["job.completed", "job.failed"]
  }'

Webhook Payload

When a job completes, we'll POST to your URL:

Webhook payload
{
  "event": "job.completed",
  "timestamp": "2024-01-05T12:30:00Z",
  "data": {
    "jobId": "clxxx123...",
    "status": "COMPLETED",
    "audioMetadata": {
      "bpm": 120.0,
      "key": "Gm"
    },
    "outputs": {
      "vocals": { "url": "https://...", "expiresAt": "..." },
      "instrumental": { "url": "https://...", "expiresAt": "..." }
    },
    "creditsCharged": 180
  }
}

Verify Webhook Signatures

Webhooks include an HMAC-SHA256 signature in the X-Webhook-Signature header. Always verify it:

Verify signature
import hmac
import hashlib

def verify_webhook(payload: bytes, signature: str, secret: str) -> bool:
    """Verify webhook signature"""
    expected = "sha256=" + hmac.new(
        secret.encode(),
        payload,
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, signature)

# In your webhook handler:
@app.post("/webhook")
def handle_webhook(request):
    signature = request.headers.get("X-Webhook-Signature")
    if not verify_webhook(request.body, signature, WEBHOOK_SECRET):
        return {"error": "Invalid signature"}, 401
    
    data = request.json()
    if data["event"] == "job.completed":
        # Process completed job
        print(f"Job {data['data']['jobId']} completed!")

7. Limits & Errors

Limits

Jobs are charged based on audio duration. See pricing for details. New users get 10 free minutes.

LimitValue
Maximum file size50 MB
Maximum audio/video duration60 minutes
Minimum audio duration5 seconds
Rate limit60 requests/minute
API keys per account5
Webhooks per account5

Error Codes

All errors return a consistent format:

Error response
{
  "error": {
    "code": "INSUFFICIENT_CREDITS",
    "message": "Not enough credits for this job.",
    "requiredSeconds": 180
  }
}
CodeHTTPDescription
MISSING_API_KEY401No Authorization header provided
INVALID_API_KEY401API key doesn't exist or is invalid
API_KEY_REVOKED403API key has been revoked
INSUFFICIENT_CREDITS402Not enough credits — buy more at /pricing
RATE_LIMIT_EXCEEDED429Too many requests, try again later
FILE_TOO_LARGE400File exceeds 50MB limit
AUDIO_TOO_LONG400Audio exceeds 60 minute limit
AUDIO_TOO_SHORT400Audio is under 10 seconds
UNSUPPORTED_FORMAT400File format not supported
JOB_NOT_FOUND404Job doesn't exist or you don't own it