API Reference
SongPort API
Turn one music link into links for Spotify, Apple Music, YouTube Music, Deezer, Tidal and SoundCloud, with the same ISRC matching that runs songport.link. One request in, all the platform links out.
Overview
The SongPort API resolves a music link (from Spotify, Apple Music, YouTube Music, Deezer or Tidal) into the matching links on the other platforms and a search link for SoundCloud, plus the track's title, artist and artwork. It's the programmatic version of what the website does when you paste a link.
It's a single JSON endpoint. You send a link, you get back a structured object with a link per platform.
By default the API runs on SongPort's managed platform keys. Your SongPort API key is enough: no Spotify, Apple Music, TIDAL or YouTube setup on your side. If you later want to bring your own credentials (higher volume, your own upstream quota), see Platform credentials.
Base URL
All requests go to:
https://api.songport.link/v1Every endpoint below is relative to that base and uses HTTPS.
Authentication
Requests are authenticated with an API key, sent as a bearer token in the Authorization header:
Authorization: Bearer sp_live_xxxxxxxxxxxxxxxxxxxxYour key is secret and meant for server-to-server use only. Keep it in your backend, never ship it in browser or mobile-app code, where it could be read and reused. There are no CORS headers on the API for exactly this reason. If a key is ever exposed, it can be revoked instantly and a new one issued.
Don't have a key yet? Email me (see Support) and I'll set you up. Access is invite-only for now.
A newly issued key is active immediately. You can call /v1/convert as soon as you accept the invite. An inactive or revoked key returns 401.
Platform credentials
Default: managed keys. Conversions run on SongPort's shared Spotify, Apple Music, TIDAL and YouTube credentials. You do not configure anything. That shared pool has a daily capacity ceiling (see Rate limits & quota), so invite-only stays in place until that capacity grows.
Optional: bring your own keys (BYOK). If you need volume beyond the shared pool, or want to draw on your own upstream quota, tell me and I'll enable the credentials section in your portal (/portal/credentials). Once every required provider is configured, your account skips the shared daily ceiling.
| Platform | What you provide (BYOK) | Notes |
|---|---|---|
deezer | Nothing | Open public endpoint. Always available, no key needed. |
youtubeMusic | A YouTube Data API key | Free from Google Cloud. Matches by title/artist (YouTube has no ISRC). |
spotify | Client ID + secret | Best ISRC coverage. |
appleMusic | MusicKit key (Team ID, Key ID, .p8) | ISRC lookup swept across regional storefronts. Needs a paid Apple Developer account. |
tidal | Client ID + secret | Matches by ISRC only. No text search. |
soundcloud | Nothing | No direct-match lookup. Always returned as a search link (isSearchFallback: true). |
On a BYOK account, a platform you have not configured falls back to the managed key for that provider. Fully managed accounts never see this section in the portal.
Converting a link
POST /v1/convert resolves one music link.
Send a JSON body with these fields:
| Field | Type | Required | Description |
|---|---|---|---|
url | string | yes | A supported music URL (or a spotify: URI). |
platforms | string[] | no | Only return these platforms (e.g. ["spotify", "appleMusic"]). Unknown keys are ignored. |
shorten | boolean | no | When true, return a shareable songport.link short URL (created if this track isn't stored yet). |
Platform keys: spotify, appleMusic, youtubeMusic, deezer, tidal, soundcloud.
As a source the API reads links from Spotify, Apple Music, YouTube Music, Deezer and Tidal. It does not read SoundCloud links: one returns 422, unless SongPort already has that exact link stored.
Album and release links work too: when the source resolves to a release, the response carries a upc (the release-level analogue of isrc) alongside the per-platform links.
The response
A successful call returns 200 with a JSON object:
{
"isrc": "GBARL9300135",
"title": "Never Gonna Give You Up",
"artist": "Rick Astley",
"thumbnail": "https://i.scdn.co/image/ab67616d...",
"platforms": {
"spotify": { "url": "https://open.spotify.com/track/4cOd...", "isSearchFallback": false },
"appleMusic": { "url": "https://music.apple.com/...", "isSearchFallback": true },
"deezer": { "url": "https://www.deezer.com/track/781592622", "isSearchFallback": false }
},
"songportUrl": "https://songport.link/t/rick-astley-never-gonna-give-you-up-w45sq8xg"
}What each field means:
| Field | Type | Description |
|---|---|---|
isrc | string | null | The recording's ISRC when known. |
upc | string | null | The release's UPC, the album-level analogue of isrc. Present when the link resolves to an album/release. |
title | string | Track title. |
artist | string | Artist name. |
thumbnail | string | null | Cover-art image URL. |
platforms | object | A map of platform key → { url, isSearchFallback }. See below. |
songportUrl | string? | A shareable short link. Pass shorten: true to make sure you get one. |
Each entry in platforms has a url and a boolean isSearchFallback:
isSearchFallback: false: a direct deep link to the exact track on that platform.isSearchFallback: true: no direct link was produced, so the URL is a best-effort search on that platform instead. Common reasons: the track isn't on that catalogue, or (on a BYOK account) that platform's credential isn't configured yet.
Examples
A minimal request with cURL:
curl -X POST https://api.songport.link/v1/convert \
-H "Authorization: Bearer sp_live_xxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"url":"https://open.spotify.com/track/4cOdK2wGLETKBW3PvgPWqT"}'The same call from a Node.js backend:
const res = await fetch("https://api.songport.link/v1/convert", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SONGPORT_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
url: "https://open.spotify.com/track/4cOdK2wGLETKBW3PvgPWqT",
platforms: ["spotify", "appleMusic"], // optional
}),
})
const data = await res.json()
console.log(data.platforms.appleMusic.url)Errors
Errors use standard HTTP status codes. The body is JSON with an error message (or plain text for auth/rate-limit responses).
| Status | Meaning |
|---|---|
400 | Bad input. Missing/invalid url, or malformed platforms. |
401 | Missing, invalid, or inactive API key. |
422 | Couldn't resolve this link. The source may be unsupported, unreadable, or no match passed verification. |
429 | A limit is spent. Monthly quota: every call returns 429 until it resets. The body links to the billing portal, where you pick a higher plan and pay in Stripe. The per-minute burst limit and the shared daily limit clear on their own. |
502 | An unexpected error while resolving the link. Safe to retry. |
Rate limits & quota
Each account has a monthly quota (calls per month, shared by every key on the account) and a short per-minute burst limit. The month starts on the day the account was created, or the day it last moved to a higher quota.
Every call counts as one call toward the monthly quota. Once the quota is spent, every call returns 429 until it resets. Retrying does not help. The response links to the billing portal. Pick a higher plan there and pay in Stripe. The quota on your account updates when the payment lands.
On managed keys (the default), there is also a shared daily capacity ceiling that protects the live site. A full ceiling returns 429 until the next day. Fully BYOK accounts (every provider configured) skip that shared ceiling and only hit their plan quota and burst limit.
Versioning
The API is versioned in the path (/v1). v1 is a stable contract: responses only gain fields. Existing ones are never removed or renamed. Any breaking change ships under a new major version (/v2), and v1 stays supported for a clearly-announced deprecation window after that.
Support
A higher plan is self-serve in the billing portal. Questions about the API: hello@songport.link.
Releasing music yourself? SongPort for Artists is a bio page with your releases. For artists.