Skip to content

Personalization

The Personalization service retrieves a user’s top artists and tracks based on listening history.

Top Artists

Get user’s top artists

import { Effect } from "effect";
import { Personalization, makeSpotifyLayer } from "@spotify-effect/core";
const program = Effect.gen(function* () {
const personalization = yield* Personalization;
const topArtists = yield* personalization.getMyTopArtists({
limit: 20,
time_range: "medium_term", // Last 6 months
});
for (const artist of topArtists.items) {
console.log(`${artist.name}${artist.genres.slice(0, 2).join(", ")}`);
}
});

Time ranges

ValueDescription
'short_term'Last 4 weeks
'medium_term'Last 6 months
'long_term'Last several years

Top Tracks

Get user’s top tracks

const program = Effect.gen(function* () {
const personalization = yield* Personalization;
const topTracks = yield* personalization.getMyTopTracks({
limit: 20,
time_range: "medium_term",
});
for (const track of topTracks.items) {
console.log(`${track.name}${track.artists[0]?.name}`);
}
});

Practical Examples

Generate a playlist from top tracks

import { Effect } from "effect";
import { Personalization, Playlists, Users } from "@spotify-effect/core";
const generateTopTracksPlaylist = (userId: string, name: string) =>
Effect.gen(function* () {
const personalization = yield* Personalization;
const playlists = yield* Playlists;
const [shortTerm, mediumTerm, longTerm] = yield* Effect.all([
personalization.getMyTopTracks({ limit: 10, time_range: "short_term" }),
personalization.getMyTopTracks({ limit: 20, time_range: "medium_term" }),
personalization.getMyTopTracks({ limit: 20, time_range: "long_term" }),
]);
const trackMap = new Map<string, Track>();
longTerm.items.forEach((t, i) => trackMap.set(t.id, t));
mediumTerm.items.forEach((t, i) => trackMap.set(t.id, t));
shortTerm.items.forEach((t, i) => trackMap.set(t.id, t));
const uniqueTracks = [...trackMap.values()].slice(0, 50);
const playlist = yield* playlists.createPlaylist(userId, name, {
description: "Your top tracks across all time",
public: false,
});
const uris = uniqueTracks.map((t) => t.uri);
for (let i = 0; i < uris.length; i += 100) {
yield* playlists.addItemsToPlaylist(playlist.id, uris.slice(i, i + 100));
}
return playlist;
});

Compare listening across time periods

const compareTimePeriods = () =>
Effect.gen(function* () {
const personalization = yield* Personalization;
const [short, medium, long] = yield* Effect.all([
personalization.getMyTopTracks({ limit: 10, time_range: "short_term" }),
personalization.getMyTopTracks({ limit: 10, time_range: "medium_term" }),
personalization.getMyTopTracks({ limit: 10, time_range: "long_term" }),
]);
const shortArtists = new Set(short.items.flatMap((t) => t.artists.map((a) => a.id)));
const mediumArtists = new Set(medium.items.flatMap((t) => t.artists.map((a) => a.id)));
const longArtists = new Set(long.items.flatMap((t) => t.artists.map((a) => a.id)));
return {
recentDiscoveries: [...shortArtists].filter((id) => !longArtists.has(id)),
timeless: [...longArtists].filter((id) => shortArtists.has(id)),
alwaysListening: [...longArtists].filter((id) => mediumArtists.has(id)),
};
});

Build genre analysis from top artists

const analyzeTopGenres = () =>
Effect.gen(function* () {
const personalization = yield* Personalization;
const topArtists = yield* personalization.getMyTopArtists({
limit: 50,
time_range: "medium_term",
});
const genreCounts = new Map<string, number>();
const artistGenres = new Map<string, string[]>();
for (const artist of topArtists.items) {
artistGenres.set(artist.id, artist.genres);
for (const genre of artist.genres) {
genreCounts.set(genre, (genreCounts.get(genre) ?? 0) + 1);
}
}
const topGenres = [...genreCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 10);
return {
topGenres,
artistGenres,
};
});

Discover music similar to top artists

import { Artists, Browse, Personalization } from "@spotify-effect/core";
const discoverFromTopArtists = () =>
Effect.gen(function* () {
const personalization = yield* Personalization;
const artists = yield* Artists;
const browse = yield* Browse;
const topArtists = yield* personalization.getMyTopArtists({
limit: 5,
time_range: "medium_term",
});
const artistIds = topArtists.items.map((a) => a.id);
const recommendations = yield* browse.getRecommendations({
seed_artists: artistIds.slice(0, 3),
limit: 30,
});
return {
basedOn: topArtists.items.map((a) => a.name),
recommendations: recommendations.tracks,
};
});

Pagination

import { paginateAll } from "@spotify-effect/core";
const getAllTopArtists = (timeRange: "short_term" | "medium_term" | "long_term") =>
Effect.gen(function* () {
const personalization = yield* Personalization;
return yield* paginateAll(
(offset, limit) => personalization.getMyTopArtists({ offset, limit, time_range: timeRange }),
50,
);
});

Scopes Required

OperationRequired Scope
getMyTopArtistsuser-top-read
getMyTopTracksuser-top-read

Next steps