Skip to content

Instantly share code, notes, and snippets.

@ritmo-v0
Last active July 14, 2025 00:49
Show Gist options
  • Select an option

  • Save ritmo-v0/a847d54c3e2637df25416713b85ea3ab to your computer and use it in GitHub Desktop.

Select an option

Save ritmo-v0/a847d54c3e2637df25416713b85ea3ab to your computer and use it in GitHub Desktop.
A simple TypeScript example of Spotify's `search` Web API.
import { readFile } from "fs/promises";
// Types & Interfaces
type Song = SongData & {
search_result: SearchResult[];
};
type SongData = {
name: string;
artist: string;
};
// # Modify based on your needs
type SearchResult = Omit<TrackItem, "type" | "artists" | "duration_ms" | "external_urls"> & {
artists: Array<string>;
};
// # Add more keys based on your needs
// Reference: https://developer.spotify.com/documentation/web-api/reference/search
type SpotifySearchResponse = {
tracks: {
items: Array<TrackItem>;
}
};
type TrackItem = {
id: string;
name: string;
type: "track";
artists: Array<SimplifiedArtist>;
uri: string;
href: string;
duration_ms: number;
external_urls: Record<string, string>;
};
type SimplifiedArtist = {
id: string;
name: string;
type: "artist";
uri: string;
href: string;
external_urls: Record<string, string>;
};
type SearchOptions = {
limit?: number;
};
// Constants & Variables
// ! Plz make sure to load secrets from .env* files other than local usage
const CLIENT_ID = "YOUR_SPOTIFY_CLIENT_ID";
const CLIENT_SECRET = "YOUR_SPOTIFY_CLIENT_SECRET";
const BEARER_TOKEN = "YOUR_BEARER_TOKEN_FROM_getAccessToken";
// Request an Access Token
// Reference: https://developer.spotify.com/documentation/web-api/tutorials/getting-started#request-an-access-token
async function getAccessToken() {
const token = await fetch("https://accounts.spotify.com/api/token", {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
grant_type: "client_credentials",
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
})
}).then(res => res.json());
return token.access_token;
}
// # Write your own data loading logic here
async function getSongData(): Promise<Record<string, SongData>> {
const data = await readFile("RITMOS_SECRET_LIBRARY.json", "utf8");
return JSON.parse(data);
}
// Fetch result from `Search` Web API
// ! You might consider adding a delay between (batch) searches to avoid 429s
async function fetchSongSearchResult(song: SongData, {
limit = 3,
}: SearchOptions = {}): Promise<Array<TrackItem>> {
const name = song.name;
const artist = song.artist;
const fetchURL = "https://api.spotify.com/v1/search?" + new URLSearchParams({
q: `track:${name} artist:${artist}`,
type: "track",
limit: String(limit),
}).toString();
const res = await fetch(fetchURL, {
headers: { "Authorization": `Bearer ${BEARER_TOKEN}` },
});
if (!res.ok) throw new Error(`HTTP ${res.status} - ${res.statusText}`);
const data: SpotifySearchResponse = await res.json();
return data.tracks.items;
}
// # Example Usage
// In this example, I have the order No. from "Ritmo's Secret Library" as the key of each SongData object,
// hence the return type `Record<string, SongData>` of `getSongData()`.
try {
const songs = await getSongData();
for (const [order, song] of Object.entries(songs)) {
console.log(`🔍 Searching for song ${order.padStart(3, "0")}: ${song.name} / ${song.artist}...`);
const trackItems = await fetchSongSearchResult(song);
const fullSongData: Song = {
...song,
search_result: trackItems.map(item => ({
id: item.id,
name: item.name,
artists: item.artists.map(artist => artist.name),
uri: item.uri,
href: item.external_urls.spotify,
})),
};
console.log(`${JSON.stringify(fullSongData, null, 2)}\n`);
}
} catch (error) {
console.error("ERR:", error);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment