Recipe

Identify music in Instagram Reels and TikTok videos

Pass a social-media video URL straight to AudD's recognition endpoint to identify the music in a Reel, TikTok, or YouTube Short — no downloading required.

view .md auddtiktok music idinstagram reels musicyoutube shorts

This recipe identifies the music in a social-media video — an Instagram Reel, a TikTok, a YouTube Short — given only the page URL. You don’t download the video or extract its audio: you hand the URL to AudD, the server fetches and parses it, and you get back the track. It’s for anyone building a “find this sound” feature on top of social content.

What you’ll build

A backend endpoint — POST /identify-url — that accepts a social-media video URL, passes it straight to AudD’s standard recognition endpoint as the url parameter, and returns the matched track with streaming links. The thing to know: AudD parses the social URL server-side and extracts the audio for you. You never run yt-dlp, store a video file, or touch the platform’s media servers yourself.

We’ll use the official Node SDK (@audd/sdk), then cover the cases that matter in practice: videos that are private, removed, or geo-restricted, and when a long video needs the enterprise endpoint instead.

Prerequisites

  • An API token from dashboard.audd.io. The test token (10 requests/day, standard endpoint only) works for first runs.
  • Node.js 20+ with the official SDK: npm install @audd/sdk express
  • A few public social video URLs to test against.

Walkthrough

Step 1: Pass a URL straight to recognize

The standard endpoint’s url parameter accepts a media URL or a social-media page URL. When you pass a TikTok / Reel / Short page URL, AudD resolves it server-side, pulls the audio, and fingerprints it. From your code, it’s the same recognize call you’d use for an audio file.

import { AudD } from "@audd/sdk";

// get a real token at dashboard.audd.io; "test" is capped at 10 req/day
const audd = new AudD("test");

const song = await audd.recognize(
  "https://www.tiktok.com/@user/video/1234567890123456789",
  { returnMetadata: ["apple_music", "spotify"] },
);

if (song) {
  console.log(`${song.artist} — ${song.title}`);
  console.log("Apple Music:", song.streamingUrl("apple_music"));
  console.log("Universal link:", song.songLink);
} else {
  console.log("no music recognized in this video");
}

