You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
SoulSync/Support/API.md

8.3 KiB

SoulSync REST API

SoulSync includes a full REST API at /api/v1/ that lets you control everything from external apps, scripts, Discord bots, Home Assistant, or anything that can make HTTP requests.

Quick Start

1. Generate an API Key

Go to Settings in the SoulSync web UI and find the SoulSync API section. Click Generate API Key, give it a label, and copy the key immediately — it's only shown once.

Alternatively, if no keys exist yet, use the bootstrap endpoint:

curl -X POST http://localhost:8008/api/v1/api-keys/bootstrap \
  -H "Content-Type: application/json" \
  -d '{"label": "My First Key"}'

2. Make Requests

Pass your key via the Authorization header:

curl -H "Authorization: Bearer sk_your_key_here" \
  http://localhost:8008/api/v1/system/status

Or as a query parameter:

http://localhost:8008/api/v1/system/status?api_key=sk_your_key_here

3. Response Format

Every response follows this format:

{
  "success": true,
  "data": { ... },
  "error": null,
  "pagination": null
}

Errors:

{
  "success": false,
  "data": null,
  "error": {
    "code": "NOT_FOUND",
    "message": "Artist 999 not found."
  }
}

Authentication

All /api/v1/ endpoints require an API key (except the bootstrap endpoint).

Method Details
Header Authorization: Bearer sk_...
Query ?api_key=sk_...

Keys are generated as sk_ followed by a random token. Only the SHA-256 hash is stored — the raw key is shown once at creation.

Error Codes

Status Code Meaning
401 AUTH_REQUIRED No API key provided
403 INVALID_KEY API key is wrong or revoked

Rate Limiting

Requests are rate-limited per IP address:

