Skip to content
DocumentationAPI Reference

Playlist Management

Create, modify, and manage Spotify playlists programmatically

Playlists are central to the Spotify experience. This guide covers everything from creating playlists to advanced operations like reordering tracks, managing collaborators, and building playlist generators.

Different operations require different OAuth scopes:

ScopeOperations
playlist-read-privateRead private playlists
playlist-read-collaborativeRead collaborative playlists
playlist-modify-publicCreate/edit public playlists
playlist-modify-privateCreate/edit private playlists
ugc-image-uploadUpload custom cover images

import Spotted from "spotted-ts";
const client = new Spotted({ bearerToken: userAccessToken });
// Get playlists with automatic pagination using async iterator
const playlists = [];
for await (const playlist of client.me.playlists.list()) {
playlists.push(playlist);
}
console.log(`You have ${playlists.length} playlists`);
// Or get a single page of playlists
const page = await client.me.playlists.list({ limit: 50 });
console.log(page.items);
// Get full playlist
const playlist = await client.playlists.retrieve("37i9dQZF1DXcBWIGoYBM5M");
console.log(`${playlist.name}: ${playlist.tracks.total} tracks`);
// Get only specific fields (more efficient)
const basicInfo = await client.playlists.retrieve("37i9dQZF1DXcBWIGoYBM5M", {
fields: "name,description,owner.display_name,tracks.total",
});
// Get all tracks using async iterator
const tracks = [];
for await (const item of client.playlists.tracks.list(
"37i9dQZF1DXcBWIGoYBM5M",
{
market: "US",
},
)) {
tracks.push(item.track);
}
console.log(`Playlist has ${tracks.length} tracks`);
// Get single page of tracks with specific fields
const page = await client.playlists.tracks.list("37i9dQZF1DXcBWIGoYBM5M", {
limit: 100,
fields: "items(track(id,name,artists,album,duration_ms)),next",
});

// Create a public playlist
const playlist = await client.users.playlists.create("user123", {
name: "My Awesome Playlist",
description: "A collection of my favorite tracks",
public: true,
});
console.log(`Created playlist: ${playlist.id}`);
// Create a private collaborative playlist
const collab = await client.users.playlists.create("user123", {
name: "Collaborative Mix",
public: false,
collaborative: true,
});
// Create playlist and add tracks
const playlist = await client.users.playlists.create("user123", {
name: "Road Trip Mix",
description: "Songs for the journey",
});
const trackUris = [
"spotify:track:4iV5W9uYEdYUVa79Axb7Rh",
"spotify:track:1301WleyT98MSxVHPZCA6M",
// ... more track URIs
];
// Add tracks (handles batching automatically for large lists)
await client.playlists.tracks.add(playlist.id, {
uris: trackUris,
});

// Add tracks to the end
await client.playlists.tracks.add("playlist123", {
uris: [
"spotify:track:4iV5W9uYEdYUVa79Axb7Rh",
"spotify:track:1301WleyT98MSxVHPZCA6M",
],
});
// Add tracks at a specific position (0 = beginning)
await client.playlists.tracks.add("playlist123", {
uris: ["spotify:track:newTrackId"],
position: 0,
});
// Remove specific tracks
await client.playlists.tracks.remove("playlist123", {
tracks: [{ uri: "spotify:track:4iV5W9uYEdYUVa79Axb7Rh" }],
});
// Remove multiple tracks
await client.playlists.tracks.remove("playlist123", {
tracks: [{ uri: "spotify:track:track1" }, { uri: "spotify:track:track2" }],
});
// Remove the second occurrence of a track (position 5)
await client.playlists.tracks.remove("playlist123", {
tracks: [{ uri: "spotify:track:duplicateTrack", positions: [5] }],
snapshot_id: "snapshotId123",
});
// Move track from position 0 to position 5
await client.playlists.tracks.update("playlist123", {
range_start: 0,
insert_before: 6,
});
// Move tracks 3-5 to the beginning
await client.playlists.tracks.update("playlist123", {
range_start: 3,
insert_before: 0,
range_length: 3,
});
// Replace all tracks (clears playlist first)
await client.playlists.tracks.update("playlist123", {
uris: ["spotify:track:newTrack1", "spotify:track:newTrack2"],
});

