API Reference

SongPort API

Turn a single music link into direct links for every major streaming platform — powered by 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, Tidal or SoundCloud) into the matching links on every other platform, 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. Repeated links are served from cache instantly.

The API runs on your own upstream credentials for each streaming platform — SongPort is the matching engine on top. Before your first call, configure at least one platform key (see Upstream keys). Deezer always works with no key at all.

Base URL

All requests go to:

https://api.songport.link/v1

Every 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_xxxxxxxxxxxxxxxxxxxx

Your 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? Reach out (see Support) and we'll set you up.

A newly issued key is inactive until you configure at least one upstream key in your portal (see Upstream keys). An inactive key returns 401.

Upstream keys (bring your own)

SongPort resolves links using your own credentials for each streaming platform, drawing on your own upstream quota rather than a shared pool. You add a key per platform once, in your portal at /portal/credentials; SongPort handles the ISRC matching on top of them.

PlatformWhat you provideNotes
deezerNothingOpen public endpoint — always available, no key needed.
youtubeMusicA YouTube Data API keyFree and instant from Google Cloud. Matches by title/artist (YouTube has no ISRC).
spotifyClient ID + secretBest ISRC coverage — improves matching everywhere.
appleMusicMusicKit key (Team ID, Key ID, .p8)ISRC lookup swept across regional storefronts.
tidalClient ID + secretMatches by ISRC only — no text search, so a track with no clean ISRC match won't resolve here.
soundcloudNothingNo direct-match lookup exists — SoundCloud is always returned as a search link (isSearchFallback: true), never a direct deep link.

A platform plays two roles, and each needs that platform's key (except Deezer):

  • As a target (a link you want back). No key → that platform comes back as a isSearchFallback: true search link, never an error.
  • As a source (the link you send in). Reading it needs that platform's key. Send a Spotify link with no Spotify key configured and the whole request returns 422. So: to convert a link from a platform, configure that platform's key — or send a deezer link, which needs none.

There is no fallback to a shared or third-party resolver on the API: a link resolves on your own keys or returns a clear error. (The one exception is the migration window below.)

Migrating from the shared setup? Existing developers get a 30-day grace period during which platforms you haven't configured yet still resolve on SongPort's shared keys. After it ends, unconfigured platforms behave exactly as above (search links as a target, 422 as a source).

Converting a link

POST /v1/convert — resolve one music link.

Send a JSON body with these fields:

FieldTypeRequiredDescription
urlstringyesA supported music URL (or a spotify: URI).
platformsstring[]noOnly return these platforms (e.g. ["spotify", "appleMusic"]). Unknown keys are ignored.
shortenbooleannoWhen true, return a shareable songport.link short URL (created if this track isn't stored yet).

Platform keys: spotify, appleMusic, youtubeMusic, deezer, tidal, soundcloud.

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:

200 OK
{
  "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 }
  },
  "cached": true,
  "songportUrl": "https://songport.link/t/rick-astley-never-gonna-give-you-up-w45sq8xg"
}

What each field means:

FieldTypeDescription
isrcstring | nullThe recording's ISRC when known.
upcstring | nullThe release's UPC — the album-level analogue of isrc. Present when the link resolves to an album/release.
titlestringTrack title.
artiststringArtist name.
thumbnailstring | nullCover-art image URL.
platformsobjectA map of platform key → { url, isSearchFallback }. See below.
cachedbooleantrue when the result came straight from cache (no upstream lookup).
songportUrlstring?A shareable short link. Always present on cache hits; on fresh conversions only when you pass shorten: true.

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. Two common reasons: you haven't configured that platform's upstream key (so it can't be resolved directly), or the track simply isn't on that platform.

Examples

A minimal request with cURL:

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:

JavaScript (server-side)
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).

StatusMeaning
400Bad input — missing/invalid url, or malformed platforms.
401Missing, invalid, or inactive API key. (A new key is inactive until you configure an upstream key.)
422Couldn't resolve this link with your configured upstream keys. Reading a link from a platform needs that platform's key — add the missing key (see Upstream keys), or send a deezer link, which needs none.
429Rate limited — monthly quota reached, or per-minute burst exceeded. Back off and retry. (While you still draw on the shared pool during the migration grace period, a temporarily-full shared daily capacity can also return 429.)
502An unexpected error while resolving the link. Safe to retry.

Rate limits & quota

Each key has a monthly quota (the number of calls per calendar month, defined by your plan) and a short per-minute burst limit.

Every call counts toward your monthly quota — including cache hits. Cache hits are fast and don't hit the upstream services, but they still count as one request. When you exceed a limit you get a 429; wait and retry, or ask for a higher plan.

Because the API runs on your own upstream keys, new (uncached) conversions draw on your own upstream quota, not a shared one — so your per-key quota and burst limit are the only limits that apply. The shared daily-capacity limit only comes into play while you still fall back to the shared pool during the migration grace period.

Versioning

The API is versioned in the path (/v1). v1 is a stable contract: we only add fields to responses, never remove or rename existing ones. Any breaking change ships under a new major version (/v2), and v1 stays supported for a clearly-announced deprecation window after that.

Support

Questions, a new key, or a higher plan? Get in touch at hello@songport.link.