Como baixar e separar stems de músicas do SoundCloud com Python (2026)
O SoundCloud tem mais de 300 milhões de faixas. Se você quer separar vocais, instrumentais, bateria ou baixo de qualquer uma delas com Python, esse guia cobre o processo completo: download com yt-dlp e separação de stems via API.
O que você vai aprender
- ✅ Como baixar áudio do SoundCloud com
yt-dlpem um comando Python - ✅ Como enviar o arquivo para a API de separação de stems e obter
job_id - ✅ Como implementar o loop de polling assíncrono até os stems ficarem prontos
- ✅ Como baixar vocais, bateria, baixo e instrumental como arquivos WAV separados
- ✅ Como montar um pipeline batch para processar várias faixas
Pré-requisitos
pip install yt-dlp requests python-dotenv
Você também precisa de uma chave de API do StemSplit (plano gratuito: 5 minutos de processamento, sem cartão de crédito): stemsplit.io/pt-BR/soundcloud-stem-splitter
Por que o SoundCloud para stems?
Resposta curta: porque é onde a maioria dos produtores independentes publica demos, remixes e acapellas — e muitas faixas não estão no Spotify ou no YouTube.
O caso de uso mais comum que eu vejo em comunidades BR de produção musical:
- DJs querem separar o stem de bateria de uma faixa exclusiva do SoundCloud para remixtear
- Cantores de cover precisam do instrumental limpo de uma versão demo
- Produtores querem estudar o groove de baixo de uma faixa de outro produtor
- Criadores de karaoke precisam remover vocais de músicas que só existem no SoundCloud
O yt-dlp suporta SoundCloud nativamente (incluindo faixas em playlists e reposts), então o pipeline fica bem direto.
Passo 1 — Baixar o áudio com yt-dlp
import subprocess
import os
from pathlib import Path
def download_soundcloud(url: str, output_dir: str = "downloads") -> Path:
"""
Baixa uma faixa do SoundCloud como MP3 de alta qualidade.
Retorna o caminho do arquivo baixado.
"""
Path(output_dir).mkdir(exist_ok=True)
# yt-dlp detecta o título automaticamente e usa como nome do arquivo
result = subprocess.run(
[
"yt-dlp",
"--extract-audio",
"--audio-format", "mp3",
"--audio-quality", "0", # melhor qualidade disponível
"--output", f"{output_dir}/%(title)s.%(ext)s",
"--no-playlist", # só a faixa, não a playlist
url,
],
capture_output=True,
text=True,
check=True,
)
# Pega o nome do arquivo gerado da saída do yt-dlp
for line in result.stdout.splitlines():
if "[ExtractAudio] Destination:" in line:
return Path(line.split("Destination:")[1].strip())
# Fallback: pega o MP3 mais recente na pasta
mp3_files = sorted(Path(output_dir).glob("*.mp3"), key=os.path.getmtime)
if not mp3_files:
raise FileNotFoundError("yt-dlp não criou nenhum arquivo MP3")
return mp3_files[-1]
# Uso
audio_path = download_soundcloud("https://soundcloud.com/artista/nome-da-faixa")
print(f"Baixado: {audio_path}")
O yt-dlp também aceita URLs de playlists do SoundCloud — basta remover --no-playlist para baixar todas as faixas.
Passo 2 — Enviar para a API e obter o job_id
Resposta curta: a API aceita upload multipart e retorna um job_id imediatamente; o processamento acontece de forma assíncrona no servidor.
import requests
from pathlib import Path
API_BASE = "https://api.stemsplit.io/v1"
API_KEY = "sua_chave_aqui" # ou use python-dotenv
def submit_job(audio_path: Path) -> str:
"""Envia o arquivo para a API e retorna o job_id."""
with open(audio_path, "rb") as f:
response = requests.post(
f"{API_BASE}/jobs",
headers={"Authorization": f"Bearer {API_KEY}"},
files={"file": (audio_path.name, f, "audio/mpeg")},
timeout=60,
)
response.raise_for_status()
job_id = response.json()["job_id"]
print(f"Job enviado: {job_id}")
return job_id
O limite de tamanho de arquivo é 100 MB. Faixas do SoundCloud em MP3 de alta qualidade ficam em torno de 8–15 MB para uma faixa de 3–5 minutos — bem dentro do limite.
Passo 3 — Polling até o job completar
Resposta curta: poll a cada 5 segundos em GET /jobs/{job_id} até o status mudar para "done".
O HTDemucs (modelo que roda nos bastidores) leva em torno de 35–45 segundos para uma faixa de 3 minutos em GPU dedicada.
import time
POLL_INTERVAL = 5 # segundos entre cada verificação
POLL_TIMEOUT = 300 # 5 minutos máximo
def poll_job(job_id: str) -> dict:
"""Aguarda o job completar e retorna o dict de stems."""
deadline = time.time() + POLL_TIMEOUT
headers = {"Authorization": f"Bearer {API_KEY}"}
while time.time() < deadline:
resp = requests.get(
f"{API_BASE}/jobs/{job_id}",
headers=headers,
timeout=15,
)
resp.raise_for_status()
data = resp.json()
status = data.get("status")
print(f" Status: {status}")
if status == "done":
return data["stems"]
if status == "failed":
raise RuntimeError(f"Job falhou: {data.get('error')}")
time.sleep(POLL_INTERVAL)
raise TimeoutError("Job excedeu o tempo limite de 5 minutos")
Passo 4 — Baixar os stems
def download_stems(stems: dict, output_dir: str = "stems") -> dict[str, Path]:
"""
Baixa cada stem como arquivo WAV.
Retorna dict {nome_do_stem: caminho_do_arquivo}.
"""
Path(output_dir).mkdir(exist_ok=True)
downloaded = {}
for stem_name, url in stems.items():
dest = Path(output_dir) / f"{stem_name}.wav"
print(f" Baixando {stem_name}…")
resp = requests.get(url, timeout=60, stream=True)
resp.raise_for_status()
with open(dest, "wb") as f:
for chunk in resp.iter_content(chunk_size=8192):
f.write(chunk)
downloaded[stem_name] = dest
return downloaded
Os stems disponíveis são: vocals, drums, bass, other (outros instrumentos).
Pipeline completo
from pathlib import Path
from dotenv import load_dotenv
import os
load_dotenv()
API_KEY = os.environ["STEMSPLIT_API_KEY"]
def process_soundcloud_track(url: str, track_name: str) -> dict[str, Path]:
"""Pipeline completo: download → separação → stems."""
print(f"\n[{track_name}]")
print("→ Baixando do SoundCloud…")
audio_path = download_soundcloud(url)
print("→ Enviando para a API…")
job_id = submit_job(audio_path)
print("→ Aguardando processamento…")
stems_urls = poll_job(job_id)
print("→ Baixando stems…")
stem_paths = download_stems(stems_urls, output_dir=f"stems/{track_name}")
print(f"✓ Pronto! {len(stem_paths)} stems salvos em stems/{track_name}/")
return stem_paths
# Exemplo de uso
if __name__ == "__main__":
tracks = [
("https://soundcloud.com/artista/faixa-1", "faixa-1"),
("https://soundcloud.com/artista/faixa-2", "faixa-2"),
]
for url, name in tracks:
try:
stems = process_soundcloud_track(url, name)
for stem, path in stems.items():
print(f" {stem}: {path}")
except Exception as e:
print(f" Erro em {name}: {e}")
Casos de uso práticos
Karaoke a partir de faixas exclusivas do SoundCloud
Muitos cantores publicam versões demo no SoundCloud antes de lançar no Spotify. Com esse pipeline você extrai o instrumental limpo em segundos — sem depender de nenhuma ferramenta online com limite de arquivo.
Estudo de produção
Isolar o stem de bateria ou baixo de uma faixa de referência é a forma mais rápida de entender o groove. Com Python você pode automatizar isso para uma lista inteira de referências de uma vez.
Criação de conteúdo
Se você cria conteúdos com a música de um produtor independente do SoundCloud, ter o stem de vocais e o instrumental separadamente facilita muito a edição — especialmente para Reels e TikTok.
Considerações sobre direitos autorais
Usar esse pipeline em faixas protegidas por direitos autorais sem autorização viola os Termos de Serviço do SoundCloud. Use apenas em:
- Faixas com licença Creative Commons
- Faixas de sua própria autoria
- Com permissão explícita do artista
Conclusão
O pipeline inteiro tem menos de 100 linhas de Python: yt-dlp resolve o download, a API cuida do processamento pesado com HTDemucs, e você só precisa implementar o polling e o download dos stems.
Para processar faixas sem escrever nenhuma linha de código — direto pelo navegador — o separador de stems do SoundCloud do StemSplit faz exatamente isso com a mesma qualidade de separação.
Se você quiser ir mais fundo, nosso primeiro artigo aqui no TabNews cobre como separar stems com Demucs localmente e via API — incluindo benchmarks de SDR e comparação Demucs vs Spleeter.
Dúvidas ou sugestões? Deixe nos comentários.