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.
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"}'Pass your key via the Authorization header:
curl -H "Authorization: Bearer sk_your_key_here" \
http://localhost:8008/api/v1/system/statusOr as a query parameter:
http://localhost:8008/api/v1/system/status?api_key=sk_your_key_here
Every response follows this envelope:
{
"success": true,
"data": { ... },
"error": null,
"pagination": null
}Error responses:
{
"success": false,
"data": null,
"error": {
"code": "NOT_FOUND",
"message": "Artist 999 not found."
},
"pagination": null
}Paginated responses include:
{
"pagination": {
"page": 1,
"limit": 50,
"total": 347,
"total_pages": 7,
"has_next": true,
"has_prev": false
}
}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.
| Status | Code | Meaning |
|---|---|---|
| 401 | AUTH_REQUIRED |
No API key provided |
| 403 | INVALID_KEY |
API key is wrong or revoked |
Requests with a valid API key are rate-limited to 600 per minute, counted per key. Requests with no key or an invalid key are rate-limited to 60 per minute per IP address.
Exceeding the limit returns 429 RATE_LIMITED.
These optional parameters work on all endpoints that return entity data:
| Param | Type | Description |
|---|---|---|
fields |
string | Comma-separated list of fields to return (e.g. ?fields=id,name,thumb_url). Omit to return all fields. |
SoulSync supports multiple user profiles. Profile-scoped endpoints (watchlist, wishlist, discovery) accept a profile identifier:
| Method | Details |
|---|---|
| Header | X-Profile-Id: 2 |
| Query | ?profile_id=2 |
If omitted, defaults to profile 1 (admin). Profile scoping applies to: watchlist, wishlist, and discovery endpoints.
SoulSync can act as an MCP server, so an AI app like Claude can search for music, check what you already own, and download things for you.
Go to Settings > Advanced > SoulSync API > MCP for AI Clients and tick Answer MCP requests at /mcp. While it's off, /mcp answers 404.
Add http://<host>:8008/mcp as a remote (streamable HTTP) MCP server and send a SoulSync API key as a Bearer token. For Claude Code:
claude mcp add --transport http soulsync http://localhost:8008/mcp --header "Authorization: Bearer sk_..."Both protocol styles work: 2026-07-28 (no handshake) and 2025-03-26 through 2025-11-25 (initialize first). Requests from a browser page on another site are refused, so a key that leaks into a web page can't be used from there.
Every tool call runs as the matching /api/v1 request with your key, so the usual rules apply: the same scope checks, the same profile, and the same 60 requests a minute.
- An admin key gets all 35 tools.
- A read key only gets the tools that look things up. A write tool still answers
403if one is ever called with a read key. - Settings, API keys, profiles, cancel-all and blocklist clears are never tools, whatever the key.
| Group | Tools |
|---|---|
| Status | get_server_status, get_recent_activity, get_library_stats |
| Your library | search_library_artists, get_library_artist, list_artist_albums, search_library_albums, get_album_tracks, search_library_tracks, recently_added, recently_played |
| Queues | list_wishlist, list_watchlist, list_downloads, list_playlists, get_request_status |
| Discovery | recent_releases, similar_artists |
| Video | video_search, video_library, video_wishlist, video_downloads, video_calendar |
| Getting music | search_music, find_album, request_album, request_track, process_wishlist, remove_from_wishlist, watch_artist, unwatch_artist, scan_watchlist, cancel_download |
| Getting video | video_wishlist_add, video_follow |
To get a whole album, the client calls find_album with "Artist - Album" and then request_album with the id it returns. Every track goes on the wishlist and tracks you already own are skipped. For one track, request_track takes "Artist - Title".
Some request apps only know how to talk to Lidarr. SoulSync can answer as Lidarr, so those apps send their album requests to SoulSync instead. Built for Singarr and MusicSeerr 1.x. MusicSeerr 2.0 and newer dropped Lidarr support, so it can't connect.
Go to Settings > Advanced > SoulSync API > Lidarr Compatible API:
- Tick Answer as Lidarr at /lidarr. While it's off, every
/lidarrroute answers404. - Pick Requests go to profile. Requested albums land on that profile's wishlist.
In the request app, set the Lidarr URL to http://<host>:8008/lidarr and paste a SoulSync admin API key as the Lidarr API key. A read key gets 401. The app talks to /lidarr/api/v1 like it would to a real Lidarr.
- Artist and album lookups come from MusicBrainz.
- When the app asks for an album to be searched (on add, or with an album or artist search), every track of that album goes on the profile's wishlist and downloads like any other wishlist item. Tracks you already own are skipped.
- If the profile can't download on its own, every request waits for approval on the Requests page first.
- The app's "do we have it" view checks your SoulSync library.
- Deleting an artist or album in the app only removes it from SoulSync's Lidarr list. Files on disk are never touched.
All under /lidarr/api/v1: system/status, qualityprofile, metadataprofile, rootfolder, tag, artist (list, lookup, add, edit, editor, delete), album (list, lookup, add, edit, monitor, delete), track, trackfile, command (AlbumSearch, ArtistSearch, RefreshArtist; other commands are accepted and do nothing), queue, history and wanted/missing.
Server status, uptime, and service connectivity.
{
"data": {
"uptime": "2h 15m 30s",
"uptime_seconds": 8130,
"services": {
"spotify": true,
"soulseek": true,
"hydrabase": false
}
}
}Recent activity feed.
{
"data": {
"activities": [
{ "type": "download", "message": "Downloaded Track Name", "timestamp": "..." },
...
]
}
}Combined library and download statistics.
{
"data": {
"library": {
"artists": 1250,
"albums": 4830,
"tracks": 52100
},
"database": {
"size_mb": 145.2,
"last_update": "2026-03-04T09:00:00"
},
"downloads": {
"active": 3
}
}
}List library artists with search, letter filtering, and pagination.
| Param | Type | Default | Description |
|---|---|---|---|
search |
string | Filter by name | |
letter |
string | all |
Filter by first letter (a-z, # for non-alpha) |
page |
int | 1 | Page number |
limit |
int | 50 | Items per page (max 200) |
watchlist |
string | all |
all, watched, or unwatched |
fields |
string | Comma-separated field list |
Response:
{
"data": {
"artists": [
{
"id": "42",
"name": "Radiohead",
"thumb_url": null,
"banner_url": null,
"genres": ["alternative rock", "art rock"],
"summary": null,
"style": null,
"mood": null,
"label": null,
"server_source": null,
"created_at": null,
"updated_at": null,
"musicbrainz_id": "a74b1b7f-71a5-4011-9441-d0b5e4122711",
"spotify_artist_id": "4Z8W4fKeB5YxbusRsdQVPb",
"itunes_artist_id": "657515",
"audiodb_id": "111239",
"deezer_id": "399",
"musicbrainz_match_status": null,
"spotify_match_status": null,
"itunes_match_status": null,
"audiodb_match_status": null,
"deezer_match_status": null,
"musicbrainz_last_attempted": null,
"spotify_last_attempted": null,
"itunes_last_attempted": null,
"audiodb_last_attempted": null,
"deezer_last_attempted": null,
"album_count": 9,
"track_count": 101,
"is_watched": true,
"image_url": "https://..."
}
]
},
"pagination": { "page": 1, "limit": 50, "total": 1250, "total_pages": 25, "has_next": true, "has_prev": false }
}Note: The list endpoint returns a subset of metadata fields. Some fields like
summary,style,mood,label,banner_url, and most*_match_status/*_last_attemptedtimestamps may benullin list view (the Discogs, Amazon, and JioSaavn statuses and timestamps are included). Use the detail endpoint below for the complete record.
Get a single artist by ID with all metadata and their album list.
{
"data": {
"artist": {
"id": "42",
"name": "Radiohead",
"thumb_url": "https://i.scdn.co/image/abc123...",
"banner_url": "https://www.theaudiodb.com/images/media/artist/fanart/...",
"genres": ["alternative rock", "art rock", "experimental"],
"summary": "Radiohead are an English rock band formed in Abingdon...",
"style": "Alternative/Indie",
"mood": "Melancholy",
"label": "XL Recordings",
"server_source": "plex",
"created_at": "2025-12-01T14:30:00",
"updated_at": "2026-02-15T09:12:00",
"musicbrainz_id": "a74b1b7f-71a5-4011-9441-d0b5e4122711",
"spotify_artist_id": "4Z8W4fKeB5YxbusRsdQVPb",
"itunes_artist_id": "657515",
"audiodb_id": "111239",
"deezer_id": "399",
"musicbrainz_match_status": "matched",
"spotify_match_status": "matched",
"itunes_match_status": "matched",
"audiodb_match_status": "matched",
"deezer_match_status": "matched",
"musicbrainz_last_attempted": "2026-01-10T08:00:00",
"spotify_last_attempted": "2026-01-10T08:00:00",
"itunes_last_attempted": "2026-01-10T08:00:00",
"audiodb_last_attempted": "2026-01-10T08:00:00",
"deezer_last_attempted": "2026-01-10T08:00:00"
},
"albums": [
{
"id": "87",
"artist_id": "42",
"title": "OK Computer",
"year": 1997,
"...": "..."
}
]
}
}Artist fields:
| Field | Type | Description |
|---|---|---|
id |
string | Internal database ID (text; usually numeric, standalone libraries may use opaque IDs containing ::soulsync) |
name |
string | Artist name |
thumb_url |
string? | Artist thumbnail/profile image URL |
banner_url |
string? | Artist banner/fanart image URL (from AudioDB) |
genres |
string[] | List of genre tags |
summary |
string? | Artist biography/description |
style |
string? | Musical style (from AudioDB) |
mood |
string? | Musical mood (from AudioDB) |
label |
string? | Record label (from AudioDB) |
server_source |
string? | Media server source (plex, jellyfin, navidrome) |
created_at |
string? | ISO 8601 timestamp when added to library |
updated_at |
string? | ISO 8601 timestamp of last update |
musicbrainz_id |
string? | MusicBrainz artist MBID |
spotify_artist_id |
string? | Spotify artist ID |
itunes_artist_id |
string? | Apple Music / iTunes artist ID |
audiodb_id |
string? | TheAudioDB artist ID |
deezer_id |
string? | Deezer artist ID |
musicbrainz_match_status |
string? | MusicBrainz enrichment status (matched, not_found, error) |
spotify_match_status |
string? | Spotify enrichment status |
itunes_match_status |
string? | iTunes enrichment status |
audiodb_match_status |
string? | AudioDB enrichment status |
deezer_match_status |
string? | Deezer enrichment status |
musicbrainz_last_attempted |
string? | ISO 8601 timestamp of last MusicBrainz lookup |
spotify_last_attempted |
string? | ISO 8601 timestamp of last Spotify lookup |
itunes_last_attempted |
string? | ISO 8601 timestamp of last iTunes lookup |
audiodb_last_attempted |
string? | ISO 8601 timestamp of last AudioDB lookup |
deezer_last_attempted |
string? | ISO 8601 timestamp of last Deezer lookup |
soul_id |
string? | Cross-install identity used by the MetaSync network |
soul_id_path |
string? | How the soul_id was derived (canonical, album, or name) |
aliases |
string[] | Alternate artist names (from MusicBrainz) |
discogs_id |
string? | Discogs artist ID |
discogs_bio |
string? | Artist biography (from Discogs) |
discogs_members |
string[] | Band member names (from Discogs) |
discogs_urls |
string[] | External links (from Discogs) |
discogs_match_status |
string? | Discogs enrichment status |
discogs_last_attempted |
string? | ISO 8601 timestamp of last Discogs lookup |
amazon_id |
string? | Amazon artist ID |
amazon_match_status |
string? | Amazon enrichment status |
amazon_last_attempted |
string? | ISO 8601 timestamp of last Amazon lookup |
jiosaavn_id |
string? | JioSaavn artist ID |
jiosaavn_match_status |
string? | JioSaavn enrichment status |
jiosaavn_last_attempted |
string? | ISO 8601 timestamp of last JioSaavn lookup |
art_locked |
bool? | Custom artwork lock: scans keep the current artwork |
genres_locked |
bool? | Genre lock: scans keep the current genres |
Fields marked
?may benullif the data hasn't been enriched from that provider yet.
List all albums for a specific artist.
List/search all albums with pagination and optional filters.
| Param | Type | Default | Description |
|---|---|---|---|
search |
string | Filter by album title | |
artist_id |
string | Filter by artist ID (text; may be opaque). An empty value means no filter | |
year |
int | Filter by release year | |
page |
int | 1 | Page number |
limit |
int | 50 | Items per page (max 200) |
fields |
string | Comma-separated field list |
Get a single album by ID with all metadata and embedded track list.
{
"data": {
"album": {
"id": "87",
"artist_id": "42",
"title": "OK Computer",
"year": 1997,
"thumb_url": "https://i.scdn.co/image/...",
"genres": ["alternative rock"],
"track_count": 12,
"duration": 3198000,
"style": "Art Rock",
"mood": "Anxious",
"label": "Parlophone",
"explicit": false,
"record_type": "album",
"server_source": "plex",
"created_at": "2025-12-01T14:30:00",
"updated_at": "2026-02-15T09:12:00",
"musicbrainz_release_id": "a1c35a51-d102-4ce7-b7b0-8a4f68385bb2",
"spotify_album_id": "6dVIqQ8qmQ5GBnJ9shOYGE",
"itunes_album_id": "1097862703",
"audiodb_id": "2110483",
"deezer_id": "6575789",
"musicbrainz_match_status": "matched",
"spotify_match_status": "matched",
"itunes_match_status": "matched",
"audiodb_match_status": "matched",
"deezer_match_status": "matched",
"musicbrainz_last_attempted": "2026-01-10T08:00:00",
"spotify_last_attempted": "2026-01-10T08:00:00",
"itunes_last_attempted": "2026-01-10T08:00:00",
"audiodb_last_attempted": "2026-01-10T08:00:00",
"deezer_last_attempted": "2026-01-10T08:00:00"
},
"tracks": [
{
"id": "510",
"title": "Airbag",
"track_number": 1,
"...": "..."
}
]
}
}Album fields:
| Field | Type | Description |
|---|---|---|
id |
string | Internal database ID (text; may be opaque) |
artist_id |
string | Parent artist ID |
title |
string | Album title |
year |
int? | Release year |
thumb_url |
string? | Album cover art URL |
genres |
string[] | Genre tags |
track_count |
int? | Number of tracks |
duration |
int? | Total duration in milliseconds |
style |
string? | Musical style (from AudioDB) |
mood |
string? | Musical mood (from AudioDB) |
label |
string? | Record label |
explicit |
bool? | Whether album contains explicit content |
record_type |
string? | Album type (album, single, ep, compilation) |
server_source |
string? | Media server source |
created_at |
string? | ISO 8601 timestamp |
updated_at |
string? | ISO 8601 timestamp |
musicbrainz_release_id |
string? | MusicBrainz release MBID |
spotify_album_id |
string? | Spotify album ID |
itunes_album_id |
string? | Apple Music / iTunes album ID |
audiodb_id |
string? | TheAudioDB album ID |
deezer_id |
string? | Deezer album ID |
musicbrainz_match_status |
string? | MusicBrainz enrichment status |
spotify_match_status |
string? | Spotify enrichment status |
itunes_match_status |
string? | iTunes enrichment status |
audiodb_match_status |
string? | AudioDB enrichment status |
deezer_match_status |
string? | Deezer enrichment status |
musicbrainz_last_attempted |
string? | ISO 8601 timestamp |
spotify_last_attempted |
string? | ISO 8601 timestamp |
itunes_last_attempted |
string? | ISO 8601 timestamp |
audiodb_last_attempted |
string? | ISO 8601 timestamp |
deezer_last_attempted |
string? | ISO 8601 timestamp |
release_date |
string? | Full release date when known (alongside the extracted year) |
soul_id |
string? | Cross-install identity used by the MetaSync network |
discogs_id |
string? | Discogs release ID |
discogs_genres |
string[] | Genres (from Discogs) |
discogs_styles |
string[] | Styles (from Discogs) |
discogs_label |
string? | Label (from Discogs) |
discogs_catno |
string? | Catalog number (from Discogs) |
discogs_country |
string? | Release country (from Discogs) |
discogs_rating |
float? | Community rating (from Discogs) |
discogs_rating_count |
int? | Number of community ratings (from Discogs) |
discogs_match_status |
string? | Discogs enrichment status |
discogs_last_attempted |
string? | ISO 8601 timestamp |
amazon_id |
string? | Amazon album ID |
amazon_match_status |
string? | Amazon enrichment status |
amazon_last_attempted |
string? | ISO 8601 timestamp |
jiosaavn_id |
string? | JioSaavn album ID |
jiosaavn_match_status |
string? | JioSaavn enrichment status |
jiosaavn_last_attempted |
string? | ISO 8601 timestamp |
bandcamp_id |
string? | Bandcamp release ID |
bandcamp_url |
string? | Bandcamp release URL |
bandcamp_tags |
string[] | Tags (from Bandcamp) |
bandcamp_label |
string? | Label (from Bandcamp) |
bandcamp_match_status |
string? | Bandcamp enrichment status |
bandcamp_last_attempted |
string? | ISO 8601 timestamp |
api_track_count |
int? | Track count of the canonical release, when resolved |
canonical_track_count |
int? | Track count fetched for the selected canonical release |
canonical_source |
string? | Provider that supplied the canonical release |
canonical_album_id |
string? | Provider ID of the canonical release |
canonical_score |
float? | Confidence score of the canonical match |
canonical_resolved_at |
string? | ISO 8601 timestamp of canonical resolution |
canonical_locked |
bool? | Canonical selection was locked by hand |
art_locked |
bool? | Custom artwork lock: scans keep the current artwork |
genres_locked |
bool? | Genre lock: scans keep the current genres |
metadata_locked |
bool? | Metadata lock: the row was hand-tagged and scans keep it |
List all tracks in an album with full metadata.
Get a single track by ID with all metadata.
{
"data": {
"track": {
"id": "512",
"album_id": "87",
"artist_id": "42",
"title": "Paranoid Android",
"artist_name": "Radiohead",
"album_title": "OK Computer",
"track_number": 2,
"duration": 383000,
"file_path": "/music/Radiohead/OK Computer/02 - Paranoid Android.flac",
"bitrate": 1024,
"bpm": 82.5,
"explicit": false,
"style": "Art Rock",
"mood": "Anxious",
"repair_status": null,
"repair_last_checked": null,
"server_source": "plex",
"created_at": "2025-12-01T14:30:00",
"updated_at": "2026-02-15T09:12:00",
"musicbrainz_recording_id": "b3e2b7e0-a147-4b3c-8eab-fd90bfff7e74",
"spotify_track_id": "6LgJvl0Xdtc73RJ1mN1a7Z",
"itunes_track_id": "1097863011",
"audiodb_id": null,
"deezer_id": "119606528",
"musicbrainz_match_status": "matched",
"spotify_match_status": "matched",
"itunes_match_status": "matched",
"audiodb_match_status": null,
"deezer_match_status": "matched",
"musicbrainz_last_attempted": "2026-01-10T08:00:00",
"spotify_last_attempted": "2026-01-10T08:00:00",
"itunes_last_attempted": "2026-01-10T08:00:00",
"audiodb_last_attempted": null,
"deezer_last_attempted": "2026-01-10T08:00:00"
}
}
}Track fields:
| Field | Type | Description |
|---|---|---|
id |
string | Internal database ID (text; may be opaque) |
album_id |
string | Parent album ID |
artist_id |
string | Parent artist ID |
title |
string | Track title |
artist_name |
string? | Artist name (joined from artists table) |
album_title |
string? | Album title (joined from albums table) |
track_number |
int? | Track number on the album |
duration |
int? | Duration in milliseconds |
file_path |
string? | SoulSync's local file path for the track |
bitrate |
int? | Audio bitrate in kbps |
bpm |
float? | Beats per minute |
explicit |
bool? | Whether track contains explicit content |
style |
string? | Musical style (from AudioDB) |
mood |
string? | Musical mood (from AudioDB) |
repair_status |
string? | Track repair status |
repair_last_checked |
string? | ISO 8601 timestamp of last repair check |
server_source |
string? | Media server source |
created_at |
string? | ISO 8601 timestamp |
updated_at |
string? | ISO 8601 timestamp |
musicbrainz_recording_id |
string? | MusicBrainz recording MBID |
spotify_track_id |
string? | Spotify track ID |
itunes_track_id |
string? | Apple Music / iTunes track ID |
audiodb_id |
string? | TheAudioDB track ID |
deezer_id |
string? | Deezer track ID |
musicbrainz_match_status |
string? | MusicBrainz enrichment status |
spotify_match_status |
string? | Spotify enrichment status |
itunes_match_status |
string? | iTunes enrichment status |
audiodb_match_status |
string? | AudioDB enrichment status |
deezer_match_status |
string? | Deezer enrichment status |
musicbrainz_last_attempted |
string? | ISO 8601 timestamp |
spotify_last_attempted |
string? | ISO 8601 timestamp |
itunes_last_attempted |
string? | ISO 8601 timestamp |
audiodb_last_attempted |
string? | ISO 8601 timestamp |
deezer_last_attempted |
string? | ISO 8601 timestamp |
disc_number |
int? | Disc number for multi-disc releases |
year |
int? | Per-track release year from file tags |
track_artist |
string? | Track performer credit (e.g. on compilations) |
recording_disambiguation |
string? | MusicBrainz recording comment distinguishing versions (e.g. live, acoustic) |
server_path |
string? | File path as reported by the media server |
file_size |
int? | File size in bytes |
soul_id |
string? | Cross-install song identity used by the MetaSync network |
album_soul_id |
string? | Cross-install identity of this track's specific release |
amazon_id |
string? | Amazon track ID |
amazon_match_status |
string? | Amazon enrichment status |
amazon_last_attempted |
string? | ISO 8601 timestamp |
jiosaavn_id |
string? | JioSaavn track ID |
jiosaavn_match_status |
string? | JioSaavn enrichment status |
jiosaavn_last_attempted |
string? | ISO 8601 timestamp |
bandcamp_id |
string? | Bandcamp track ID |
bandcamp_url |
string? | Bandcamp track URL |
bandcamp_tags |
string[] | Tags (from Bandcamp) |
bandcamp_label |
string? | Label (from Bandcamp) |
bandcamp_match_status |
string? | Bandcamp enrichment status |
bandcamp_last_attempted |
string? | ISO 8601 timestamp |
metadata_locked |
bool? | Metadata lock: the row was hand-tagged and scans keep it |
Search tracks by title and/or artist. At least one of title or artist is required.
| Param | Type | Default | Description |
|---|---|---|---|
title |
string | Track title to search | |
artist |
string | Artist name to search | |
limit |
int | 50 | Max results (max 200) |
fields |
string | Comma-separated field list |
List all genres in the library with occurrence counts.
| Param | Type | Default | Description |
|---|---|---|---|
source |
string | artists |
Table to aggregate from: artists or albums |
{
"data": {
"genres": [
{ "name": "rock", "count": 234 },
{ "name": "alternative rock", "count": 189 },
{ "name": "indie rock", "count": 156 },
{ "name": "electronic", "count": 98 },
{ "name": "pop", "count": 87 }
],
"source": "artists"
}
}Get recently added content, ordered by creation date.
| Param | Type | Default | Description |
|---|---|---|---|
type |
string | albums |
Entity type: albums, artists, or tracks |
limit |
int | 50 | Max items (max 200) |
fields |
string | Comma-separated field list |
{
"data": {
"items": [
{
"id": "4831",
"artist_id": "42",
"title": "A Moon Shaped Pool",
"year": 2016,
"thumb_url": "https://...",
"genres": ["art rock"],
"...": "..."
}
],
"type": "albums"
}
}Look up a library entity by its external provider ID. Useful for cross-referencing with Spotify, MusicBrainz, iTunes, Deezer, AudioDB, Tidal, Qobuz, Genius, Discogs, Amazon, JioSaavn, or Bandcamp.
| Param | Type | Required | Description |
|---|---|---|---|
type |
string | Yes | artist, album, or track |
provider |
string | Yes | spotify, musicbrainz, itunes, deezer, audiodb, tidal, qobuz, genius, discogs, amazon, jiosaavn, or bandcamp |
id |
string | Yes | The external ID value |
fields |
string | No | Comma-separated field list |
Not every provider applies to every type: Genius IDs are not stored for albums, Bandcamp IDs are not stored for artists, and Discogs IDs are not stored for tracks. Those combinations return 400 BAD_REQUEST.
Example — find an artist by Spotify ID:
GET /api/v1/library/lookup?type=artist&provider=spotify&id=4Z8W4fKeB5YxbusRsdQVPb
{
"data": {
"artist": {
"id": "42",
"name": "Radiohead",
"spotify_artist_id": "4Z8W4fKeB5YxbusRsdQVPb",
"...": "..."
}
}
}Example — find a track by MusicBrainz recording ID:
GET /api/v1/library/lookup?type=track&provider=musicbrainz&id=b3e2b7e0-a147-4b3c-8eab-fd90bfff7e74
Returns 404 NOT_FOUND if no matching entity exists in the library.
Library statistics (counts and database info).
{
"data": {
"artists": 1250,
"albums": 4830,
"tracks": 52100,
"database_size_mb": 145.2,
"last_update": "2026-03-04T09:00:00"
}
}Per-album completed-download aggregates, used by the MetaSync node to build its download-based signals. One row per library album that has at least one completed download, keyed by the album's MusicBrainz release ID (an opaque text identifier). Only IDs, counts, and timestamps are returned — no titles or file paths. Albums without a MusicBrainz release ID do not appear.
Parameters:
since(optional): ISO-8601 timestamp. Each row'scompleted_sincecounts only downloads completed at or after this instant.until(optional): ISO-8601 timestamp.completed_sincecounts only downloads completed before this instant, sosince+untilcount one closed window. Without either,completed_sinceequalscompleted_downloads.
{
"data": {
"albums": [
{
"musicbrainz_release_id": "b1392450-e666-3926-a536-22c65f834433",
"completed_downloads": 12,
"completed_since": 3,
"first_downloaded_at": "2026-08-02 14:03:11",
"last_downloaded_at": "2026-10-21 09:30:00"
}
]
}
}Per-album listening aggregates for one time window, used by the MetaSync node for its opt-in listening signal. One row per library album played in [since, until), keyed by the album's MusicBrainz release ID: how many plays, how many distinct tracks, and on how many distinct (UTC) days. Only IDs and counts are returned — no titles, no individual play times, no profile or media-server details. Albums without a MusicBrainz release ID, and plays that matched no library track, do not appear.
Only the shared listening history is read — the plays the admin's own stats show. A profile with its own ListenBrainz, Last.fm, or media-server account keeps its own history, and that history is never included here.
Parameters:
since(required): ISO-8601 timestamp, window start (inclusive).until(required): ISO-8601 timestamp, window end (exclusive). At most 31 days aftersince.
{
"data": {
"albums": [
{
"musicbrainz_release_id": "b1392450-e666-3926-a536-22c65f834433",
"plays": 14,
"distinct_tracks": 9,
"distinct_days": 3
}
]
}
}Search external music sources (Spotify, iTunes, Hydrabase). These endpoints search external services, not your local library (use /library/tracks or /library/lookup for that).
{
"query": "Daft Punk Around the World",
"source": "auto",
"limit": 20
}| Field | Type | Default | Description |
|---|---|---|---|
query |
string | required | Search query |
source |
string | auto |
auto (Hydrabase > Spotify > iTunes), spotify, or itunes |
limit |
int | 20 | Max results (max 50) |
Response:
{
"data": {
"tracks": [
{
"id": "2cGxRwrMyEAp8dEbuZaVv6",
"name": "Around the World",
"artists": ["Daft Punk"],
"album": "Homework",
"duration_ms": 428000,
"popularity": 78,
"preview_url": "https://...",
"image_url": "https://i.scdn.co/image/...",
"release_date": "1997-01-17"
}
],
"source": "spotify"
}
}{
"query": "Discovery",
"limit": 10
}Response:
{
"data": {
"albums": [
{
"id": "2noRn2Aes5aoNVsU6iWThc",
"name": "Discovery",
"artists": ["Daft Punk"],
"release_date": "2001-03-12",
"total_tracks": 14,
"album_type": "album",
"image_url": "https://..."
}
],
"source": "spotify"
}
}{
"query": "Daft Punk",
"limit": 10
}Response:
{
"data": {
"artists": [
{
"id": "4tZwfgrHOc3mvqYlEYSvnL",
"name": "Daft Punk",
"popularity": 82,
"genres": ["electro", "french house"],
"followers": 21000000,
"image_url": "https://..."
}
],
"source": "spotify"
}
}List active and recent download tasks.
{
"data": {
"downloads": [
{
"id": "task_abc123",
"status": "downloading",
"track_name": "Paranoid Android",
"artist_name": "Radiohead",
"album_name": "OK Computer",
"username": "soulseek_user_42",
"filename": "02 - Paranoid Android.flac",
"progress": 67,
"size": 45000000,
"error": null,
"batch_id": "batch_xyz",
"track_index": 2,
"retry_count": 0,
"metadata_enhanced": false,
"status_change_time": 1709550000.123
}
]
}
}Download fields:
| Field | Type | Description |
|---|---|---|
id |
string | Unique task identifier |
status |
string | pending, searching, downloading, completed, failed |
track_name |
string? | Track being downloaded |
artist_name |
string? | Artist name |
album_name |
string? | Album name |
username |
string? | Soulseek peer username |
filename |
string? | Remote filename |
progress |
int | Download progress percentage (0-100) |
size |
int? | File size in bytes |
error |
string? | Error message if failed |
batch_id |
string? | Batch download group ID |
track_index |
int? | Track position in batch |
retry_count |
int | Number of retry attempts |
metadata_enhanced |
bool | Whether metadata was enhanced post-download |
status_change_time |
float? | Unix timestamp of last status change |
Cancel a specific download.
{
"username": "soulseek_username"
}Cancel all active downloads and clear completed ones.
Tracks that failed to download, queued for retry. Profile-scoped via X-Profile-Id.
List wishlist tracks with standardized format.
| Param | Type | Default | Description |
|---|---|---|---|
category |
string | singles or albums |
|
page |
int | 1 | Page number |
limit |
int | 50 | Items per page (max 200) |
fields |
string | Comma-separated field list |
{
"data": {
"tracks": [
{
"id": 15,
"spotify_track_id": "6LgJvl0Xdtc73RJ1mN1a7Z",
"track_name": "Paranoid Android",
"artist_name": "Radiohead",
"album_name": "OK Computer",
"spotify_data": { "...full Spotify track object..." },
"failure_reason": "No sources found",
"retry_count": 3,
"last_attempted": "2026-03-03T15:30:00",
"date_added": "2026-03-01T10:00:00",
"source_type": "playlist",
"source_info": { "playlist_name": "My Playlist", "playlist_id": "..." },
"profile_id": 1
}
]
},
"pagination": { "..." }
}Wishlist track fields:
| Field | Type | Description |
|---|---|---|
id |
int | Internal database ID |
spotify_track_id |
string | Spotify track ID |
track_name |
string | Extracted track name |
artist_name |
string | Extracted artist name(s) |
album_name |
string? | Extracted album name |
spotify_data |
object | Full Spotify track metadata object |
failure_reason |
string? | Why the download failed |
retry_count |
int | Number of retry attempts |
last_attempted |
string? | ISO 8601 timestamp of last attempt |
date_added |
string? | ISO 8601 timestamp when added |
source_type |
string? | How it was added: playlist, album, manual, api |
source_info |
object? | Context about the source (playlist name, etc.) |
profile_id |
int? | Profile this track belongs to |
Add a track to the wishlist.
{
"spotify_track_data": {
"id": "6LgJvl0Xdtc73RJ1mN1a7Z",
"name": "Paranoid Android",
"artists": [{ "name": "Radiohead" }],
"album": { "name": "OK Computer", "album_type": "album" }
},
"failure_reason": "No sources found",
"source_type": "api"
}Remove a track from the wishlist by its Spotify track ID.
Trigger wishlist download processing (retries all failed tracks). 409 CONFLICT while a run is already in progress.
Artists being monitored for new releases. Profile-scoped via X-Profile-Id.
List all watched artists for the current profile.
{
"data": {
"artists": [
{
"id": 5,
"spotify_artist_id": "4tZwfgrHOc3mvqYlEYSvnL",
"itunes_artist_id": "5468295",
"artist_name": "Daft Punk",
"image_url": "https://i.scdn.co/image/...",
"date_added": "2026-01-15T10:00:00",
"last_scan_timestamp": "2026-03-04T06:00:00",
"created_at": "2026-01-15T10:00:00",
"updated_at": "2026-03-04T06:00:00",
"profile_id": 1,
"include_albums": true,
"include_eps": true,
"include_singles": true,
"include_live": false,
"include_remixes": false,
"include_acoustic": false,
"include_compilations": false,
"follow_defaults": true
}
]
}
}Watchlist artist fields:
| Field | Type | Description |
|---|---|---|
id |
int | Internal database ID |
spotify_artist_id |
string? | Spotify artist ID |
itunes_artist_id |
string? | iTunes artist ID |
artist_name |
string | Artist name |
image_url |
string? | Artist image URL |
date_added |
string? | ISO 8601 timestamp |
last_scan_timestamp |
string? | ISO 8601 timestamp of last scan |
created_at |
string? | ISO 8601 timestamp |
updated_at |
string? | ISO 8601 timestamp |
profile_id |
int? | Profile this entry belongs to |
include_albums |
bool | Monitor for new albums |
include_eps |
bool | Monitor for new EPs |
include_singles |
bool | Monitor for new singles |
include_live |
bool | Include live recordings |
include_remixes |
bool | Include remixes |
include_acoustic |
bool | Include acoustic versions |
include_compilations |
bool | Include compilations |
follow_defaults |
bool | The artist uses the watchlist defaults. The include_* fields then show the defaults, which are the rules a scan applies |
Add an artist to the watchlist.
{
"artist_id": "4tZwfgrHOc3mvqYlEYSvnL",
"artist_name": "Daft Punk"
}Update content type filters for a watched artist without having to remove and re-add them. Only the fields you include in the body will be updated.
{
"include_live": true,
"include_remixes": true,
"include_compilations": false
}Accepts any combination of: include_albums, include_eps, include_singles, include_live, include_remixes, include_acoustic, include_compilations, follow_defaults.
Send "follow_defaults": true to put an artist back on the watchlist defaults. Sending release rules for an artist that follows the defaults gives it its own rules, starting from the defaults it was getting, so the fields you leave out keep the values a scan was already using.
Response:
{
"data": {
"message": "Watchlist filters updated.",
"updated": {
"include_live": true,
"include_remixes": true,
"include_compilations": false
}
}
}Remove an artist from the watchlist. artist_id can be a Spotify or iTunes artist ID.
Trigger a watchlist scan for new releases. Returns 409 CONFLICT if a scan is already running.
Browse discovery pool, similar artists, and recent releases. Profile-scoped via X-Profile-Id.
List discovery pool tracks with pagination and optional filters.
| Param | Type | Default | Description |
|---|---|---|---|
new_releases_only |
string | false |
Set to true to filter to new releases only |
source |
string | spotify or itunes (omit for all) |
|
page |
int | 1 | Page number |
limit |
int | 100 | Items per page (max 500) |
fields |
string | Comma-separated field list |
{
"data": {
"tracks": [
{
"id": 1024,
"spotify_track_id": "3n3Ppam7vgaVa1iaRUc9Lp",
"spotify_album_id": "2noRn2Aes5aoNVsU6iWThc",
"spotify_artist_id": "4tZwfgrHOc3mvqYlEYSvnL",
"itunes_track_id": null,
"itunes_album_id": null,
"itunes_artist_id": null,
"source": "spotify",
"track_name": "Something About Us",
"artist_name": "Daft Punk",
"album_name": "Discovery",
"album_cover_url": "https://i.scdn.co/image/...",
"duration_ms": 232000,
"popularity": 76,
"release_date": "2001-03-12",
"is_new_release": false,
"artist_genres": ["electro", "french house"],
"added_date": "2026-03-01T12:00:00"
}
]
},
"pagination": { "page": 1, "limit": 100, "total": 450, "..." }
}List top similar artists discovered from watchlist analysis.
| Param | Type | Default | Description |
|---|---|---|---|
limit |
int | 50 | Max artists (max 200) |
fields |
string | Comma-separated field list |
{
"data": {
"artists": [
{
"id": 88,
"source_artist_id": "4tZwfgrHOc3mvqYlEYSvnL",
"similar_artist_spotify_id": "12Chz98pHFMPJEknJQMWvI",
"similar_artist_itunes_id": null,
"similar_artist_name": "Justice",
"similarity_rank": 1,
"occurrence_count": 5,
"last_updated": "2026-03-01T12:00:00",
"last_featured": "2026-03-03T08:00:00"
}
]
}
}List recent releases from watched artists.
| Param | Type | Default | Description |
|---|---|---|---|
limit |
int | 50 | Max releases (max 200) |
fields |
string | Comma-separated field list |
{
"data": {
"releases": [
{
"id": 12,
"watchlist_artist_id": 5,
"album_spotify_id": "2noRn2Aes5aoNVsU6iWThc",
"album_itunes_id": null,
"source": "spotify",
"album_name": "Random Access Memories (10th Anniversary Edition)",
"release_date": "2023-05-12",
"album_cover_url": "https://...",
"track_count": 22,
"added_date": "2026-03-01T06:00:00"
}
]
}
}Get discovery pool metadata (when it was last populated, track count).
{
"data": {
"last_populated": "2026-03-04T06:00:00",
"track_count": 450,
"updated_at": "2026-03-04T06:00:00"
}
}List all bubble snapshots for the current profile. Returns snapshots for three types: artist_bubbles, search_bubbles, discover_downloads. Each snapshot is null if it hasn't been created yet.
{
"data": {
"snapshots": {
"artist_bubbles": {
"data": {
"bubbles": [
{ "name": "Radiohead", "value": 45, "genre": "alternative rock" },
{ "name": "Daft Punk", "value": 32, "genre": "electronic" },
{ "name": "Portishead", "value": 18, "genre": "trip hop" }
],
"total_artists": 3,
"generated_at": "2026-03-04T06:00:00"
},
"timestamp": "2026-03-04T06:00:00"
},
"search_bubbles": {
"data": {
"bubbles": [
{ "query": "ambient electronic", "count": 12 },
{ "query": "shoegaze", "count": 8 }
],
"generated_at": "2026-03-03T14:00:00"
},
"timestamp": "2026-03-03T14:00:00"
},
"discover_downloads": null
}
}
}Get a specific bubble snapshot by type.
| Param | Type | Description |
|---|---|---|
snapshot_type |
path | artist_bubbles, search_bubbles, or discover_downloads |
{
"data": {
"snapshot": {
"data": {
"bubbles": [
{ "name": "Radiohead", "value": 45, "genre": "alternative rock" },
{ "name": "Daft Punk", "value": 32, "genre": "electronic" },
{ "name": "Portishead", "value": 18, "genre": "trip hop" }
],
"total_artists": 3,
"generated_at": "2026-03-04T06:00:00"
},
"timestamp": "2026-03-04T06:00:00"
}
}
}Bubble snapshot fields:
| Field | Type | Description |
|---|---|---|
data |
object | Parsed snapshot data (structure varies by type — contains bubble arrays and metadata) |
timestamp |
string | ISO 8601 timestamp when the snapshot was created/updated |
Snapshot types:
artist_bubbles— artist listening weight visualization data.search_bubbles— search frequency visualization data.discover_downloads— discovery download activity visualization data. Returns404if the requested snapshot type doesn't exist for this profile.
List user playlists from Spotify or Tidal.
| Param | Type | Default | Description |
|---|---|---|---|
source |
string | spotify |
spotify or tidal |
{
"data": {
"playlists": [
{
"id": "37i9dQZF1DXcBWIGoYBM5M",
"name": "Today's Top Hits",
"owner": "spotify",
"track_count": 50,
"image_url": "https://..."
}
],
"source": "spotify"
}
}Get playlist details with full track list.
| Param | Type | Default | Description |
|---|---|---|---|
source |
string | spotify |
Currently only spotify supported for detail view |
{
"data": {
"playlist": {
"id": "37i9dQZF1DXcBWIGoYBM5M",
"name": "Today's Top Hits",
"owner": "Spotify",
"total_tracks": 50,
"tracks": [
{
"id": "2cGxRwrMyEAp8dEbuZaVv6",
"name": "Around the World",
"artists": ["Daft Punk"],
"album": "Homework",
"duration_ms": 428000,
"image_url": "https://..."
}
]
},
"source": "spotify"
}
}Trigger playlist sync/download.
{
"playlist_name": "My Playlist",
"tracks": [
{
"id": "2cGxRwrMyEAp8dEbuZaVv6",
"name": "Around the World",
"artists": [{ "name": "Daft Punk" }]
}
]
}Manage user profiles. Profiles scope watchlist, wishlist, and discovery data.
List all profiles.
{
"data": {
"profiles": [
{
"id": 1,
"name": "Admin",
"avatar_color": "#6366f1",
"avatar_url": null,
"is_admin": true,
"has_pin": false,
"created_at": "2026-01-15T10:00:00",
"updated_at": "2026-02-20T14:30:00"
},
{
"id": 2,
"name": "Family",
"avatar_color": "#10b981",
"avatar_url": "https://example.com/avatar.jpg",
"is_admin": false,
"has_pin": true,
"created_at": "2026-02-01T15:00:00",
"updated_at": "2026-03-01T09:45:00"
}
]
}
}Get a single profile by ID.
{
"data": {
"profile": {
"id": 2,
"name": "Family",
"avatar_color": "#10b981",
"avatar_url": "https://example.com/avatar.jpg",
"is_admin": false,
"has_pin": true,
"created_at": "2026-02-01T15:00:00",
"updated_at": "2026-03-01T09:45:00"
}
}
}Profile fields:
| Field | Type | Description |
|---|---|---|
id |
int | Profile ID (auto-increment) |
name |
string | Display name (unique) |
avatar_color |
string | Hex color for the default avatar circle (default #6366f1) |
avatar_url |
string? | Custom avatar image URL |
is_admin |
bool | Whether this profile has admin privileges |
has_pin |
bool | Whether a PIN is set (the PIN hash itself is never exposed) |
created_at |
string | ISO 8601 timestamp when profile was created |
updated_at |
string | ISO 8601 timestamp of last profile update |
Note: The
pin_hashcolumn exists in the database but is never returned by the API. Only the computedhas_pinboolean is exposed.
Create a new profile.
Request body:
{
"name": "Family",
"avatar_color": "#10b981",
"avatar_url": "https://example.com/avatar.jpg",
"is_admin": false,
"pin": "1234"
}All fields except name are optional. The pin field accepts a raw PIN string — it is hashed with PBKDF2-SHA256 before storage.
Response (201 Created):
{
"data": {
"profile": {
"id": 3,
"name": "Family",
"avatar_color": "#10b981",
"avatar_url": "https://example.com/avatar.jpg",
"is_admin": false,
"has_pin": true,
"created_at": "2026-03-04T12:00:00",
"updated_at": "2026-03-04T12:00:00"
}
}
}Returns 409 CONFLICT if the profile name already exists.
Update a profile. Only include the fields you want to change.
Request body:
{
"name": "New Name",
"avatar_color": "#ef4444",
"pin": "5678"
}Set "pin": "" or "pin": null to remove a PIN.
Response:
{
"data": {
"profile": {
"id": 2,
"name": "New Name",
"avatar_color": "#ef4444",
"avatar_url": "https://example.com/avatar.jpg",
"is_admin": false,
"has_pin": true,
"created_at": "2026-02-01T15:00:00",
"updated_at": "2026-03-04T12:05:00"
}
}
}Delete a profile and all its per-profile data (watchlist, wishlist, discovery pool, bubble snapshots). Cannot delete profile 1 (admin) — returns 403 FORBIDDEN.
Response:
{
"data": {
"message": "Profile 3 deleted."
}
}Browse and manage the retag queue — albums/tracks pending metadata corrections.
The queue is the Library Re-tag job's pending findings: a group is an album with
pending findings (id = the album's id), a track is one finding (id = the
finding's id), and deleting a group dismisses its findings.
List all retag groups with track counts. Groups are ordered by artist name (ascending) then creation date (descending).
{
"data": {
"groups": [
{
"id": 1,
"group_type": "album",
"artist_name": "Radiohead",
"album_name": "OK Computer",
"image_url": "https://i.scdn.co/image/ab67616d0000b273c8b444df094c596ea9da41f6",
"spotify_album_id": "6dVIqQ8qmQ5GBnJ9shOYGE",
"itunes_album_id": "1097862703",
"total_tracks": 12,
"release_date": "1997-06-16",
"created_at": "2026-03-04T10:00:00",
"track_count": 12
},
{
"id": 2,
"group_type": "album",
"artist_name": "Radiohead",
"album_name": "In Rainbows",
"image_url": "https://i.scdn.co/image/ab67616d0000b2737de1fcc2ebeab8b6e22a1b8a",
"spotify_album_id": "7eyQXxuf2nGj9d2367Gi5f",
"itunes_album_id": "1109731429",
"total_tracks": 10,
"release_date": "2007-10-10",
"created_at": "2026-03-04T10:05:00",
"track_count": 10
}
]
}
}Retag group fields:
| Field | Type | Description |
|---|---|---|
id |
int | Group ID (the album's id) |
group_type |
string | Type of retag group (currently always album) |
artist_name |
string | Artist name for this retag group |
album_name |
string | Album name for this retag group |
image_url |
string? | Album cover image URL |
spotify_album_id |
string? | Spotify album ID for metadata lookup |
itunes_album_id |
string? | iTunes/Apple Music album ID for metadata lookup |
total_tracks |
int | Expected total tracks for the album |
release_date |
string? | Album release date (YYYY-MM-DD) |
created_at |
string | ISO 8601 timestamp when group was created |
track_count |
int | Computed: number of tracks currently in this group |
Get a retag group with its full track list. Tracks are ordered by disc number then track number.
{
"data": {
"group": {
"id": 1,
"group_type": "album",
"artist_name": "Radiohead",
"album_name": "OK Computer",
"image_url": "https://i.scdn.co/image/ab67616d0000b273c8b444df094c596ea9da41f6",
"spotify_album_id": "6dVIqQ8qmQ5GBnJ9shOYGE",
"itunes_album_id": "1097862703",
"total_tracks": 12,
"release_date": "1997-06-16",
"created_at": "2026-03-04T10:00:00",
"track_count": 12
},
"tracks": [
{
"id": 1,
"group_id": 1,
"track_number": 1,
"disc_number": 1,
"title": "Airbag",
"file_path": "/downloads/Radiohead/OK Computer/01 - Airbag.flac",
"file_format": "flac",
"spotify_track_id": "6LgJvl0Xdtc73RJ1mN1a7Z",
"itunes_track_id": null,
"created_at": "2026-03-04T10:00:00"
},
{
"id": 2,
"group_id": 1,
"track_number": 2,
"disc_number": 1,
"title": "Paranoid Android",
"file_path": "/downloads/Radiohead/OK Computer/02 - Paranoid Android.flac",
"file_format": "flac",
"spotify_track_id": "6LgJvl0Xdtc73RJ1mN1a7Z",
"itunes_track_id": "1097863062",
"created_at": "2026-03-04T10:00:00"
},
{
"id": 3,
"group_id": 1,
"track_number": 3,
"disc_number": 1,
"title": "Subterranean Homesick Alien",
"file_path": "/downloads/Radiohead/OK Computer/03 - Subterranean Homesick Alien.flac",
"file_format": "flac",
"spotify_track_id": "3sFhbVuZGMAsmFSIFGVPgS",
"itunes_track_id": null,
"created_at": "2026-03-04T10:00:00"
}
]
}
}Retag track fields:
| Field | Type | Description |
|---|---|---|
id |
int | Track ID (the Re-tag finding's id) |
group_id |
int | Parent retag group ID (foreign key) |
track_number |
int? | Track number on disc |
disc_number |
int | Disc number (default 1) |
title |
string | Track title |
file_path |
string | Full file path to the downloaded audio file |
file_format |
string? | Audio format (flac, mp3, opus, etc.) |
spotify_track_id |
string? | Spotify track ID for metadata lookup |
itunes_track_id |
string? | iTunes/Apple Music track ID for metadata lookup |
created_at |
string | When the Re-tag finding was raised |
Dismiss a retag group: every pending Re-tag finding of that album.
{
"data": {
"message": "Retag group 1 deleted."
}
}Dismiss every pending Re-tag finding; the message counts the groups (albums) cleared.
{
"data": {
"message": "Cleared 5 retag groups."
}
}Get retag queue statistics.
{
"data": {
"groups": 5,
"tracks": 47,
"artists": 3
}
}| Field | Type | Description |
|---|---|---|
groups |
int | Total number of retag groups in queue |
tracks |
int | Total number of tracks across all groups |
artists |
int | Number of distinct artists in the queue |
Browse cached ListenBrainz playlists and their tracks. Playlists are cached locally when SoulSync pulls recommendations from ListenBrainz.
List cached ListenBrainz playlists with optional type filtering and pagination.
| Param | Type | Default | Description |
|---|---|---|---|
type |
string | Filter by playlist_type (e.g. weekly-jams, weekly-exploration) |
|
page |
int | 1 | Page number |
limit |
int | 50 | Items per page (max 200) |
{
"data": {
"playlists": [
{
"id": 1,
"playlist_mbid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"title": "Weekly Jams for user123",
"creator": "listenbrainz",
"playlist_type": "weekly-jams",
"track_count": 50,
"annotation_data": {
"algorithm": "collaborative-filtering",
"source_patch": "weekly-jams"
},
"last_updated": "2026-03-03T06:00:00",
"cached_date": "2026-03-03T06:05:00"
},
{
"id": 2,
"playlist_mbid": "f9e8d7c6-b5a4-3210-fedc-ba9876543210",
"title": "Weekly Exploration for user123",
"creator": "listenbrainz",
"playlist_type": "weekly-exploration",
"track_count": 50,
"annotation_data": {
"algorithm": "collaborative-filtering",
"source_patch": "weekly-exploration"
},
"last_updated": "2026-03-03T06:00:00",
"cached_date": "2026-03-03T06:08:00"
}
]
},
"pagination": { "page": 1, "limit": 50, "total": 8, "total_pages": 1, "has_next": false, "has_prev": false }
}ListenBrainz playlist fields:
| Field | Type | Description |
|---|---|---|
id |
int | Internal database ID |
playlist_mbid |
string | MusicBrainz playlist MBID (unique) |
title |
string | Playlist title as given by ListenBrainz |
creator |
string? | Playlist creator (usually listenbrainz) |
playlist_type |
string | Type: weekly-jams, weekly-exploration, etc. |
track_count |
int | Number of tracks in the playlist |
annotation_data |
object? | Parsed JSON annotation metadata from ListenBrainz |
last_updated |
string | ISO 8601 timestamp of last update from ListenBrainz |
cached_date |
string | ISO 8601 timestamp when SoulSync cached this playlist |
Get a ListenBrainz playlist with its full track list. playlist_id can be the internal database ID (integer) or the MusicBrainz playlist MBID (UUID string). Tracks are returned in playlist order (by position).
{
"data": {
"playlist": {
"id": 1,
"playlist_mbid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"title": "Weekly Jams for user123",
"creator": "listenbrainz",
"playlist_type": "weekly-jams",
"track_count": 50,
"annotation_data": {
"algorithm": "collaborative-filtering",
"source_patch": "weekly-jams"
},
"last_updated": "2026-03-03T06:00:00",
"cached_date": "2026-03-03T06:05:00"
},
"tracks": [
{
"id": 1,
"playlist_id": 1,
"position": 1,
"track_name": "Paranoid Android",
"artist_name": "Radiohead",
"album_name": "OK Computer",
"duration_ms": 383000,
"recording_mbid": "b3e2b7e0-a147-4b3c-8eab-fd90bfff7e74",
"release_mbid": "a1c35a51-d102-4ce7-b7b0-8a4f68385bb2",
"album_cover_url": "https://coverartarchive.org/release/a1c35a51-d102-4ce7-b7b0-8a4f68385bb2/front-250.jpg",
"additional_metadata": {
"artist_mbids": ["a74b1b7f-71a5-4011-9441-d0b5e4122711"],
"caa_id": 12345678901,
"caa_release_mbid": "a1c35a51-d102-4ce7-b7b0-8a4f68385bb2"
}
},
{
"id": 2,
"playlist_id": 1,
"position": 2,
"track_name": "All I Need",
"artist_name": "Radiohead",
"album_name": "In Rainbows",
"duration_ms": 226000,
"recording_mbid": "c7d9e0f1-2345-6789-abcd-ef0123456789",
"release_mbid": "d4e5f6a7-8901-2345-bcde-f67890123456",
"album_cover_url": "https://coverartarchive.org/release/d4e5f6a7-8901-2345-bcde-f67890123456/front-250.jpg",
"additional_metadata": {
"artist_mbids": ["a74b1b7f-71a5-4011-9441-d0b5e4122711"],
"caa_id": 23456789012,
"caa_release_mbid": "d4e5f6a7-8901-2345-bcde-f67890123456"
}
},
{
"id": 3,
"playlist_id": 1,
"position": 3,
"track_name": "Something About Us",
"artist_name": "Daft Punk",
"album_name": "Discovery",
"duration_ms": 232000,
"recording_mbid": "e8f9a0b1-c2d3-e4f5-6789-012345678901",
"release_mbid": "f0a1b2c3-d4e5-f6a7-8901-234567890123",
"album_cover_url": "https://coverartarchive.org/release/f0a1b2c3-d4e5-f6a7-8901-234567890123/front-250.jpg",
"additional_metadata": {
"artist_mbids": ["056e4f3e-d505-4dad-8ec1-d04f521cbb56"],
"caa_id": 34567890123,
"caa_release_mbid": "f0a1b2c3-d4e5-f6a7-8901-234567890123"
}
}
]
}
}ListenBrainz track fields:
| Field | Type | Description |
|---|---|---|
id |
int | Internal track ID |
playlist_id |
int | Parent playlist ID (foreign key) |
position |
int | Position in the playlist (1-based) |
track_name |
string | Track title |
artist_name |
string | Artist name |
album_name |
string | Album name |
duration_ms |
int | Track duration in milliseconds |
recording_mbid |
string? | MusicBrainz recording MBID |
release_mbid |
string? | MusicBrainz release MBID |
album_cover_url |
string? | Album cover art URL (usually from Cover Art Archive) |
additional_metadata |
object? | Parsed JSON with extra MusicBrainz data (artist MBIDs, CAA info, etc.) |
Browse internal enrichment caches. Useful for debugging enrichment issues, understanding match quality, or auditing what metadata has been resolved.
List cached MusicBrainz lookups with optional filtering and pagination. Results ordered by last_updated descending.
| Param | Type | Default | Description |
|---|---|---|---|
entity_type |
string | Filter by type: artist, album, track |
|
search |
string | Filter by entity name (case-insensitive partial match) | |
page |
int | 1 | Page number |
limit |
int | 50 | Items per page (max 200) |
{
"data": {
"entries": [
{
"id": 42,
"entity_type": "artist",
"entity_name": "Radiohead",
"artist_name": null,
"musicbrainz_id": "a74b1b7f-71a5-4011-9441-d0b5e4122711",
"spotify_id": "4Z8W4fKeB5YxbusRsdQVPb",
"itunes_id": "657515",
"metadata_json": {
"name": "Radiohead",
"type": "Group",
"country": "GB",
"disambiguation": "English rock band",
"begin_date": "1985",
"end_date": null,
"tags": ["alternative rock", "art rock", "experimental"]
},
"match_confidence": 95,
"last_updated": "2026-03-01T12:00:00"
},
{
"id": 88,
"entity_type": "album",
"entity_name": "OK Computer",
"artist_name": "Radiohead",
"musicbrainz_id": "b1f5a82e-5cfa-36f8-b084-4a93d7e7e608",
"spotify_id": "6dVIqQ8qmQ5GBnJ9shOYGE",
"itunes_id": "1097862703",
"metadata_json": {
"title": "OK Computer",
"date": "1997-06-16",
"country": "XE",
"status": "Official",
"packaging": "Jewel Case",
"barcode": "724385522925"
},
"match_confidence": 100,
"last_updated": "2026-02-28T08:30:00"
},
{
"id": 215,
"entity_type": "track",
"entity_name": "Paranoid Android",
"artist_name": "Radiohead",
"musicbrainz_id": "b3e2b7e0-a147-4b3c-8eab-fd90bfff7e74",
"spotify_id": "6LgJvl0Xdtc73RJ1mN1a7Z",
"itunes_id": "1097863062",
"metadata_json": {
"title": "Paranoid Android",
"length": 383000,
"isrcs": ["GBAYE9700074"]
},
"match_confidence": 98,
"last_updated": "2026-02-28T08:35:00"
}
]
},
"pagination": { "page": 1, "limit": 50, "total": 1250, "total_pages": 25, "has_next": true, "has_prev": false }
}MusicBrainz cache fields:
| Field | Type | Description |
|---|---|---|
id |
int | Cache entry ID |
entity_type |
string | Entity type: artist, album, or track |
entity_name |
string | Name that was looked up |
artist_name |
string? | Associated artist name (for album/track lookups) |
musicbrainz_id |
string? | Resolved MusicBrainz MBID (null if no match) |
spotify_id |
string? | Cross-referenced Spotify ID |
itunes_id |
string? | Cross-referenced iTunes/Apple Music ID |
metadata_json |
object? | Parsed JSON with full MusicBrainz metadata (tags, dates, ISRCs, etc.) |
match_confidence |
int? | Match confidence score (0-100) |
last_updated |
string | ISO 8601 timestamp of last cache update |
Unique constraint:
(entity_type, entity_name, artist_name)— each entity is cached once per type+name combination.
Get MusicBrainz cache statistics — total entries, matched vs unmatched, and breakdown by entity type.
{
"data": {
"total": 1250,
"matched": 1100,
"unmatched": 150,
"by_type": {
"artist": 500,
"album": 450,
"track": 300
}
}
}| Field | Type | Description |
|---|---|---|
total |
int | Total cache entries |
matched |
int | Entries with a resolved musicbrainz_id |
unmatched |
int | Entries where no MusicBrainz match was found |
by_type |
object | Entry counts keyed by entity_type |
List cached discovery provider matches. When SoulSync discovers tracks from external sources (Tidal playlists, YouTube Music, ListenBrainz recommendations, Beatport), it resolves them against Spotify or iTunes to get downloadable metadata. These resolved matches are cached here to avoid redundant API calls. Results ordered by last_used_at descending.
| Param | Type | Default | Description |
|---|---|---|---|
provider |
string | Filter by target provider: spotify or itunes |
|
search |
string | Filter by title or artist (case-insensitive partial match on both) | |
page |
int | 1 | Page number |
limit |
int | 50 | Items per page (max 200) |
Example — Spotify-resolved match (e.g. a Tidal track matched to Spotify):
{
"data": {
"entries": [
{
"id": 100,
"normalized_title": "paranoid android",
"normalized_artist": "radiohead",
"provider": "spotify",
"match_confidence": 0.95,
"matched_data_json": {
"id": "6LgJvl0Xdtc73RJ1mN1a7Z",
"name": "Paranoid Android",
"artists": ["Radiohead"],
"album": {
"id": "6dVIqQ8qmQ5GBnJ9shOYGE",
"name": "OK Computer",
"album_type": "album",
"release_date": "1997-06-16",
"total_tracks": 12,
"images": [
{ "url": "https://i.scdn.co/image/ab67616d0000b273c8b444df094c596ea9da41f6", "height": 640, "width": 640 },
{ "url": "https://i.scdn.co/image/ab67616d00001e02c8b444df094c596ea9da41f6", "height": 300, "width": 300 },
{ "url": "https://i.scdn.co/image/ab67616d00004851c8b444df094c596ea9da41f6", "height": 64, "width": 64 }
]
},
"duration_ms": 383000,
"external_urls": {
"spotify": "https://open.spotify.com/track/6LgJvl0Xdtc73RJ1mN1a7Z"
},
"source": "spotify"
},
"original_title": "Paranoid Android",
"original_artist": "Radiohead",
"created_at": "2026-02-15T10:00:00",
"last_used_at": "2026-03-04T09:00:00",
"use_count": 12
},
{
"id": 205,
"normalized_title": "something about us",
"normalized_artist": "daft punk",
"provider": "itunes",
"match_confidence": 0.92,
"matched_data_json": {
"id": "724633277",
"name": "Something About Us",
"artists": ["Daft Punk"],
"album": {
"name": "Discovery",
"album_type": "album",
"images": [
{ "url": "https://is1-ssl.mzstatic.com/image/thumb/Music125/v4/a1/b2/c3/disc-cover.jpg/300x300bb.jpg", "height": 300, "width": 300 }
]
},
"source": "itunes"
},
"original_title": "Something About Us",
"original_artist": "Daft Punk",
"created_at": "2026-02-20T14:30:00",
"last_used_at": "2026-03-03T18:00:00",
"use_count": 5
}
]
},
"pagination": { "page": 1, "limit": 50, "total": 3200, "total_pages": 64, "has_next": true, "has_prev": false }
}Discovery match cache fields:
| Field | Type | Description |
|---|---|---|
id |
int | Cache entry ID |
normalized_title |
string | Lowercase normalized track title used for matching |
normalized_artist |
string | Lowercase normalized artist name used for matching |
provider |
string | Target provider the track was matched against (spotify or itunes) |
match_confidence |
float | Match confidence score (0.0-1.0). Only matches ≥ 0.70 are cached. |
matched_data_json |
object | Parsed JSON with full resolved track data (structure varies by provider — see below) |
original_title |
string? | Original (un-normalized) track title from the discovery source |
original_artist |
string? | Original (un-normalized) artist name from the discovery source |
created_at |
string | ISO 8601 timestamp when match was first cached |
last_used_at |
string | ISO 8601 timestamp when match was last reused |
use_count |
int | Number of times this cached match has been reused |
matched_data_json structure by provider:
| Field | Spotify | iTunes | Description |
|---|---|---|---|
id |
string | string | Spotify track ID or iTunes track ID |
name |
string | string | Resolved track name |
artists |
string[] | string[] | Artist names (normalized to string array before caching) |
album |
object | object | Album data (see below) |
album.id |
string | — | Spotify album ID (not present for iTunes) |
album.name |
string | string | Album name |
album.album_type |
string | string | Album type (album, single, compilation) |
album.release_date |
string | — | Release date (Spotify only) |
album.total_tracks |
int | — | Track count (Spotify only) |
album.images |
array | array | Cover art: [{url, height, width}] (Spotify provides 3 sizes; iTunes provides 1) |
duration_ms |
int | — | Track duration in milliseconds (Spotify only) |
external_urls |
object | — | External URLs e.g. {"spotify": "https://..."} (Spotify only) |
source |
"spotify" |
"itunes" |
Which provider this match came from |
Unique constraint:
(normalized_title, normalized_artist, provider)— each title+artist+provider combination is cached once. Discovery sources (Tidal, YouTube, ListenBrainz, Beatport) are the input — theproviderfield records which catalog they were matched against.
Get discovery match cache statistics — total entries, total reuses, average confidence, and breakdown by provider.
{
"data": {
"total": 3200,
"total_uses": 15000,
"avg_confidence": 0.872,
"by_provider": {
"spotify": 2800,
"itunes": 400
}
}
}| Field | Type | Description |
|---|---|---|
total |
int | Total cached match entries |
total_uses |
int | Sum of all use_count values across entries |
avg_confidence |
float? | Average match confidence (rounded to 3 decimals, null if empty) |
by_provider |
object | Entry counts keyed by provider name |
Get current settings. Sensitive values (passwords, tokens, secrets) are redacted.
Update settings (partial update). Uses dot-notation keys.
{
"soulseek.search_timeout": 90,
"logging.level": "DEBUG"
}Response:
{
"data": {
"message": "Settings updated.",
"updated_keys": ["soulseek.search_timeout", "logging.level"]
}
}The movies and TV side. Every endpoint runs the same handler the web UI uses and wraps it in the v1 envelope. Returns 503 NOT_AVAILABLE on a server with the video side disabled.
Titles in the video library. kind=movies|shows (default movies), plus search, letter, sort, status, genre, resolution, page, limit. When kind=shows, the response also includes total_episodes — the global episode count across all shows.
What you own of one library movie or show (kind=movie|show, id is the library id). Every file with its resolution, quality, codecs, audio, size, runtime and path, whether SoulSync can reach it, the subtitles beside it and whether its name matches your naming template; the title's match (tmdb_id, imdb_id, tvdb_id, fixed); and the quality verdict against its profile. For a show, every season with owned, missing, upcoming, size_bytes, res_mix and monitored, plus the folders the show lives in. ?season=<n> adds the disk checks (reachability, subtitles, naming) for that season's files; without it a show is a database read.
Fix match. Body: {"tmdb_id": 841}. Re-points the library title to that TMDB entry, fills the imdb / tvdb ids TMDB gives for it, takes TMDB's title and year when your server named it differently, and locks the ids so a scan can't switch it back. Returns the new tmdb_id, title and year. 409 (with nothing changed) when TMDB doesn't answer for that id.
TMDB multi-search over movies, shows and people.
kind=movie|show for a page of items (search, sort, page, limit); no kind returns counts only.
Add a movie, or a set of a show's episodes:
{"movie": {"tmdb_id": 603, "title": "The Matrix", "year": 1999}}{"show": {"tmdb_id": 1396, "title": "Breaking Bad"},
"episodes": [{"season_number": 1, "episode_number": 1}, {"season_number": 1, "episode_number": 2}]}Body: {"scope": "movie|show|season|episode", "tmdb_id": 603, "season_number"?: 1, "episode_number"?: 1}.
Followed shows, people and studios.
Body: {"kind": "show|person|studio", "tmdb_id": 1396, "title": "Breaking Bad"}.
Body: {"kind": "show", "tmdb_id": 1396}.
Ask for a library scan. Body: {"mode"?: "incremental|deep|full"}. 409 CONFLICT while one is running.
One call for a dashboard widget. library (movies, shows, episodes on disk, episodes_missing = monitored and aired with no file, size_bytes), scoped to the video server; database (size_mb, last_update, last_scan, ISO UTC); downloads (active, failed, completed, cancelled, by_status, speed_bps, completed_all_time); wishlist (movies, episodes, videos, total).
Video downloads, active rows first then newest (the latest 100). status filters by one status or a comma list: queued, searching, downloading, importing, failed, import_failed, completed, cancelled, plus active and failed as shorthand for their groups. A filtered answer adds count. An unknown status is 400 BAD_REQUEST.
Upcoming and recent episodes and releases. start and end as ISO dates.
For a calendar app, use the iCal feed instead: GET /api/video/calendar/feed/<token>.ics. The token is per profile and is the credential, so the feed needs no session or API key. Get the link from the calendar page (⋯ → Subscribe), or GET /api/video/calendar/feed-url from a signed-in session; POST to the same path makes a new token and retires the old one. The feed takes days (1–60, default 14), scope=watchlist|all, movies=0 and specials=1, and applies that profile's video access and rating limits.
File a request for an admin to approve. Body: {"kind": "movie|show", "tmdb_id": 27205, "title": "Inception", "year"?: 2010, "note"?: "..."}.
List all API keys (shows prefix and label only, never the full key).
{
"data": {
"keys": [
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"label": "Discord Bot",
"key_prefix": "sk_a3Bf9x2",
"created_at": "2026-03-01T12:00:00",
"last_used_at": "2026-03-04T09:15:00"
}
]
}
}Generate a new API key. The raw key is returned once — save it immediately.
{
"label": "Discord Bot"
}Response:
{
"data": {
"key": "sk_a3Bf9x2Kp7Qm4Rn8Yt6Wv0Xz1Cb5Dj9Fg",
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"label": "Discord Bot",
"key_prefix": "sk_a3Bf9x2",
"created_at": "2026-03-04T10:00:00"
}
}Revoke an API key by its UUID.
Generate the first API key when none exist. No authentication required. Returns 403 if keys already exist.
{
"label": "My First Key"
}All library, watchlist, wishlist, and discovery endpoints support the ?fields= parameter to request only specific fields. This reduces response size when you only need a few fields.
GET /api/v1/library/artists/42?fields=id,name,genres,spotify_artist_id
{
"data": {
"artist": {
"id": 42,
"name": "Radiohead",
"genres": ["alternative rock", "art rock"],
"spotify_artist_id": "4Z8W4fKeB5YxbusRsdQVPb"
}
}
}import requests
API_URL = "http://localhost:8008/api/v1"
API_KEY = "sk_your_key_here"
headers = {"Authorization": f"Bearer {API_KEY}"}
# Get full artist details with all enrichment metadata
artist = requests.get(f"{API_URL}/library/artists/42", headers=headers).json()
print(f"Artist: {artist['data']['artist']['name']}")
print(f"Spotify: {artist['data']['artist']['spotify_artist_id']}")
print(f"MusicBrainz: {artist['data']['artist']['musicbrainz_id']}")
print(f"Albums: {len(artist['data']['albums'])}")
# Get a specific album with tracks
album = requests.get(f"{API_URL}/library/albums/87", headers=headers).json()
for track in album["data"]["tracks"]:
print(f" {track['track_number']}. {track['title']} ({track['duration']}ms)")
# Look up by Spotify ID
result = requests.get(f"{API_URL}/library/lookup",
headers=headers,
params={"type": "artist", "provider": "spotify", "id": "4Z8W4fKeB5YxbusRsdQVPb"}
).json()
# Browse genres
genres = requests.get(f"{API_URL}/library/genres", headers=headers).json()
for g in genres["data"]["genres"][:10]:
print(f" {g['name']}: {g['count']} artists")
# Recently added albums
recent = requests.get(f"{API_URL}/library/recently-added?type=albums&limit=10",
headers=headers).json()
# Search external sources
search = requests.post(f"{API_URL}/search/tracks",
headers=headers,
json={"query": "Daft Punk", "limit": 5})
# Add to watchlist (as profile 2)
requests.post(f"{API_URL}/watchlist",
headers={**headers, "X-Profile-Id": "2"},
json={"artist_id": "4tZwfgrHOc3mvqYlEYSvnL", "artist_name": "Daft Punk"})
# Update watchlist filters
requests.patch(f"{API_URL}/watchlist/4tZwfgrHOc3mvqYlEYSvnL",
headers=headers,
json={"include_live": True, "include_remixes": True})
# Get discovery pool
pool = requests.get(f"{API_URL}/discover/pool?limit=50", headers=headers).json()
# Get only specific fields to reduce payload
minimal = requests.get(
f"{API_URL}/library/artists?fields=id,name,thumb_url&limit=100",
headers=headers).json()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 artists
const artists = await fetch(`${API_URL}/library/artists?page=1&limit=25`, { headers })
.then(r => r.json());
// Get album with full metadata and tracks
const album = await fetch(`${API_URL}/library/albums/87`, { headers })
.then(r => r.json());
// Look up by external ID
const lookup = await fetch(
`${API_URL}/library/lookup?type=track&provider=spotify&id=6LgJvl0Xdtc73RJ1mN1a7Z`,
{ headers }
).then(r => r.json());
// Trigger watchlist scan
await fetch(`${API_URL}/watchlist/scan`, { method: 'POST', headers });
// Get discovery similar artists for profile 2
const similar = await fetch(`${API_URL}/discover/similar-artists?limit=20`, {
headers: { ...headers, 'X-Profile-Id': '2' }
}).then(r => r.json());# System status
curl -H "Authorization: Bearer sk_..." http://localhost:8008/api/v1/system/status
# Get artist with full metadata
curl -H "Authorization: Bearer sk_..." \
http://localhost:8008/api/v1/library/artists/42
# Get album with tracks
curl -H "Authorization: Bearer sk_..." \
http://localhost:8008/api/v1/library/albums/87
# Get single track
curl -H "Authorization: Bearer sk_..." \
http://localhost:8008/api/v1/library/tracks/512
# Look up by Spotify ID
curl -H "Authorization: Bearer sk_..." \
"http://localhost:8008/api/v1/library/lookup?type=artist&provider=spotify&id=4Z8W4fKeB5YxbusRsdQVPb"
# Browse genres
curl -H "Authorization: Bearer sk_..." \
http://localhost:8008/api/v1/library/genres
# Recently added albums
curl -H "Authorization: Bearer sk_..." \
"http://localhost:8008/api/v1/library/recently-added?type=albums&limit=10"
# Search external 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}'
# Watchlist with profile
curl -H "Authorization: Bearer sk_..." \
-H "X-Profile-Id: 2" \
http://localhost:8008/api/v1/watchlist
# Update watchlist filters
curl -X PATCH http://localhost:8008/api/v1/watchlist/4tZwfgrHOc3mvqYlEYSvnL \
-H "Authorization: Bearer sk_..." \
-H "Content-Type: application/json" \
-d '{"include_live": true, "include_remixes": true}'
# Discovery pool
curl -H "Authorization: Bearer sk_..." \
"http://localhost:8008/api/v1/discover/pool?limit=50&new_releases_only=true"
# Field filtering — only get id, name, and Spotify ID
curl -H "Authorization: Bearer sk_..." \
"http://localhost:8008/api/v1/library/artists?fields=id,name,spotify_artist_id&limit=100"| Method | Endpoint | Description |
|---|---|---|
| System | ||
| GET | /system/status |
Server status and service connectivity |
| GET | /system/activity |
Recent activity feed |
| GET | /system/stats |
Combined library + download stats |
| Library — Artists | ||
| GET | /library/artists |
List/search artists (paginated) |
| GET | /library/artists/<id> |
Artist detail + albums |
| GET | /library/artists/<id>/albums |
Albums for an artist |
| Library — Albums | ||
| GET | /library/albums |
List/search albums (paginated) |
| GET | /library/albums/<id> |
Album detail + tracks |
| GET | /library/albums/<id>/tracks |
Tracks in an album |
| Library — Tracks | ||
| GET | /library/tracks/<id> |
Track detail |
| GET | /library/tracks |
Search tracks by title/artist |
| Library — Browse | ||
| GET | /library/genres |
Genre listing with counts |
| GET | /library/recently-added |
Recently added content |
| GET | /library/lookup |
External ID lookup |
| GET | /library/stats |
Library statistics |
| Search | ||
| POST | /search/tracks |
Search external track sources |
| POST | /search/albums |
Search external album sources |
| POST | /search/artists |
Search external artist sources |
| Downloads | ||
| GET | /downloads |
List download tasks |
| POST | /downloads/<id>/cancel |
Cancel a download |
| POST | /downloads/cancel-all |
Cancel all downloads |
| Wishlist | ||
| GET | /wishlist |
List wishlist tracks |
| POST | /wishlist |
Add to wishlist |
| DELETE | /wishlist/<track_id> |
Remove from wishlist |
| POST | /wishlist/process |
Trigger processing |
| Watchlist | ||
| GET | /watchlist |
List watched artists |
| POST | /watchlist |
Add artist to watchlist |
| PATCH | /watchlist/<artist_id> |
Update content filters |
| DELETE | /watchlist/<artist_id> |
Remove from watchlist |
| POST | /watchlist/scan |
Trigger scan |
| Discovery | ||
| GET | /discover/pool |
Discovery pool tracks |
| GET | /discover/similar-artists |
Similar artists |
| GET | /discover/recent-releases |
Recent releases |
| GET | /discover/pool/metadata |
Pool metadata |
| GET | /discover/bubbles |
All bubble snapshots |
| GET | /discover/bubbles/<type> |
Specific bubble snapshot |
| Profiles | ||
| GET | /profiles |
List all profiles |
| GET | /profiles/<id> |
Profile detail |
| POST | /profiles |
Create profile |
| PUT | /profiles/<id> |
Update profile |
| DELETE | /profiles/<id> |
Delete profile |
| Retag Queue | ||
| GET | /retag/groups |
List retag groups |
| GET | /retag/groups/<id> |
Retag group detail + tracks |
| DELETE | /retag/groups/<id> |
Delete retag group |
| DELETE | /retag/groups |
Clear all retag groups |
| GET | /retag/stats |
Retag queue statistics |
| ListenBrainz | ||
| GET | /listenbrainz/playlists |
List cached playlists |
| GET | /listenbrainz/playlists/<id> |
Playlist detail + tracks |
| Cache | ||
| GET | /cache/musicbrainz |
Browse MusicBrainz cache |
| GET | /cache/musicbrainz/stats |
MusicBrainz cache statistics |
| GET | /cache/discovery-matches |
Browse discovery match cache |
| GET | /cache/discovery-matches/stats |
Discovery match cache statistics |
| Playlists | ||
| GET | /playlists |
List playlists |
| GET | /playlists/<id> |
Playlist detail + tracks |
| POST | /playlists/<id>/sync |
Trigger playlist sync |
| Video | ||
| GET | /video/library |
Movies or shows in the library |
| GET | /video/library/genres |
Video genres |
| GET | /video/search |
TMDB multi-search |
| GET | /video/trending |
Trending titles |
| GET | /video/wishlist |
Wishlist page or counts |
| GET | /video/wishlist/counts |
Wishlist counts |
| POST | /video/wishlist |
Add a movie or episodes |
| DELETE | /video/wishlist |
Remove at any scope |
| GET | /video/watchlist |
Followed shows / people / studios |
| POST | /video/watchlist |
Follow |
| DELETE | /video/watchlist |
Unfollow |
| POST | /video/scan |
Ask for a library scan |
| GET | /video/scan/status |
Scan progress |
| GET | /video/stats |
Library, database, download and wishlist counts |
| GET | /video/downloads |
Downloads (?status= filter) |
| GET | /video/downloads/status |
Download status |
| GET | /video/downloads/history |
Download history |
| GET | /video/calendar |
Upcoming / recent releases |
| GET | /video/requests |
Requests |
| POST | /video/requests |
File a request |
| POST | /video/requests/<id>/approve |
Approve |
| POST | /video/requests/<id>/deny |
Deny |
| Settings | ||
| GET | /settings |
Get settings (redacted) |
| PATCH | /settings |
Update settings |
| API Keys | ||
| GET | /api-keys |
List API keys |
| POST | /api-keys |
Generate new key |
| DELETE | /api-keys/<id> |
Revoke key |
| POST | /api-keys/bootstrap |
Bootstrap first key (no auth) |