Stem splitting, in Python.
Ship stem separation from your Python service in twelve lines — no GPU, no model download, no FFmpeg. Typed client on PyPI, FastAPI and Flask webhook handlers on this page, the same htdemucs_ft model you already trust.
Install
Authenticate
Mint a key on the developer settings page, then export it as AISTEMSPLITTER_API_KEY so the client picks it up automatically. Pass api_key= to the constructor only when you need a key per request — env var is the recommended default.
import os
from aistemsplitter import AiStemSplitter
client = AiStemSplitter(api_key=os.environ["AISTEMSPLITTER_API_KEY"])Hello world
Twelve lines from imports to four stems on disk. Submits a job to htdemucs_ft, polls until done, then writes vocals.wav, drums.wav, bass.wav, and other.wav next to your script. Copy, paste, run.
import os
import requests
from aistemsplitter import AiStemSplitter
client = AiStemSplitter(api_key=os.environ["AISTEMSPLITTER_API_KEY"])
# 1. Submit a split job
job = client.create_split(
input={"type": "direct_url", "url": "https://example.com/song.mp3"},
stem_model="htdemucs_ft",
)
# 2. Wait until completion (polls under the hood)
result = client.wait_for_split(job.id)
# 3. Download all six stems to disk
for name, url in result.stems.items():
with requests.get(url, stream=True) as r:
with open(f"./{name}.wav", "wb") as f:
for chunk in r.iter_content(chunk_size=8192):
f.write(chunk)Methods
The full SDK surface is four typed methods you can scan in under a minute — submit, get, wait, download. Each one mirrors one REST endpoint, so you can drop down to raw HTTP via /developers/api whenever you outgrow the typed wrapper. Models exposed: htdemucs_ft, htdemucs, htdemucs_6s. Billed at $0.08–$0.14 per minute against credit packs that never expire.
Submit a new split job; returns a job id and queued status.
Fetch the current status of a split job.
Poll until the job succeeds, fails, or the timeout elapses.
Paginated list of recent split jobs for the API key.
Get a pre-signed PUT URL for direct browser/server uploads.
Verify the HMAC-SHA256 signature on an incoming webhook payload.
Webhooks
Skip the polling loop in production. Set webhook_url on submit, then verify the HMAC signature on the incoming POST and read presigned stem URLs straight from the body. FastAPI and Flask handlers on the same page.
import os
from fastapi import FastAPI, Request, HTTPException
from aistemsplitter import AiStemSplitter
app = FastAPI()
client = AiStemSplitter(api_key=os.environ["AISTEMSPLITTER_API_KEY"])
@app.post("/webhooks/aistemsplitter")
async def handle_webhook(request: Request):
raw = await request.body()
try:
event = client.verify_webhook(request.headers, raw)
except Exception:
raise HTTPException(status_code=400, detail="invalid signature")
if event.type == "split.succeeded":
# event.data["stems"] -> six URLs
pass
elif event.type == "split.failed":
# event.data["error"] -> { code, message }
pass
return {"ok": True}FAQ
Should I use the sync client or the async one?
Default to sync (StemSplitter) — that's what the 12-line hello world uses, and it's what fits naturally inside a Django view or a Celery task. Switch to AsyncStemSplitter when you're already on FastAPI, Starlette, or asyncio in a notebook: the method names match (await client.submit, await client.wait, await client.download), so swapping costs one import + one await per call.
Does it work with FastAPI and Flask?
Yes — both are first-class. The Webhooks section above ships runnable handlers for each: FastAPI uses async def webhook(request: Request) with verify_signature; Flask uses a sync @app.post route. Both verify HMAC-SHA256 with one import (from aistemsplitter import verify_signature) and unpack presigned stem URLs from the JSON body without extra parsing.
How do I install in a virtualenv or Poetry project?
Standard tooling: `python -m venv .venv && source .venv/bin/activate && pip install aistemsplitter` for venv; `poetry add aistemsplitter` for Poetry; `uv add aistemsplitter` for uv. The package is pure-Python wheels (no native compilation), so installation completes in seconds on every platform — no pyenv shim, no FFmpeg pre-step, no GPU driver.
Will hosted output match my local htdemucs_ft quality?
Yes. We expose the same model file the open-source community trusts — htdemucs_ft, plus htdemucs and htdemucs_6s — so output is bit-comparable to a local Demucs run with the same model + same input, modulo our managed inference settings (batch size, precision). Pass model='htdemucs_6s' on submit when you need guitar and piano stems.
What if I outgrow the typed SDK and need raw HTTP?
Drop straight to /developers/api. The SDK is a thin typed wrapper over the same REST endpoints the curl quickstart calls, so the auth header (Authorization: Bearer ast_live_…), the webhook signature scheme (HMAC-SHA256 in aistemsplitter-signature), and the response shapes are identical. You can mix-and-match — call submit via the SDK, fetch status via raw httpx for a custom retry policy.
How do I handle very long audio without timing out?
Two strategies. (1) Use webhooks — set webhook_url on submit and skip the polling loop entirely; the API posts results when the job finishes regardless of duration. (2) For files larger than 50 MB, call client.presign_upload() to get a direct-to-storage URL, upload via httpx multipart, then submit with the returned audio_url instead of streaming through our gateway.
Next steps
Ship stem separation before the sprint ends.
No card to mint a key. Free tier covers your first 10 minutes; the rest runs on credit packs that never expire — $0.08–$0.14 per minute depending on volume.