Concept

The song_link (lis.tn) universal music link

Every AudD match includes a song_link on the lis.tn domain; how its ?thumb and ?provider conventions and the SDK helpers let you build cover-art and 'open in your service' UIs without requesting provider metadata.

view .md auddsong_linklis.tncover art

Every AudD match comes back with a song_link — a universal URL on the lis.tn domain that resolves to a page linking the recognized song across streaming services. It’s present on a match whether or not you ask for any provider metadata, which makes it the cheapest way to put a clickable result and its cover art in front of a user. This page covers what song_link is, the two URL conventions it supports, and when to reach for it versus requesting full provider blocks.

TL;DR

  • Every match includes song_link, e.g. https://lis.tn/Warriors. No provider metadata request needed — it’s there by default.
  • On a lis.tn-hosted link, two conventions work:
    • append ?thumb → the cover-art image URL (https://lis.tn/Warriors?thumb).
    • append ?<provider> → a redirect to that provider (https://lis.tn/Warriors?spotify, …?apple_music).
  • The SDKs wrap these: thumbnail_url / thumbnailUrl for cover art, streaming_url(provider) / streamingUrl(provider) for a provider redirect.
  • These conventions apply only to lis.tn-hosted links. Some song_links point elsewhere (e.g. a YouTube URL) and won’t support ?thumb / ?<provider>; the SDK helpers return null in that case.
  • Want structured metadata and direct provider URLs instead of redirects? Request provider blocks with return — at the cost of a little latency.

Why this matters

The common UI need after a recognition — show the cover art, give the user a button to open the track in their music service — has two ways to be solved, and the cheap one is right more often than people expect.

The expensive way is to request provider metadata blocks (apple_music, spotify, …) on every recognition. That gives you structured objects and direct URLs, but each requested provider adds latency to the call, and you pay that cost on every recognition even when the user never clicks through.

The cheap way uses only song_link, which is already in the response. A lis.tn link is a universal redirect: send the user to it and lis.tn sends them on to a service that has the track; append ?thumb and it hands you the cover image. No extra providers requested, no extra latency on the recognition call. For a “tap to open, here’s the artwork” UI, that’s usually all you need.

The guardrail: the ?thumb and ?<provider> conventions only work on links actually hosted on lis.tn. A song_link can occasionally point somewhere else, and the conventions won’t apply there — which is exactly why the SDK helpers exist and why they return null rather than handing you a broken URL.

song_link is a universal music link on the lis.tn domain. It resolves to a landing page for the recognized track that links out to the streaming services carrying it. It’s on every match, without requesting any provider metadata:

{
  "status": "success",
  "result": {
    "artist": "Imagine Dragons",
    "title": "Warriors",
    "album": "Smoke + Mirrors (Deluxe)",
    "song_link": "https://lis.tn/Warriors"
  }
}

A bare recognition call — no return, no provider blocks — already gives you this:

from audd import AudD

audd = AudD("test")  # 10 free standard-endpoint requests/day; your own token at dashboard.audd.io

song = audd.recognize("https://audd.tech/example.mp3")
print(song.song_link)  # -> https://lis.tn/Warriors

That single URL is a complete click-through: it works as a link a user can tap to open the song on a service they already use.

The ?thumb convention: cover art

Append ?thumb to a lis.tn song_link and it returns the cover-art image URL for the track:

https://lis.tn/Warriors?thumb

Use it directly as an <img> source. There’s no separate artwork request and no provider block to ask for — the cover art rides along on the link you already have.

<img src="https://lis.tn/Warriors?thumb" alt="cover art">

The ?<provider> convention: open in a service

Append ?<provider> to a lis.tn song_link and it redirects to that provider’s page for the track:

https://lis.tn/Warriors?spotify
https://lis.tn/Warriors?apple_music

This is a redirect, not a metadata object: the URL sends the user straight to the service. It’s exactly what you want behind an “Open in Spotify” / “Open in Apple Music” button — no need to request that provider’s block just to produce the link.

The SDK helpers

Rather than concatenating query strings by hand, the SDKs expose helpers that build these URLs for you — and, importantly, return null when the song_link isn’t a lis.tn link that supports the convention:

  • Cover art: thumbnail_url (Python, snake_case languages) / thumbnailUrl (Node, Swift, camelCase languages).
  • Provider redirect: streaming_url(provider) / streamingUrl(provider).
// Node
const song = await audd.recognize(source);

const cover = song.thumbnailUrl;                 // lis.tn ?thumb URL, or null
const spotify = song.streamingUrl("spotify");    // lis.tn ?spotify redirect, or null
const apple = song.streamingUrl("apple_music");
# Python
song = audd.recognize(source)

cover = song.thumbnail_url                 # lis.tn ?thumb URL, or None
spotify = song.streaming_url("spotify")    # lis.tn ?spotify redirect, or None

Prefer these helpers over building the URL yourself. They encode the lis.tn-only rule below so you don’t ship a broken link.

When the conventions don’t apply

?thumb and ?<provider> only work on lis.tn-hosted links. Most song_links are on lis.tn, but some point elsewhere (for example, a YouTube URL). For those, appending ?thumb or ?spotify does nothing useful — the destination doesn’t honor the convention. The SDK helpers detect this and return null (or None) instead of a non-working URL, so always treat the helper result as optional and have a fallback.

In practice this means: don’t assume thumbnailUrl is always a string. Render the artwork only when it’s present, and fall back to a placeholder or to a provider block (if you requested one) when it’s null.

The lis.tn conventions and the return provider blocks solve overlapping problems with different trade-offs.

song_link conventionsProvider blocks via return
In the response by defaultYesNo — must list providers in return
CostNone extraEach requested provider adds latency
What you getA universal redirect + a cover-art URLStructured per-provider metadata and direct URLs/IDs
Direct vs redirectRedirect through lis.tnDirect provider URL
Works for any providerOnly those lis.tn routes toOnly the providers you requested

Reach for song_link + helpers when you need a click-through and cover art and nothing more — that’s the majority of “show the result” UIs, and it keeps the recognition call fast. Request provider blocks with return (e.g. return=apple_music,spotify) when you need structured metadata — provider track IDs, preview URLs, direct (non-redirect) links — and can absorb the added latency.

Worked example

A “now playing” card built entirely from song_link, with no provider blocks requested. The recognition call is minimal and fast; the card still shows cover art and an “open in your service” button.

// Node — minimal recognition, no provider metadata requested
const song = await audd.recognize(source);

if (!song) {
  render({ empty: true });
} else {
  render({
    artist: song.artist,
    title: song.title,
    // cover art straight off song_link?thumb (null if not a lis.tn link)
    cover: song.thumbnailUrl,
    // "open in your service" buttons — each is a lis.tn redirect, or null
    links: {
      spotify: song.streamingUrl("spotify"),
      appleMusic: song.streamingUrl("apple_music"),
      deezer: song.streamingUrl("deezer"),
    },
    // the universal fallback link, always present on a match
    songLink: song.songLink,
  });
}

The rendering side treats every helper result as optional:

function render(card) {
  // cover art with a placeholder fallback when thumbnailUrl is null
  const img = card.cover ?? "/placeholder-cover.png";

  // show only the provider buttons that resolved; always show the universal link
  const buttons = Object.entries(card.links)
    .filter(([, url]) => url != null)
    .map(([name, url]) => ({ name, url }));

  // card.songLink is always there as the catch-all click-through
}

Because nothing here requested a provider block, the recognition stays fast, and the card degrades gracefully: if the match’s song_link isn’t a lis.tn link, the provider buttons and cover simply don’t render, while songLink itself still gives the user a working click-through.

Common mistakes

  • Requesting provider blocks just to get cover art or a click-through. song_link + ?thumb already covers that, with no added latency. Save return for when you genuinely need structured provider metadata.
  • Assuming thumbnailUrl / streamingUrl(...) is always non-null. They return null when the song_link isn’t a lis.tn link that supports the convention. Always have a fallback.
  • Hand-building ?thumb / ?spotify URLs on a raw song_link. That breaks silently on non-lis.tn links. Use the SDK helpers, which encode the rule and fail safely.
  • Treating ?<provider> as a metadata source. It’s a redirect to the service, not a data object. If you need the provider’s track ID or a preview URL, request that provider’s block with return.

Related

Reading this as an AI agent? The raw Markdown is at concepts/lisn-song-link.md, and the full index is /resources/llms.txt.