Move liked songs and playlists between Spotify, Tidal and CSV files.
From a web page on your own computer, or from the command line.
| From \ to | CSV | Spotify | Tidal |
|---|---|---|---|
| Spotify | export |
transfer |
|
| Tidal | export |
transfer |
|
| CSV | import |
import |
Music-Sync finds each track by its ISRC, or else by title, artist and length, and never takes a live version for the studio one. Running it twice does not add tracks twice. It only handles playlist data, never audio.
You need Python 3.14 or newer.
git clone https://github.com/Stensel8/Music-Sync
cd Music-Sync
python3 -m venv .venv
source .venv/bin/activate # Windows (PowerShell): .venv\Scripts\Activate.ps1
pip install -e '.[web]'music-sync webOpen http://127.0.0.1:8888. For each service you have not set up yet, the page shows where to create a developer app, what to fill in and where to paste its Client ID. Then log in, and you are ready.
Spotify only works with a Premium account; Tidal and CSV work without it. See Setting up Spotify and Tidal for the details.
The dashboard has three parts: export to CSV, import a CSV, and transfer between the services. While it works it shows each step, how far it is and how long it will still take. The tracks it could not find are listed with the reason, and can be downloaded as a CSV.
music-sync login spotify # once per service
music-sync login tidal
music-sync transfer spotify tidal --sync-favorites # liked songs, to your Tidal favourites
music-sync transfer spotify tidal --liked # liked songs, to a new playlist
music-sync transfer spotify tidal --playlist "Road trip"
music-sync export spotify --liked -o liked.csv
music-sync export tidal --all -o backup/ # liked songs and every playlist
music-sync import tidal liked.csv --playlist "From CSV"
music-sync import tidal albums.csv --albums # artist,album per row, to your favourite albums
music-sync status # what is set up and logged in
music-sync doctor # tries the real APIs| Option | What it does |
|---|---|
--dry-run |
Look everything up, change nothing |
--unmatched FILE |
Save the tracks that were not found to a CSV |
--min-score 0.8 |
How sure a match must be (0 to 1) |
--to-playlist NAME |
The playlist to fill (transfer) |
--to-favorites |
Add to your favourites (liked songs) instead of a playlist (import) |
--albums |
The CSV lists albums, not tracks; add them to your favourites (import) |
-q |
No progress output |
-v (before the command) |
Log every API call, for a bug report |
music-sync --help and music-sync transfer --help show the rest.
Music-Sync needs a developer app for each service, made from your own account. music-sync web walks you through it; these are the same steps.
Important
Since February 2026 Spotify's API only works for apps whose owner has Premium. Without it every request fails with HTTP 403. Someone with Premium can make the app and add you under Settings > User Management. See Spotify's quota modes and migration guide.
- Create an app in the Spotify Developer Dashboard.
- Redirect URI:
http://127.0.0.1:8888/spotify/callback(127.0.0.1, notlocalhost). - Under Which API/SDKs are you planning to use? tick Web API only.
- Copy the Client ID. The client secret is not needed.
Without Premium the dashboard shows this banner, and the Web API cannot be ticked:
- Click Create New App in the TIDAL developer dashboard.
- Redirect URI:
http://127.0.0.1:8888/tidal/callback. - Scopes:
collection.read,collection.write,playlists.read,playlists.writeanduser.read. - Copy the Client ID and the Client Secret. The secret lets Music-Sync search Tidal's catalogue.
The first run creates it and prints where it is:
- Linux and macOS:
~/.config/music-sync/config.toml(a hidden folder: open it by its path) - Windows:
%APPDATA%\music-sync\config.toml
Paste what you copied between the quotes and save. There is no need to restart: Music-Sync reads the file again when it changes.
[spotify]
client_id = "..."
[tidal]
client_id = "..."
client_secret = "..."country = "NL" at the top picks the Tidal catalogue; without it your system's country is used. The environment variables SPOTIFY_CLIENT_ID, TIDAL_CLIENT_ID, TIDAL_CLIENT_SECRET and MUSICSYNC_COUNTRY take priority over the file. Your logins are kept next to it in tokens.json: keep that file private.
A track that is already in the target playlist is recognised without a lookup, so a second run is quick. Any other track is found by its id on the target service (a CSV exported from it), else by its ISRC, else by searching for title and artist, and as a last resort in the tracklist of its album, as spotify_to_tidal does. Each search result gets a score from 0 to 1: half for the title, 0.4 for the artist and 0.1 for the length. From 0.8 on (--min-score) it is a match.
Remaster notes, "feat." parts, spelling and punctuation do not count. Another version does: live, remix, acoustic, instrumental, sped up, "(Taylor's Version)" and the like, and other numbers ("Part 1" is not "Part 2"). A track that is not found says why:
| Reason | What it means |
|---|---|
| not on Tidal | Nothing by this artist came up: probably not in your country's catalogue |
| only other songs on Tidal | The artist is there, this song is not |
| only another version | Only a live version, remix and the like |
| score too low for a match (0.75, needs 0.80) | Close, but not sure enough; often the length differs |
| Column | Contents |
|---|---|
title |
Track title (the only column that is required) |
artists |
Artists, separated by ; |
album |
Album title |
duration_ms |
Length in milliseconds |
isrc |
ISRC, the most reliable way to find a track again |
spotify_uri / tidal_id |
The track's id on that service |
Files must be UTF-8. The column names of other exporters (Track Name, Artist Name(s), ...) work too, and a file without a header row is read as artist,title. With --albums each row is an album: artist,album, as csv2tidal took them.
- Spotify only lets apps read playlists you own or collaborate on, not the ones you follow. Music-Sync skips those, and empty playlists, and says so.
- A transfer to Tidal is fast (20 ISRCs per request); one to Spotify takes a request per track.
- The web interface is for one person on their own computer. It only listens on
127.0.0.1; never expose it to a network. - Tidal's API is young. If something fails, add the output of
music-sync doctor tidalto your issue.
pip install -e . --group dev
pytest
ruff check . && ruff format --check . && pyright
vulture && bandit -r musicsync -llCI runs the same checks, plus pip-audit.
Music-Sync started out as csv2tidal, a small script by Roland Behme (Nugman) that put a list of albums in your Tidal favourites. He took the idea from a gist by RZetko, and the login code from spotify_to_tidal by Tim Rae and its contributors, which we still borrow ideas from. All three ran on python-tidal, started by Thomas Amland and kept alive by morguldir and tehkillerbee. Sten Tijhuis merged csv2tidal with his own Music-Sync prototype into this tool. The colours of the web interface come from daisyUI.
AGPL-3.0, the license Roland chose for csv2tidal.





