---
title: "Add music recognition to a React Native app"
description: "Record a few seconds of audio in a React Native app, upload it to your own backend, and identify the song with AudD's standard recognition endpoint."
slug: "/resources/integrations/react-native"
section: "integrations"
keywords: [audd, react native, music recognition, mobile, audio recording]
---

# Add music recognition to a React Native app

This page shows how to add "what's this song?" to a React Native app. The app
records a few seconds of audio on the device, uploads the clip to a backend
you control, and the backend calls AudD to identify the track. It's for mobile
developers who want song identification without putting their API token inside
a shipped app.

## What you'll build

Three moving parts:

1. **The React Native app** records a short clip with an audio-recording
   library and POSTs the file to your backend over `fetch` + `FormData`.
2. **Your backend** receives the upload, calls AudD's standard recognition
   endpoint with the official `@audd/sdk`, and returns a compact JSON answer.
3. **The app** renders the result: artist, title, and a `song_link` the user
   can tap to open the track.

Recognition runs on the **standard endpoint** (`POST https://api.audd.io/`).
That's the right choice here: it's built for a short audio clip, responds in
under 2 seconds, and matches against AudD's database of 160 million songs. A
5–15 second clip is plenty. The standard endpoint has a 10 MB file-size cap,
which a few seconds of compressed audio stays well under.

> **Never put your AudD token in the app.** A shipped mobile app is not a
> trusted environment. Anyone can pull the IPA/APK off a device, unzip it, and
> read every string in the JavaScript bundle and native binary — including any
> API token you embedded. A token in the app is a token anyone can extract and
> spend against your account. The token lives **only** on your backend, and
> the app talks to your backend. This page is built around that rule.

## Prerequisites