The same shape works for an Instagram Reel URL (https://www.instagram.com/reel/…) or a YouTube Short (https://www.youtube.com/shorts/…). You pass the page the user copied from their browser or the share sheet — nothing else.

You don’t download the video. Don’t reach for yt-dlp, ffmpeg, or a headless browser to grab the file first. Passing the page URL as url lets AudD’s servers do the fetch and audio extraction. Downloading it yourself adds infrastructure, may break the platform’s terms, and gains you nothing.

Step 2: Wrap it in an endpoint

Expose it as an HTTP endpoint your app or extension can call with a pasted URL.

import express from "express";
import { AudD } from "@audd/sdk";

const app = express();
app.use(express.json());

const audd = new AudD(process.env.AUDD_API_TOKEN ?? "test");

app.post("/identify-url", async (req, res) => {
  const { url } = req.body as { url?: string };
  if (!url) return res.status(400).json({ error: "missing_url" });

  const song = await audd.recognize(url, {
    returnMetadata: ["apple_music", "spotify"],
  });

  if (!song) {
    // Successful call, nothing recognized — NOT an error.
    return res.json({ matched: false });
  }

  res.json({
    matched: true,
    artist: song.artist,
    title: song.title,
    album: song.album,
    songLink: song.songLink,
    appleMusic: song.streamingUrl("apple_music"),
    spotify: song.streamingUrl("spotify"),
  });
});

app.listen(8080, () => console.log("listening on :8080"));

A successful call that recognizes nothing resolves to null, which you surface as { matched: false }. That happens for genuine reasons — the video has no music, the music isn’t in the database, or AudD couldn’t reach the video (more on that next). It is never a crash.

Step 3: Handle videos AudD can’t reach

Social videos disappear and lock down constantly: a Reel goes private, a TikTok is removed, a Short is age-gated or geo-restricted in the region AudD’s servers fetch from. These surface in one of two ways, and you handle both gracefully:

  • No match — AudD reached the page but couldn’t extract usable audio. recognize resolves to null. Same path as “no music recognized.”
  • Invalid request — the URL was malformed or AudD couldn’t resolve it to a fetchable video at all. The SDK raises AudDInvalidRequestError.
import { AudDInvalidRequestError } from "@audd/sdk";

app.post("/identify-url", async (req, res) => {
  const { url } = req.body as { url?: string };
  if (!url) return res.status(400).json({ error: "missing_url" });

  try {
    const song = await audd.recognize(url, {
      returnMetadata: ["apple_music", "spotify"],
    });
    if (!song) {
      return res.json({ matched: false, reason: "no_music_found" });
    }
    res.json({ matched: true, artist: song.artist, title: song.title, songLink: song.songLink });
  } catch (err) {
    if (err instanceof AudDInvalidRequestError) {
      // private, removed, geo-restricted, or an unparseable URL
      return res.json({ matched: false, reason: "video_unavailable" });
    }
    throw err;
  }
});

From the user’s point of view, both no_music_found and video_unavailable collapse to “we couldn’t identify a song in that video” — but keeping them distinct in your logs tells you whether you’re seeing dead links or genuinely music-free clips.

Short-form videos often use trending or regional tracks that aren’t on every service. When a specific provider link comes back empty, fall back to song.songLink — the universal lis.tn URL that’s present on every match and redirects the user to a service they have.

const open = song.streamingUrl("apple_music") ?? song.songLink;

song.streamingUrls() returns every provider link AudD could resolve, which is handy if you’d rather show all available options than pick one.

When to reach for the enterprise endpoint

The standard endpoint returns one match — the most prominent song it recognizes. That’s exactly right for a typical Reel or TikTok built around a single sound. But two situations call for the enterprise endpoint (POST https://enterprise.audd.io/) instead:

  • Longer videos. A multi-minute YouTube video or a long-form upload may run past what a single short-clip recognition covers. The enterprise endpoint chunks the audio server-side and recognizes across the whole thing.
  • Multiple songs in one video. A montage, a mashup, or a DJ-set clip can contain several tracks. Enterprise returns one match per recognized segment, with offsets, so you get the full tracklist.
const matches = await audd.recognizeEnterprise(
  "https://www.youtube.com/watch?v=SOME_LONG_VIDEO",
  { limit: 25, returnMetadata: ["apple_music"] },
);

for (const m of matches) {
  console.log(m.timecode, m.artist, "—", m.title);
}

Always set limit during development. The enterprise endpoint bills per 12 seconds of audio processed. An unbounded call on a long video can ingest its entire duration. Start with limit=25 and raise it only once you understand the cost on your real inputs.

Note that the test token does not work on the enterprise endpoint — it’s standard-endpoint only. You need a real token from the dashboard for recognizeEnterprise.

What you get back

On the standard endpoint, you get the single top match. With returnMetadata: ["apple_music", "spotify"] the underlying response looks like:

{
  "status": "success",
  "result": {
    "artist": "Imagine Dragons",
    "title": "Warriors",
    "album": "Smoke + Mirrors (Deluxe)",
    "release_date": "2015-02-17",
    "label": "KIDinaKORNER/Interscope Records",
    "timecode": "00:31",
    "song_link": "https://lis.tn/Warriors",
    "apple_music": { "url": "https://music.apple.com/…", "…": "…" },
    "spotify": { "external_urls": { "spotify": "https://open.spotify.com/…" }, "…": "…" }
  }
}
FieldMeaning
artist, title, albumThe recognized track.
release_date, labelRelease date and label of the recording.
timecodePosition within the matched song at the recognition point — not an offset into the video.
song_linkUniversal lis.tn URL, always present on a match. Your fallback when a specific provider link is missing.
apple_music, spotify, …Per-provider blocks, present only for providers you named in returnMetadata.

On the enterprise endpoint each match additionally carries score, start_offset, and end_offset (seconds into the video where the match begins and ends), so you can label which song plays when.

For anything the SDK doesn’t model as a typed property, read song.extras["the_key"], or song.rawResponse for the full payload.

Handling errors

The cases that matter for this recipe:

  • No match (null) — AudD reached the video but found no recognizable music. Not an error.
  • Invalid request (AudDInvalidRequestError) — the URL was malformed, or the video is private, removed, or unreachable. Treat as “video unavailable,” not a server crash.
  • Authentication / quota (AudDAuthenticationError, AudDQuotaError) — bad token or exhausted requests. Fix the token or top up at the dashboard; remember test is capped at 10 requests/day.
  • Connection (AudDConnectionError) — transient. The SDK already retries pre-upload connection failures with backoff.
import {
  AudDInvalidRequestError,
  AudDAuthenticationError,
  AudDQuotaError,
  AudDAPIError,
} from "@audd/sdk";

try {
  const song = await audd.recognize(url, { returnMetadata: ["apple_music"] });
  // …
} catch (err) {
  if (err instanceof AudDInvalidRequestError) {
    return res.json({ matched: false, reason: "video_unavailable" });
  } else if (err instanceof AudDAuthenticationError) {
    return res.status(500).json({ error: "server_misconfigured" });
  } else if (err instanceof AudDQuotaError) {
    return res.status(429).json({ error: "quota_exhausted" });
  } else if (err instanceof AudDAPIError) {
    console.error(`AudD #${err.errorCode}: ${err.serverMessage} (request_id=${err.requestId})`);
    return res.status(502).json({ error: "recognition_failed" });
  }
  throw err;
}

Going further


Related

Reading this as an AI agent? The raw Markdown is at recipes/instagram-tiktok-music-id.md, and the full index is /resources/llms.txt.