Beta founding offer: lock in 50% off any plan for life, before the beta ends. Claim founding pricing →
Radio airplay detection

Know what's playing
on any stream

Register a live radio or broadcast stream URL and we detect every track as it airs: a timestamped play log with ISRC, artist, release, and cross-platform IDs, plus a signed webhook for every confirmed play. 93M+ tracks.

1

Register a stream URL

POST the direct URL of any internet radio or broadcast stream. Monitoring starts immediately, with no setup and no audio to upload.

2

We monitor it continuously

We pull the audio around the clock and recognise each track as it plays, against an acoustic index spanning 93M+ tracks. How the engine works

3

Poll the now-playing log

Read the current track, or the full timestamped history. Each entry is a plain track object with ISRC, release, genre, and platform IDs.

Register a stream

One call with the stream URL. You get back a stream id you'll poll for results. Keep your API key on the server.

bash
# Register a live stream URL - we start monitoring it immediately
curl -X POST https://api.sonovault.now/v1/streams \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://stream.example.com/radio.mp3","name":"My Station"}'
# -> { "id": "9f2c...", "url": "...", "name": "My Station", "status": "active" }

Poll for what's playing

Get the current now-playing track, or the full timestamped log. Use ?since= to fetch only what's new since your last poll.

bash
# What's playing right now
curl https://api.sonovault.now/v1/streams/9f2c... \
-H "x-api-key: YOUR_API_KEY"
bash
# The timestamped recognition log (newest first; ?since= for incremental polling)
curl "https://api.sonovault.now/v1/streams/9f2c.../history?since=2026-06-14T18:00:00Z" \
-H "x-api-key: YOUR_API_KEY"
javascript
const API = "https://api.sonovault.now";
const headers = { "x-api-key": process.env.SONOVAULT_API_KEY };
// 1. Register the stream once
const { id } = await fetch(`${API}/v1/streams`, {
method: "POST",
headers: { ...headers, "Content-Type": "application/json" },
body: JSON.stringify({ url: "https://stream.example.com/radio.mp3", name: "My Station" }),
}).then((r) => r.json());
// 2. Poll the now-playing track (e.g. every 30s)
setInterval(async () => {
const { now_playing } = await fetch(`${API}/v1/streams/${id}`, { headers }).then((r) => r.json());
if (now_playing) {
const t = now_playing.track;
console.log(`${t.artists[0].name} - ${t.title} (since ${now_playing.started_at})`);
}
}, 30_000);

Or don't poll: webhooks & live feed

Register a webhook and we POST a signed stream.play.started event to your endpoint the moment a track is detected on any of your monitored streams; stream.offline and stream.online events cover the stream itself. Prefer a connection you hold open? The same events arrive on a Server-Sent Events feed. Both are bundled with stream monitoring and use no API credits.

bash
# Get a signed POST for every confirmed play - no polling
curl -X POST https://api.sonovault.now/v1/webhooks \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://your-app.example.com/hooks/sonovault","event_types":["stream.play.started","stream.offline","stream.online"]}'
# -> { "id": "wh_...", "secret": "whsec_..." } (store the secret to verify deliveries)
bash
# Or a live Server-Sent Events feed across all your streams
curl -N https://api.sonovault.now/v1/streams/live \
-H "x-api-key: YOUR_API_KEY" \
-H "Accept: text/event-stream"

The response

now_playing.track and each history entry are plain track objects (the same shape returned by track search). now_playing is null during ad breaks, talk, or unrecognised audio.

json
{
"id": "9f2c8e1a-...",
"url": "https://stream.example.com/radio.mp3",
"name": "My Station",
"status": "active",
"runtime_status": "running",
"now_playing": {
"track": {
"id": 121217540,
"title": "One More Time",
"artists": [{ "id": 9001, "name": "Daft Punk", "is_primary": true }],
"releases": [{ "id": 4242, "title": "Discovery", "release_date": "2001-03-12" }],
"isrc": "GBDUW0000054",
"duration": 320,
"genre": ["Electronic"],
"subgenre": ["French House"]
},
"started_at": "2026-06-14T18:42:11Z"
},
"last_recognized_at": "2026-06-14T18:43:02Z"
}
json
{
"plays": [
{
"track": { "id": 121217540, "title": "One More Time", "artists": [{ "id": 9001, "name": "Daft Punk", "is_primary": true }], "isrc": "GBDUW0000054" },
"started_at": "2026-06-14T18:42:11Z"
},
{
"track": { "id": 988213, "title": "Strangest Thing", "artists": [{ "id": 7720, "name": "The War On Drugs", "is_primary": true }], "isrc": "USAT21703012" },
"started_at": "2026-06-14T18:37:40Z"
}
]
}
No code needed

Monitor a stream from your dashboard

Logged-in users get a Stream Monitoring tool in the dashboard: paste a stream URL, watch the live now-playing track and recognition history, and stop monitoring with a click. Same recognition as the API.

Stream monitoring is billed €29 per active stream / month, on top of your API plan. You're billed only while a stream is being monitored. Polling the now-playing and history endpoints doesn't use any API credits. See pricing and the full API reference. Coming from another provider? See how we compare with AudD.

Frequently asked questions

You register the URL of a live audio stream (an internet radio station, a broadcast simulcast, your own feed). Sonovault pulls the audio continuously, fingerprints it in a rolling window, and recognises each track as it airs against an acoustic index spanning 93M+ tracks. You read the result however suits your app: poll the now-playing endpoint, receive signed webhooks for every confirmed play, or hold open the live SSE feed. It's the continuous counterpart to the one-shot Identify endpoint. For a full plain-language walkthrough of the detection engine, see how radio monitoring works.
Yes. Register the station's stream URL, create a webhook subscribed to stream.play.started, and your endpoint receives a signed POST with the full track object (title, artists, ISRC) for every confirmed play on that stream, a few minutes after it starts. Filter for the recordings you care about on your side, e.g. by ISRC, and forward matches to email, Slack, or wherever you want the alert. Note that monitoring is per stream: you choose which stations to watch.
Plain HTTP/Icecast/Shoutcast audio streams, the kind you can play directly in a media player (mp3 or AAC). Give us the direct stream URL, not a webpage or playlist page. Streams that require login, DRM, or are HLS/DASH-only aren't supported in this version.
Recognition is high-precision: a track is only reported once the audio agrees across several consecutive windows, so you don't get spurious entries. Recall depends on catalogue coverage: well-known commercial music (pop, rock, dance, jazz) recognises reliably; very new, underground, or unreleased tracks may not be in the fingerprint index. Every reported track is a real catalogue match with full metadata.
Detection lags real-time by roughly the length of the analysis window plus a short confirmation, typically under a minute after a track starts. Poll the endpoint on whatever interval suits you (every 15–60 seconds is plenty); each track also carries the timestamp it started, so your log stays accurate even if you poll infrequently.
Stream monitoring is billed per active stream: €29 per stream per month, on top of your API plan. You're billed for streams that are actively being monitored; stop a stream and billing stops at the end of the period. Polling the now-playing and history endpoints doesn't use any API credits; it's included with the stream. Contact us for high-volume / multi-channel broadcaster pricing.
Yes. Logged-in users get a Stream Monitoring tool in the dashboard: paste a stream URL, see the live now-playing track and the recognition history, and stop monitoring with a click. It runs the same recognition as the API.