Odesli.js
A lightweight Node.js wrapper for the Odesli (Songlink) API – easily retrieve smart music links, metadata, and sharing info across all major streaming platforms.
Install / Use
/learn @MattrAus/Odesli.jsREADME
odesli.js
A Node.js client for the Odesli API (formerly song.link/album.link) that helps you find links to music across multiple streaming platforms.
📋 Table of Contents
- odesli.js
Features
- 🔗 Cross-platform links: Get links for music across Spotify, Apple Music, YouTube Music, and more
- 🎵 Song & Album support: Works with both individual tracks and full albums
- 🌍 Multi-country support: Specify country codes for region-specific results
- 🔑 API Key support: Optional API key for higher rate limits
- 📦 TypeScript support: Full TypeScript definitions included
- ⚡ Lightweight: Minimal dependencies, fast performance
- 🛡️ Robust error handling: Comprehensive error messages and validation
- 🧪 Fully tested: 100% test coverage with comprehensive test suite
Installation
From npm
npm install odesli.js
Quick Start
CommonJS (require):
const Odesli = require('odesli.js');
// Initialize without API key (10 requests/minute limit)
const odesli = new Odesli();
// Or with API key for higher limits
// const odesli = new Odesli({
// apiKey: 'your-api-key-here',
// version: 'v1-alpha.1', // optional, defaults to v1-alpha.1
// });
// You can also disable the metrics collector if you don't need it
// const odesliLight = new Odesli({ metrics: false });
// Fetch a song by URL (this is the actual working example)
(async () => {
try {
const song = await odesli.fetch(
'https://open.spotify.com/track/4Km5HrUvYTaSUfiSGPJeQR'
);
console.log(`🎵 ${song.title} by ${song.artist.join(', ')}`);
console.log(`🔗 Song.link: ${song.pageUrl}`);
} catch (error) {
console.error('❌ Error:', error.message);
}
})();
ESM (import):
import Odesli from 'odesli.js';
// Initialize without API key (10 requests/minute limit)
const odesli = new Odesli();
// Or with API key for higher limits
// const odesli = new Odesli({
// apiKey: 'your-api-key-here',
// version: 'v1-alpha.1', // optional, defaults to v1-alpha.1
// });
// You can also disable the metrics collector if you don't need it
// const odesliLight = new Odesli({ metrics: false });
// Fetch a song by URL (this is the actual working example)
try {
const song = await odesli.fetch(
'https://open.spotify.com/track/4Km5HrUvYTaSUfiSGPJeQR'
);
console.log(`🎵 ${song.title} by ${song.artist.join(', ')}`);
console.log(`🔗 Song.link: ${song.pageUrl}`);
} catch (error) {
console.error('❌ Error:', error.message);
}
API Key
An API key is optional but recommended for production use. Without an API key, you're limited to 10 requests per minute.
To get an API key, email developers@song.link.
Usage
Basic Usage
CommonJS (require):
const Odesli = require('odesli.js');
// Initialize without API key (10 requests/minute limit)
const odesli = new Odesli();
// Or with API key for higher limits
// const odesli = new Odesli({
// apiKey: 'your-api-key-here',
// });
(async () => {
// Fetch a song by URL
const song = await odesli.fetch(
'https://open.spotify.com/track/4Km5HrUvYTaSUfiSGPJeQR'
);
console.log(`${song.title} by ${song.artist.join(', ')}`);
// Fetch multiple songs at once
const urls = [
'https://open.spotify.com/track/4Km5HrUvYTaSUfiSGPJeQR',
'https://open.spotify.com/track/0V3wPSX9ygBnCm8psDIegu',
];
const songs = await odesli.fetch(urls);
songs.forEach(song => console.log(song.title));
})();
ESM (import):
import Odesli from 'odesli.js';
// Initialize without API key (10 requests/minute limit)
const odesli = new Odesli();
// Or with API key for higher limits
// const odesli = new Odesli({
// apiKey: 'your-api-key-here',
// });
// Fetch a song by URL
const song = await odesli.fetch(
'https://open.spotify.com/track/4Km5HrUvYTaSUfiSGPJeQR'
);
console.log(`${song.title} by ${song.artist.join(', ')}`);
// Fetch multiple songs at once
const urls = [
'https://open.spotify.com/track/4Km5HrUvYTaSUfiSGPJeQR',
'https://open.spotify.com/track/0V3wPSX9ygBnCm8psDIegu',
];
const songs = await odesli.fetch(urls);
songs.forEach(song => console.log(song.title));
Advanced Usage
CommonJS (require):
const Odesli = require('odesli.js');
// Initialize with custom options
const odesli = new Odesli({
apiKey: 'your-api-key-here',
version: 'v1-alpha.1',
cache: true,
timeout: 10000,
maxRetries: 3,
retryDelay: 1000,
headers: { 'User-Agent': 'MyApp/1.0' },
validateParams: true,
logger: (message, level) => console.log(`[${level}] ${message}`),
});
(async () => {
// Fetch with options
const song = await odesli.fetch('https://open.spotify.com/track/123', {
country: 'GB',
skipCache: false,
timeout: 5000,
});
// Batch fetch with concurrency control
const urls = [
'https://open.spotify.com/track/123',
'https://music.apple.com/us/album/test/456?i=789',
'https://www.youtube.com/watch?v=abc123',
];
const songs = await odesli.fetch(urls, {
country: 'US',
concurrency: 3,
skipCache: true,
});
// Handle errors in batch results
songs.forEach((song, index) => {
if (song.error) {
console.log(`Song ${index + 1}: Error - ${song.error}`);
} else {
console.log(`Song ${index + 1}: ${song.title}`);
}
});
})();
ESM (import):
import Odesli from 'odesli.js';
// Initialize with custom options
const odesli = new Odesli({
apiKey: 'your-api-key-here',
version: 'v1-alpha.1',
cache: true,
timeout: 10000,
maxRetries: 3,
retryDelay: 1000,
headers: { 'User-Agent': 'MyApp/1.0' },
validateParams: true,
logger: (message, level) => console.log(`[${level}] ${message}`),
});
// Fetch with options
const song = await odesli.fetch(
'https://open.spotify.com/track/4Km5HrUvYTaSUfiSGPJeQR',
{
country: 'GB',
skipCache: false,
timeout: 5000,
}
);
// Batch fetch with concurrency control
const urls = [
'https://open.spotify.com/track/4Km5HrUvYTaSUfiSGPJeQR',
'https://open.spotify.com/track/0V3wPSX9ygBnCm8psDIegu',
'https://open.spotify.com/track/3n3Ppam7vgaVa1iaRUc9Lp',
];
const songs = await odesli.fetch(urls, {
country: 'US',
concurrency: 3,
skipCache: true,
});
// Handle errors in batch results
songs.forEach((song, index) => {
if (song.error) {
console.log(`Song ${index + 1}: Error - ${song.error}`);
} else {
console.log(`Song ${index + 1}: ${song.title}`);
}
});
Response Format
All methods return a response object with the following structure:
{
entityUniqueId: "SPOTIFY_SONG::4Km5HrUvYTaSUfiSGPJeQR",
title: "Bad and Boujee",
artist: ["Migos"],
type: "song",
thumbnail: "https://i.scdn.co/image/...",
userCountry: "US",
pageUrl: "https://song.link/i/4Km5HrUvYTaSUfiSGPJeQR",
linksByPlatform: {
spotify: {
entityUniqueId: "SPOTIFY_SONG::4Km5HrUvYTaSUfiSGPJeQR",
url: "https://open.spotify.com/track/4Km5HrUvYTaSUfiSGPJeQR",
nativeAppUriMobile: "spotify://track/4Km5HrUvYTaSUfiSGPJeQR",
nativeAppUriDesktop: "spotify://track/4Km5HrUvYTaSUfiSGPJeQR"
},
appleMusic: {
// ... similar structure
}
// ... other platforms
},
entitiesByUniqueId: {
"SPOTIFY_SONG::4Km5HrUvYTaSUfiSGPJeQR": {
id: "4Km5HrUvYTaSUfiSGPJeQR",
type: "song",
title: "Bad and Boujee",
artistName: ["Migos"],
thumbnailUrl: "https://i.scdn.co/image/...",
apiProvider: "spotify",
platforms: ["spotify"]
}
// ... other entities
}
}
Supported Platforms
- Spotify
- Apple Music
- iTunes
- YouTube Music
- YouTube
- Google Play Music
- Pandora
- Deezer
- Tidal
- Amazon Music
- SoundCloud
- Napster
- Yandex Music
- Spinrilla
TypeScript Support
This package includes full TypeScript definitions with strict country code validation:
CommonJS (require):
const Odesli = require('odesli.js');
const odesli = new Odesli({ apiKey: 'your-key' });
(async () => {
// Get all valid country codes and names for UI dropdowns
const countryOptions = Odesli.getCountryOptions();
console.log('Top 3 countries:', countr