// Update name and description
await client.playlists.update("playlist123", {
name: "Updated Playlist Name",
description: "New description for the playlist",
});
// Make playlist private
await client.playlists.update("playlist123", { public: false });
// Enable collaborative mode (must be private)
await client.playlists.update("playlist123", {
public: false,
collaborative: true,
});

Upload a custom cover image for your playlist:

import { readFileSync } from "fs";
// Image must be JPEG and base64 encoded, max 256KB
const imageBuffer = readFileSync("cover.jpg");
const base64Image = imageBuffer.toString("base64");
await client.playlists.images.upload("playlist123", base64Image);

async function shufflePlaylist(playlistId) {
// Get all tracks
const tracks = [];
for await (const item of client.playlists.tracks.list(playlistId)) {
tracks.push(item.track);
}
const trackUris = tracks.map((t) => `spotify:track:${t.id}`);
// Fisher-Yates shuffle
for (let i = trackUris.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[trackUris[i], trackUris[j]] = [trackUris[j], trackUris[i]];
}
// Replace with shuffled order
await client.playlists.tracks.update(playlistId, {
uris: trackUris.slice(0, 100),
});
// Add remaining tracks if more than 100
for (let i = 100; i < trackUris.length; i += 100) {
await client.playlists.tracks.add(playlistId, {
uris: trackUris.slice(i, i + 100),
});
}
}
async function removeDuplicates(playlistId) {
const tracks = [];
for await (const item of client.playlists.tracks.list(playlistId)) {
tracks.push(item.track);
}
const seen = new Set();
const duplicatePositions = [];
tracks.forEach((track, index) => {
if (seen.has(track.id)) {
duplicatePositions.push(index);
} else {
seen.add(track.id);
}
});
if (duplicatePositions.length === 0) {
console.log("No duplicates found");
return;
}
// Get playlist snapshot
const playlist = await client.playlists.retrieve(playlistId, {
fields: "snapshot_id",
});
let snapshotId = playlist.snapshot_id;
// Remove duplicates by position (from end to avoid index shifting)
for (let i = duplicatePositions.length - 1; i >= 0; i--) {
const position = duplicatePositions[i];
const trackUri = `spotify:track:${tracks[position].id}`;
const result = await client.playlists.tracks.remove(playlistId, {
tracks: [{ uri: trackUri, positions: [position] }],
snapshot_id: snapshotId,
});
snapshotId = result.snapshot_id;
}
console.log(`Removed ${duplicatePositions.length} duplicates`);
}
async function sortPlaylistByArtist(playlistId) {
const tracks = [];
for await (const item of client.playlists.tracks.list(playlistId)) {
tracks.push(item.track);
}
// Sort by primary artist name
const sortedTracks = [...tracks].sort((a, b) => {
const artistA = a.artists[0]?.name.toLowerCase() || "";
const artistB = b.artists[0]?.name.toLowerCase() || "";
return artistA.localeCompare(artistB);
});
const trackUris = sortedTracks.map((t) => `spotify:track:${t.id}`);
// Replace with sorted tracks
await client.playlists.tracks.update(playlistId, {
uris: trackUris.slice(0, 100),
});
for (let i = 100; i < trackUris.length; i += 100) {
await client.playlists.tracks.add(playlistId, {
uris: trackUris.slice(i, i + 100),
});
}
}

Create playlists based on specific criteria using the SDK:

