User Library
Access and manage user profiles, saved tracks, albums, and listening history
The User Library endpoints allow you to access user profile information, manage saved content, and retrieve listening history. These features enable personalized experiences based on user preferences and behavior.
Required Scopes
Section titled “Required Scopes”| Scope | Access |
|---|---|
user-read-private | User profile, subscription level |
user-read-email | User email address |
user-library-read | Saved tracks, albums, shows, audiobooks |
user-library-modify | Save/remove tracks, albums, shows, audiobooks |
user-top-read | Top artists and tracks |
user-read-recently-played | Recently played tracks |
user-follow-read | Followed artists and users |
user-follow-modify | Follow/unfollow artists and users |
User Profile
Section titled “User Profile”Get Current User Profile
Section titled “Get Current User Profile”import Spotted from "spotted-ts";
const client = new Spotted({ bearerToken: userAccessToken });
const user = await client.me.retrieve();console.log(` Display Name: ${user.display_name} Email: ${user.email} Country: ${user.country} Product: ${user.product} // "premium", "free", etc. Followers: ${user.followers.total}`);Get Another User’s Profile
Section titled “Get Another User’s Profile”const profile = await client.users.retrieve("spotify");Saved Tracks
Section titled “Saved Tracks”Get Saved Tracks
Section titled “Get Saved Tracks”// Get all saved tracks using async iteratorconst savedTracks = [];for await (const item of client.me.tracks.list({ market: "US" })) { savedTracks.push(item);}
console.log(`You have ${savedTracks.length} saved tracks`);
// Each item contains: added_at, tracksavedTracks.forEach((item) => { console.log(`${item.track.name} - saved on ${item.added_at}`);});Save Tracks
Section titled “Save Tracks”// Save multiple tracks (max 50 per request)await client.me.tracks.save({ ids: ["4iV5W9uYEdYUVa79Axb7Rh", "1301WleyT98MSxVHPZCA6M"],});Remove Saved Tracks
Section titled “Remove Saved Tracks”await client.me.tracks.remove({ ids: ["4iV5W9uYEdYUVa79Axb7Rh"],});Check Saved Tracks
Section titled “Check Saved Tracks”const saved = await client.me.tracks.check({ ids: "trackId1,trackId2",});// [true, false] - first track is saved, second is notSaved Albums
Section titled “Saved Albums”Get Saved Albums
Section titled “Get Saved Albums”// Get all saved albums using async iteratorfor await (const item of client.me.albums.list({ market: "US" })) { console.log(`${item.album.name} by ${item.album.artists[0].name}`);}Save and Remove Albums
Section titled “Save and Remove Albums”// Save albumsawait client.me.albums.save({ ids: ["albumId1", "albumId2"] });
// Remove albumsawait client.me.albums.remove({ ids: ["albumId1"] });
// Check if albums are savedconst saved = await client.me.albums.check({ ids: "albumId1,albumId2" });// Returns [true, false]Saved Audiobooks
Section titled “Saved Audiobooks”Audiobooks are available within the US, UK, Canada, Ireland, New Zealand, and Australia markets.
Get Saved Audiobooks
Section titled “Get Saved Audiobooks”// Get all saved audiobooks using async iteratorfor await (const item of client.me.audiobooks.list({ market: "US" })) { console.log(`${item.name} by ${item.authors[0].name}`);}Save and Remove Audiobooks
Section titled “Save and Remove Audiobooks”// Save audiobooksawait client.me.audiobooks.save({ ids: ["audiobookId1", "audiobookId2"] });
// Remove audiobooksawait client.me.audiobooks.remove({ ids: ["audiobookId1"] });
// Check if audiobooks are savedconst saved = await client.me.audiobooks.check({ ids: "audiobookId1,audiobookId2",});// Returns [true, false]Top Artists and Tracks
Section titled “Top Artists and Tracks”Get the user’s most listened to artists and tracks over different time periods.
Time Ranges
Section titled “Time Ranges”Approximately the last 4 weeks of listening history
Approximately the last 6 months
Several years of listening history
Get Top Artists
Section titled “Get Top Artists”// Get top artists for different time periodsconst recentTopArtists = [];for await (const artist of client.me.top.listArtists({ time_range: "short_term", limit: 50,})) { recentTopArtists.push(artist); if (recentTopArtists.length >= 50) break;}
console.log("Your recent top artists:");recentTopArtists.forEach((artist, i) => { console.log( `${i + 1}. ${artist.name} (${artist.genres.slice(0, 2).join(", ")})`, );});Get Top Tracks
Section titled “Get Top Tracks”// Get top tracks for a time rangeconst topTracks = [];for await (const track of client.me.top.listTracks({ time_range: "medium_term", limit: 50,})) { topTracks.push(track); if (topTracks.length >= 50) break;}
// Compare top tracks across time periodsasync function analyzeListeningTrends() { const getTop20 = async (timeRange) => { const tracks = []; for await (const track of client.me.top.listTracks({ time_range: timeRange, limit: 20, })) { tracks.push(track); if (tracks.length >= 20) break; } return tracks; };
const [shortTerm, mediumTerm, longTerm] = await Promise.all([ getTop20("short_term"), getTop20("medium_term"), getTop20("long_term"), ]);
// Find consistent favorites (in all three lists) const shortTermIds = new Set(shortTerm.map((t) => t.id)); const mediumTermIds = new Set(mediumTerm.map((t) => t.id));
const consistentFavorites = longTerm.filter( (track) => shortTermIds.has(track.id) && mediumTermIds.has(track.id), );
return { recentFavorites: shortTerm, consistentFavorites, allTimeFavorites: longTerm, };}Recently Played
Section titled “Recently Played”Track what the user has been listening to recently.
// Get recently played tracksconst recent = await client.me.player.listRecentlyPlayedTracks({ limit: 50 });
recent.items.forEach((item) => { const playedAt = new Date(item.played_at); console.log(`${item.track.name} - played at ${playedAt.toLocaleString()}`);});
// Get tracks played after a specific timestamp (Unix ms)const oneHourAgo = Date.now() - 60 * 60 * 1000;const lastHour = await client.me.player.listRecentlyPlayedTracks({ after: oneHourAgo,});Build Listening History Analysis
Section titled “Build Listening History Analysis”async function analyzeListeningHistory() { const recent = await client.me.player.listRecentlyPlayedTracks({ limit: 50 });
// Count plays per artist const artistCounts = {}; const hourlyDistribution = new Array(24).fill(0);
recent.items.forEach((item) => { // Count by artist const artistName = item.track.artists[0].name; artistCounts[artistName] = (artistCounts[artistName] || 0) + 1;
// Count by hour const hour = new Date(item.played_at).getHours(); hourlyDistribution[hour]++; });
// Sort artists by play count const topArtists = Object.entries(artistCounts) .sort((a, b) => b[1] - a[1]) .slice(0, 5);
// Find peak listening hour const peakHour = hourlyDistribution.indexOf(Math.max(...hourlyDistribution));
return { totalPlays: recent.items.length, topArtists, peakListeningHour: peakHour, hourlyDistribution, };}Following Artists
Section titled “Following Artists”Get Followed Artists
Section titled “Get Followed Artists”// Get all followed artists using async iteratorconst followedArtists = [];for await (const artist of client.me.following.list({ type: "artist" })) { followedArtists.push(artist);}
console.log(`Following ${followedArtists.length} artists`);Follow/Unfollow Artists
Section titled “Follow/Unfollow Artists”// Follow artistsawait client.me.following.follow({ type: "artist", ids: "artistId1,artistId2",});
// Unfollow artistsawait client.me.following.unfollow({ type: "artist", ids: "artistId1",});
// Check if following specific artistsconst isFollowing = await client.me.following.check({ type: "artist", ids: "artistId1,artistId2",});// Returns [true, false]Complete Library Manager
Section titled “Complete Library Manager”Here’s a comprehensive example for managing user library operations using the SDK:
import Spotted from "spotted-ts";
const client = new Spotted({ bearerToken: userAccessToken });
// Get library statisticsasync function getLibraryStats() { // Get counts from first page of each resource const [tracksPage, albumsPage, audiobooksPage] = await Promise.all([ client.me.tracks.list({ limit: 1 }), client.me.albums.list({ limit: 1 }), client.me.audiobooks.list({ limit: 1 }), ]);
// Get top artists for genre analysis const topArtists = []; for await (const artist of client.me.top.listArtists({ time_range: "long_term", limit: 50, })) { topArtists.push(artist); if (topArtists.length >= 50) break; }
// Calculate genre distribution const genreCounts = {}; topArtists.forEach((artist) => { artist.genres.forEach((genre) => { genreCounts[genre] = (genreCounts[genre] || 0) + 1; }); });
const topGenres = Object.entries(genreCounts) .sort((a, b) => b[1] - a[1]) .slice(0, 10) .map(([genre]) => genre);
return { savedTracksCount: tracksPage.total, savedAlbumsCount: albumsPage.total, savedAudiobooksCount: audiobooksPage.total, topGenres, topArtist: topArtists[0]?.name, };}
// Export library to JSONasync function exportLibrary() { const tracks = []; for await (const item of client.me.tracks.list()) { tracks.push({ id: item.track.id, name: item.track.name, artist: item.track.artists[0].name, album: item.track.album.name, addedAt: item.added_at, }); }
const albums = []; for await (const item of client.me.albums.list()) { albums.push({ id: item.album.id, name: item.album.name, artist: item.album.artists[0].name, addedAt: item.added_at, }); }
const audiobooks = []; for await (const item of client.me.audiobooks.list()) { audiobooks.push({ id: item.id, name: item.name, author: item.authors[0].name, addedAt: item.added_at, }); }
return { exportedAt: new Date().toISOString(), tracks, albums, audiobooks };}
// Usageconst stats = await getLibraryStats();console.log(`Library Stats:- ${stats.savedTracksCount} saved tracks- ${stats.savedAlbumsCount} saved albums- ${stats.savedAudiobooksCount} saved audiobooks- Top artist: ${stats.topArtist}- Top genres: ${stats.topGenres.slice(0, 3).join(", ")}`);
// Export entire libraryconst backup = await exportLibrary();console.log( `Exported ${backup.tracks.length} tracks, ${backup.albums.length} albums, and ${backup.audiobooks.length} audiobooks`,);Sync Library Across Accounts
Section titled “Sync Library Across Accounts”Example of syncing saved tracks between accounts using the SDK:
async function syncLibraries(sourceClient, targetClient, options = {}) { const { dryRun = false } = options;
const results = { added: 0, skipped: 0 };
// Get all source tracks const sourceTracks = []; for await (const item of sourceClient.me.tracks.list()) { sourceTracks.push(item.track.id); }
// Check and sync in batches of 50 for (let i = 0; i < sourceTracks.length; i += 50) { const batch = sourceTracks.slice(i, i + 50); const savedStatus = await targetClient.me.tracks.check({ ids: batch.join(","), });
const toSave = batch.filter((_, idx) => !savedStatus[idx]);
if (toSave.length > 0 && !dryRun) { await targetClient.me.tracks.save({ ids: toSave }); }
results.added += toSave.length; results.skipped += batch.length - toSave.length; }
return results;}Next Steps
Section titled “Next Steps”- Playlist Management - Create playlists from your library
- Audio Analysis - Analyze your music taste
- Playback Control - Control what’s playing