Endpoint Type Limit
Read (GET) 60/min
Search (POST /search/*) 20/min
Write (POST/DELETE/PATCH) 30/min
Downloads (POST /downloads) 10/min
System polling (GET /system/*) 120/min

Exceeding the limit returns 429 RATE_LIMITED.


Endpoints

System

GET /api/v1/system/status

Server status, uptime, and service connectivity.

{
  "data": {
    "uptime": "2h 15m 30s",
    "uptime_seconds": 8130,
    "services": {
      "spotify": true,
      "soulseek": true,
      "hydrabase": false
    }
  }
}

GET /api/v1/system/activity

Recent activity feed.

GET /api/v1/system/stats

Combined library and download statistics.


Library

GET /api/v1/library/artists

List library artists with search and pagination.

Param Type Default Description
search string Filter by name
letter string all Filter by first letter (a-z, #)
page int 1 Page number
limit int 50 Items per page (max 200)
watchlist string all all, watched, or unwatched

GET /api/v1/library/artists/<artist_id>

Get artist details with album list.

GET /api/v1/library/artists/<artist_id>/albums

List albums for an artist.

GET /api/v1/library/albums/<album_id>/tracks

List tracks in an album.

GET /api/v1/library/tracks

Search tracks by title and/or artist.

Param Type Description
title string Track title to search
artist string Artist name to search
limit int Max results (default 50, max 200)

GET /api/v1/library/stats

Library statistics (artist/album/track counts, database info).


Search external music sources (Spotify, iTunes, Hydrabase).

POST /api/v1/search/tracks

{
  "query": "Daft Punk Around the World",
  "source": "auto",
  "limit": 20
}

source: "auto" (default — tries Hydrabase, then Spotify, then iTunes), "spotify", or "itunes".

POST /api/v1/search/albums

{
  "query": "Discovery",
  "limit": 10
}

POST /api/v1/search/artists

{
  "query": "Daft Punk",
  "limit": 10
}

Downloads

GET /api/v1/downloads

List active and recent download tasks.

POST /api/v1/downloads/<download_id>/cancel

Cancel a specific download.

{
  "username": "soulseek_username"
}

POST /api/v1/downloads/cancel-all

Cancel all active downloads and clear completed ones.


Wishlist

Tracks that failed to download, queued for retry.

GET /api/v1/wishlist

List wishlist tracks.

Param Type Description
category string singles or albums
page int Page number
limit int Items per page

POST /api/v1/wishlist

Add a track to the wishlist.

{
  "spotify_track_data": { "id": "...", "name": "...", "artists": [...] },
  "failure_reason": "No sources found",
  "source_type": "api"
}

DELETE /api/v1/wishlist/<track_id>

Remove a track from the wishlist.

POST /api/v1/wishlist/process

Trigger wishlist download processing.


Watchlist

Artists being monitored for new releases.

GET /api/v1/watchlist

List all watched artists.

POST /api/v1/watchlist

Add an artist to the watchlist.

{
  "artist_id": "4tZwfgrHOc3mvqYlEYSvnL",
  "artist_name": "Daft Punk"
}

DELETE /api/v1/watchlist/<artist_id>

Remove an artist from the watchlist.

POST /api/v1/watchlist/scan

Trigger a watchlist scan for new releases.


Playlists

GET /api/v1/playlists

List user playlists.

Param Type Default Description
source string spotify spotify or tidal

GET /api/v1/playlists/<playlist_id>

Get playlist details with tracks.

POST /api/v1/playlists/<playlist_id>/sync

Trigger playlist sync/download.

{
  "playlist_name": "My Playlist",
  "tracks": [...]
}

Settings

GET /api/v1/settings

Get current settings (sensitive values like passwords and tokens are redacted).

PATCH /api/v1/settings

Update settings (partial update).

{
  "soulseek.search_timeout": 90,
  "logging.level": "DEBUG"
}

API Key Management

GET /api/v1/api-keys

List all API keys (shows prefix and label only, never the full key).

POST /api/v1/api-keys

Generate a new API key.

{
  "label": "Discord Bot"
}

Response includes the raw key (shown only once):

{
  "data": {
    "key": "sk_a3Bf9x2Kp7...",
    "id": "uuid",
    "label": "Discord Bot",
    "key_prefix": "sk_a3Bf9x2",
    "created_at": "2026-03-03T12:00:00Z"
  }
}

DELETE /api/v1/api-keys/<key_id>

Revoke an API key.

POST /api/v1/api-keys/bootstrap

Generate the first API key when none exist (no auth required). Returns 403 if keys already exist.


Examples

Python

import requests

API_URL = "http://localhost:8008/api/v1"
API_KEY = "sk_your_key_here"

headers = {"Authorization": f"Bearer {API_KEY}"}

# Search for tracks
resp = requests.post(f"{API_URL}/search/tracks",
    headers=headers,
    json={"query": "Daft Punk", "limit": 5})
tracks = resp.json()["data"]["tracks"]

# Add artist to watchlist
requests.post(f"{API_URL}/watchlist",
    headers=headers,
    json={"artist_id": "4tZwfgrHOc3mvqYlEYSvnL", "artist_name": "Daft Punk"})

# Check system status
status = requests.get(f"{API_URL}/system/status", headers=headers).json()
print(f"Uptime: {status['data']['uptime']}")

JavaScript

const API_URL = 'http://localhost:8008/api/v1';
const API_KEY = 'sk_your_key_here';

const headers = {
  'Authorization': `Bearer ${API_KEY}`,
  'Content-Type': 'application/json'
};

// Browse library
const artists = await fetch(`${API_URL}/library/artists?page=1&limit=25`, { headers })
  .then(r => r.json());

// Trigger watchlist scan
await fetch(`${API_URL}/watchlist/scan`, { method: 'POST', headers });

curl

# System status
curl -H "Authorization: Bearer sk_..." http://localhost:8008/api/v1/system/status

# Search tracks
curl -X POST http://localhost:8008/api/v1/search/tracks \
  -H "Authorization: Bearer sk_..." \
  -H "Content-Type: application/json" \
  -d '{"query": "Boards of Canada", "limit": 5}'

# Library artists page 1
curl -H "Authorization: Bearer sk_..." \
  "http://localhost:8008/api/v1/library/artists?page=1&limit=25"