- An API token from [dashboard.audd.io](https://dashboard.audd.io). Keep it on
  the backend. The string `test` works for a first run on the backend — it's a
  public token capped at 10 requests/day on the standard endpoint.
- A React Native app (bare React Native or Expo with a dev/prebuild that
  includes a native audio module — recording needs native code).
- An audio-recording library. Common, well-maintained options:
  - `react-native-audio-recorder-player`
  - `expo-av` (Expo) / its successor `expo-audio`
  - `react-native-nitro-sound`
  Each records to a file on the device and gives you back a file path (and on
  most, the recording format). Pick one and follow its README for setup; the
  example below shows the shape, not one library's exact method names.
- Node.js 20+ for the backend, with `npm install @audd/sdk express multer`.

## Walkthrough

### Step 1: Record a clip on the device

Ask for the microphone permission, start recording when the user taps a
button, and stop after a few seconds. Every recording library follows the same
three-call shape — start, stop, read the resulting file path:

```ts
// Shape of a typical RN audio-recording library.
// Method names differ per library — check its README.
import { PermissionsAndroid, Platform } from "react-native";
import { recorder } from "./recorder"; // your chosen library, wrapped

async function ensureMicPermission() {
  if (Platform.OS === "android") {
    const granted = await PermissionsAndroid.request(
      PermissionsAndroid.PERMISSIONS.RECORD_AUDIO,
    );
    return granted === PermissionsAndroid.RESULTS.GRANTED;
  }
  return true; // iOS: declare NSMicrophoneUsageDescription in Info.plist
}

// Returns a local file URI like file:///.../clip.m4a
async function recordClip(seconds = 8): Promise<string> {
  const uri = await recorder.startRecorder(); // begins writing to a file
  await new Promise((r) => setTimeout(r, seconds * 1000));
  await recorder.stopRecorder();
  return uri;
}
```

On iOS, recording typically produces an `.m4a` (AAC) file; on Android, an
`.m4a`/`.mp4` or `.aac` depending on the library. AudD accepts MP3, WAV, FLAC,
M4A, OGG, AAC, WMA, and AIFF, so the default output of any of these libraries
is a format AudD can read — you don't need to transcode.

### Step 2: Upload the clip to your backend

POST the recorded file to your backend as multipart form data. React Native's
`fetch` accepts a `{ uri, name, type }` object as a `FormData` part and streams
the file straight from disk — you don't read it into a JS string.

```ts
async function identify(fileUri: string) {
  const form = new FormData();
  form.append("file", {
    uri: fileUri,
    name: "clip.m4a",
    type: "audio/m4a",
  } as any);

  const res = await fetch("https://your-backend.example.com/identify", {
    method: "POST",
    body: form,
    // Don't set Content-Type yourself — RN sets the multipart boundary.
  });

  if (!res.ok) throw new Error(`backend ${res.status}`);
  return res.json() as Promise<IdentifyResponse>;
}

type IdentifyResponse =
  | { match: false }
  | {
      match: true;
      artist: string;
      title: string;
      album: string | null;
      songLink: string | null;
    };
```

There is no `api_token` and no call to `api.audd.io` here — the app only knows
about your backend.

### Step 3: Recognize on the backend

The backend receives the upload and forwards the bytes to AudD with the
official Node SDK. The SDK reads the token from the constructor (or the
`AUDD_API_TOKEN` environment variable) and returns a result object on a match
or `null` on a successful call that matched nothing.

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

const upload = multer({ limits: { fileSize: 10 * 1024 * 1024 } }); // 10 MB cap
const audd = new AudD(process.env.AUDD_API_TOKEN!); // token lives here, only here
const app = express();

app.post("/identify", upload.single("file"), async (req, res) => {
  if (!req.file) return res.status(400).json({ error: "no_file" });

  const song = await audd.recognize(req.file.buffer, {
    returnMetadata: ["apple_music", "spotify"], // optional streaming links
  });

  if (!song) return res.json({ match: false });

  res.json({
    match: true,
    artist: song.artist,
    title: song.title,
    album: song.album,
    songLink: song.songLink, // universal lis.tn URL
  });
});

app.listen(3000);
```

`audd.recognize` accepts the uploaded bytes directly (`req.file.buffer`), so
you never write the clip to disk. Set the `multer` limit to 10 MB to match the
standard endpoint's file-size cap and reject oversized uploads before they
reach AudD.

### Step 4: Show the result in the app

Render the answer and make the `songLink` tappable so the user can open the
track. `song_link` is a universal URL on `lis.tn` that resolves to the user's
preferred music service.

```tsx
import { Linking, Text, View } from "react-native";

function Result({ data }: { data: IdentifyResponse }) {
  if (!data.match) return <Text>No match — try a louder or longer clip.</Text>;
  return (
    <View>
      <Text style={{ fontWeight: "bold" }}>{data.title}</Text>
      <Text>{data.artist}</Text>
      {data.songLink && (
        <Text
          style={{ color: "#2563eb" }}
          onPress={() => Linking.openURL(data.songLink!)}
        >
          Open this track
        </Text>
      )}
    </View>
  );
}
```

## What you get back

On a match, the backend returns the fields the app needs. The AudD result
carries more — `album`, `release_date`, `label`, `timecode`, and per-provider
streaming blocks when you pass `returnMetadata` — and you forward whichever
your UI uses:

```json
{
  "match": true,
  "artist": "Imagine Dragons",
  "title": "Warriors",
  "album": "Smoke + Mirrors (Deluxe)",
  "songLink": "https://lis.tn/Warriors"
}
```

A successful call that matched nothing returns `{ "match": false }` — the SDK
gives you `null`, which is not an error. Distinguish "we recognized
nothing" (ask the user to try again) from "the request failed" (a real error,
handled below).

`timecode` on a match is the position **within the matched song** where the
user's clip occurred — it is not an offset into the user's recording.

## Handling errors

The backend is where errors surface; the app only sees your HTTP status codes.
The cases that matter:

- **No match** — `recognize` returns `null`. Not an error. Return
  `{ match: false }` and prompt the user to retry.
- **Authentication errors** — a bad or missing token. This is a server
  misconfiguration, not a user problem. Fail loudly at startup, never per
  request, and never leak the token text in a response the app could log.
- **Quota errors** — you've hit your request limit. Surface to ops; don't
  retry in a tight loop. Return a 503 the app can show as "try again later".
- **Invalid-audio errors** — the clip wasn't decodable audio. Usually a
  too-short or silent recording. Return a 422 and ask for a longer clip.
- **Connection errors** — transient network trouble between your backend and
  AudD. Retry with backoff.

```ts
import { AudDError } from "@audd/sdk";

app.post("/identify", upload.single("file"), async (req, res) => {
  try {
    const song = await audd.recognize(req.file!.buffer);
    return res.json(song ? { match: true, /* ...fields */ } : { match: false });
  } catch (err) {
    if (err instanceof AudDError) {
      // log err.errorCode / err.requestId for support — never to the client
      return res.status(502).json({ error: "recognition_failed" });
    }
    return res.status(400).json({ error: "bad_request" });
  }
});
```

## Going further

- **Trim the clip before upload.** A shorter clip uploads faster on a phone
  network. 5–15 seconds is enough for the standard endpoint.
- **Authenticate the app→backend hop.** Your backend now holds the AudD token,
  so protect `/identify` with your own app auth (a session token, an API key
  you issue per install) so only your app can spend your AudD quota.
- **Rate-limit per user** on the backend so one client can't drain your quota.
- For a desktop build of the same record-upload-recognize flow, see the
  [Electron integration](/resources/integrations/electron-desktop).

---

**Related**

- [Build a Shazam-style music identification app](/resources/recipes/shazam-clone)
- [Add music recognition to an Electron desktop app](/resources/integrations/electron-desktop)
- [Node.js SDK docs](https://docs.audd.io/sdks/node)
- [API reference](https://docs.audd.io)