---
title: "Identify music in Instagram Reels and TikTok videos"
description: "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."
slug: "/resources/recipes/instagram-tiktok-music-id"
section: "recipes"
keywords: [audd, tiktok music id, instagram reels music, youtube shorts, social video recognition]
---

# Identify music in Instagram Reels and TikTok videos

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](https://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.

```ts
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.

```ts
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`.

```ts
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.

### Step 4: Use the universal link when a provider is missing

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.

```ts
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.

```ts
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:

```json
{
  "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/…" }, "…": "…" }
  }
}
```

| Field | Meaning |
|---|---|
| `artist`, `title`, `album` | The recognized track. |
| `release_date`, `label` | Release date and label of the recording. |
| `timecode` | Position *within the matched song* at the recognition point — not an offset into the video. |
| `song_link` | Universal `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.

```ts
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

- **Identify music from the microphone instead of a URL.** If your users want
  to identify a song playing around them rather than one in a posted video,
  see [Build a Shazam-style music identification app](/resources/recipes/shazam-clone).
- **Full tracklists and copyright checks.** To get every song in a longer
  upload — for a content-ID or moderation use case — see
  [Build a copyright scanner for user-uploaded content](/resources/recipes/ugc-copyright-scanner).
- **Choosing an endpoint.** For the full standard-vs-enterprise decision,
  read [Standard, enterprise, or streams: how to choose](/resources/concepts/standard-vs-enterprise-vs-streams).

---

**Related**

- [Build a Shazam-style music identification app](/resources/recipes/shazam-clone)
- [Build a copyright scanner for user-uploaded content](/resources/recipes/ugc-copyright-scanner)
- [Node.js SDK docs](https://docs.audd.io/sdks/node)
- [API reference](https://docs.audd.io)