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. Get an API key from Settings → API Keys
- 2. Create a job with your audio URL
- 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"}'Developer Resources
Integration Guides
View allUse Case Guides
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.
Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxKeep 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:
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"}'{
"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:
curl -X PUT "https://storage.example.com/presigned-url..." \
-H "Content-Type: audio/mpeg" \
--data-binary @my-song.mp3Option 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.
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"
}'{
"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
| Parameter | Values | Description |
|---|---|---|
outputType | VOCALSINSTRUMENTALBOTHFOUR_STEMSSIX_STEMS | What stems to extract |
quality | FASTBALANCEDBEST | Processing quality (6-stem requires BEST) |
outputFormat | MP3WAVFLAC | Output file format |
Output Types Explained
VOCALSJust the vocals (acapella)INSTRUMENTALBacking track (no vocals)BOTHVocals + Instrumental (karaoke)FOUR_STEMSVocals, Drums, Bass, OtherSIX_STEMSVocals, Drums, Bass, Other, Piano, Guitar — ideal for DJs4. 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.
curl https://stemsplit.io/api/v1/jobs/clxxx123 \
-H "Authorization: Bearer sk_live_xxx"{
"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.
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"}'{
"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):
{
"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
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:
{
"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:
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.
| Limit | Value |
|---|---|
| Maximum file size | 50 MB |
| Maximum audio/video duration | 60 minutes |
| Minimum audio duration | 5 seconds |
| Rate limit | 60 requests/minute |
| API keys per account | 5 |
| Webhooks per account | 5 |
Error Codes
All errors return a consistent format:
{
"error": {
"code": "INSUFFICIENT_CREDITS",
"message": "Not enough credits for this job.",
"requiredSeconds": 180
}
}| Code | HTTP | Description |
|---|---|---|
MISSING_API_KEY | 401 | No Authorization header provided |
INVALID_API_KEY | 401 | API key doesn't exist or is invalid |
API_KEY_REVOKED | 403 | API key has been revoked |
INSUFFICIENT_CREDITS | 402 | Not enough credits — buy more at /pricing |
RATE_LIMIT_EXCEEDED | 429 | Too many requests, try again later |
FILE_TOO_LARGE | 400 | File exceeds 50MB limit |
AUDIO_TOO_LONG | 400 | Audio exceeds 60 minute limit |
AUDIO_TOO_SHORT | 400 | Audio is under 10 seconds |
UNSUPPORTED_FORMAT | 400 | File format not supported |
JOB_NOT_FOUND | 404 | Job doesn't exist or you don't own it |