import Spotted from "spotted-ts";
const client = new Spotted({ bearerToken: userAccessToken });
// Generate playlist from user's top tracks
async function createTopTracksPlaylist(userId, timeRange = "medium_term") {
// Get user's top tracks
const topTracks = [];
for await (const track of client.me.top.listTracks({
time_range: timeRange,
limit: 50,
})) {
topTracks.push(track);
if (topTracks.length >= 50) break;
}
// Create playlist
const playlist = await client.users.playlists.create(userId, {
name: `My Top Tracks (${timeRange.replace("_", " ")})`,
description: "Generated from your most played tracks",
});
// Add tracks
await client.playlists.tracks.add(playlist.id, {
uris: topTracks.map((t) => t.uri),
});
return playlist;
}
// Generate playlist from an artist's discography
async function createArtistDiscographyPlaylist(
userId,
artistId,
tracksPerAlbum = 5,
) {
const artist = await client.artists.retrieve(artistId);
const trackUris = [];
for await (const album of client.artists.listAlbums(artistId, {
include_groups: "album,single",
})) {
let count = 0;
for await (const track of client.albums.listTracks(album.id)) {
if (count >= tracksPerAlbum) break;
trackUris.push(track.uri);
count++;
}
}
const playlist = await client.users.playlists.create(userId, {
name: `${artist.name} - Complete Discography`,
description: `All tracks from ${artist.name}'s albums and singles`,
});
// Add tracks in batches
for (let i = 0; i < trackUris.length; i += 100) {
await client.playlists.tracks.add(playlist.id, {
uris: trackUris.slice(i, i + 100),
});
}
return playlist;
}
// Generate workout playlist based on BPM
async function createWorkoutPlaylist(userId, targetBpm = 140, tolerance = 10) {
const recs = await client.recommendations.list({
seed_genres: "workout,electronic,hip-hop",
target_tempo: targetBpm,
min_tempo: targetBpm - tolerance,
max_tempo: targetBpm + tolerance,
min_energy: 0.7,
limit: 50,
});
const playlist = await client.users.playlists.create(userId, {
name: `Workout Mix (${targetBpm} BPM)`,
description: `High-energy tracks around ${targetBpm} BPM`,
});
await client.playlists.tracks.add(playlist.id, {
uris: recs.tracks.map((t) => t.uri),
});
return playlist;
}
// Generate mood-based playlist
async function createMoodPlaylist(userId, mood) {
const moodSettings = {
happy: {
target_valence: 0.8,
target_energy: 0.7,
seed_genres: "pop,dance",
},
sad: {
target_valence: 0.2,
target_energy: 0.3,
seed_genres: "acoustic,indie",
},
energetic: {
target_energy: 0.9,
target_danceability: 0.8,
seed_genres: "electronic,dance",
},
chill: {
target_energy: 0.3,
target_acousticness: 0.6,
seed_genres: "ambient,chill",
},
focus: {
target_instrumentalness: 0.8,
target_energy: 0.5,
seed_genres: "classical,ambient",
},
party: {
target_danceability: 0.9,
target_energy: 0.8,
seed_genres: "dance,party,edm",
},
};
const settings = moodSettings[mood] || moodSettings.happy;
const recs = await client.recommendations.list({ ...settings, limit: 50 });
const playlist = await client.users.playlists.create(userId, {
name: `${mood.charAt(0).toUpperCase() + mood.slice(1)} Vibes`,
description: `A ${mood} playlist generated just for you`,
});
await client.playlists.tracks.add(playlist.id, {
uris: recs.tracks.map((t) => t.uri),
});
return playlist;
}
// Usage
const topPlaylist = await createTopTracksPlaylist("user123", "long_term");
const discography = await createArtistDiscographyPlaylist(
"user123",
"06HL4z0CvFAxyc27GXpf02",
);
const workout = await createWorkoutPlaylist("user123", 150);
const chillPlaylist = await createMoodPlaylist("user123", "chill");

// Check if users follow the playlist
const follows = await client.playlists.followers.check("playlist123", {
ids: "user1,user2",
});
// Returns [true, false]
// Follow a playlist (add to library)
await client.playlists.followers.follow("playlist123", { public: true });
// Unfollow a playlist (remove from library)
await client.playlists.followers.unfollow("playlist123");