Analytics API: Scope Filters and Per-Track Data
Every analytics request covers some part of your catalog, and every request returns one aggregated result over that part. Those two facts together catch people out: filter a call by an album’s UPC and you get the album’s numbers, not a row per track on it. This guide covers the filters that set the scope, what the aggregation does to the result, and the two endpoints that break a scope down track by track.
The scope filters
Section titled “The scope filters”Four filters narrow an analytics request to part of your catalog. They work on GET /analytics/summary, on the standalone series and demographic endpoints, and on GET /analytics/leaderboards and /analytics/placements.
| Filter | Type | The tracks it resolves to |
|---|---|---|
filter[release_id] | integer | every track on that release |
filter[isrc] | string | the one recording carrying that ISRC |
filter[upc] | string | every track on the release with that barcode |
filter[artist_names][] | array of strings | every track in your catalog credited to those artists |
GET /analytics/streams?filter[start_date]=2026-06-01&filter[end_date]=2026-06-30&filter[upc]=0123456789012Send none of them and the request covers your whole accessible catalog — the default scope for every analytics call.
Only one filter applies
Section titled “Only one filter applies”The four are resolved in a fixed order — release_id, then isrc, then upc, then artist_names[] — and the first one present wins. Sending filter[release_id] and filter[upc] together doesn’t intersect them; the UPC is ignored. Send only the filter you mean.
What happens when the identifier isn’t yours
Section titled “What happens when the identifier isn’t yours”The three release-level filters answer differently when the identifier doesn’t resolve inside your catalog:
filter[release_id]raises an error —404when no such release exists,403when it exists but isn’t yours.filter[isrc]andfilter[upc]resolve to an empty scope, and the request succeeds with an emptydata.
So when you’re driving the API from an identifier a user typed, test for empty data rather than waiting for a 404.
Narrowing to one of your labels
Section titled “Narrowing to one of your labels”GET /analytics/summary and GET /analytics/leaderboards also accept filter[label_id], which narrows the request to a single one of your own labels. An unknown label returns 404 and a label you don’t own returns 403. It can only narrow your scope — there’s no value that widens it.
To narrow by store instead, use filter[platform]. See Analytics API: Platforms, Availability, and Limits for the accepted values and the section-by-platform matrix.
One scope in, one series out
Section titled “One scope in, one series out”The series and summary sections aggregate over the resolved scope. A daily series groups by date and platform and sums the measure across every track in scope, so the number of rows you get back depends on how many dates and platforms reported — never on how many tracks the filter matched.
GET /analytics/streams?filter[start_date]=2026-06-01&filter[end_date]=2026-06-02&filter[upc]=0123456789012{ "data": [ { "date": "2026-06-01", "platform": "SPOTIFY", "total": 1804 }, { "date": "2026-06-01", "platform": "DEEZER", "total": 96 }, { "date": "2026-06-02", "platform": "SPOTIFY", "total": 1731 } ]}A twelve-track album and a single both return this shape. The total on each row is the whole scope’s figure for that date and platform, and it’s the same value whether the album has two tracks or twenty.
The demographic endpoints behave the same way one dimension over: /analytics/streams-by-country groups by country and sums across the scope, so a UPC-filtered call gives the album’s country split rather than each track’s. Every section of /analytics/summary follows the same rule, with two named exceptions covered below.
Getting per-track numbers
Section titled “Getting per-track numbers”| What you want | Where to get it |
|---|---|
| Everything for a single track | filter[isrc] on any analytics endpoint |
| Per-track totals for a period, ranked | GET /analytics/leaderboards?type=tracks |
| Per-track daily series | GET /analytics/summary with metrics[]=track-streams-daily |
Per-track totals: the leaderboards endpoint
Section titled “Per-track totals: the leaderboards endpoint”GET /analytics/leaderboards ranks your catalog by summed streams over the window. It’s the API equivalent of the Top performers card in the Analytics dashboard.
type is required and takes artists, tracks, albums, or all (all three lists in one request). The scope filters narrow it exactly as they narrow a series, so type=tracks with filter[upc] gives you that album’s tracks and nothing else.
GET /analytics/leaderboards?type=tracks&filter[start_date]=2026-06-01&filter[end_date]=2026-06-30&filter[upc]=0123456789012&limit=50Track rows carry name, artistName, streams, release_id, identifier (the platform identifier, or null where the row spans more than one), and isrc (the recording the row’s streams belong to). Rows come back ordered by streams, highest first.
isrc is returned whenever the row is a single recording beyond doubt, including when several platforms each report that recording under their own identifier — the usual case for a track available on more than one service. It’s null when the row can’t be shown to cover one recording: unfiltered reads, which rank by name across your whole catalog; rows narrowed by filter[artist_names][] alone; and rows where a title and artist cover two different recordings. A null means that row has no single recording to join to, not that the recording has no ISRC. Read isrc and identifier as independent — either can be null while the other carries a value — and use the per-track daily sections below when every row must carry an ISRC.
Two limits worth planning for:
limitdefaults to 10 and can’t exceed 50. An album with more than 50 tracks can’t be listed in full through this endpoint — use the per-track daily sections instead and total the rows yourself.- The window can’t exceed 180 days, narrower than the 400 days the series endpoints allow. Stitching several shorter windows together doesn’t reconstruct a longer ranking, because the top ten of each month isn’t the top ten of the quarter.
Per-track daily series: two summary sections
Section titled “Per-track daily series: two summary sections”GET /analytics/summary carries two sections that report each track separately instead of the release-wide total:
| Section | Row shape | Reported by |
|---|---|---|
track-streams-daily | { date, platform, isrc, streams } | every platform |
track-listeners-daily | { date, platform, isrc, listeners } | Spotify, Apple Music and Amazon Music |
Both are opt-in: they’re computed only when you name them in metrics[], and both require filter[release_id], filter[isrc], or filter[upc]. Naming one without a release or track filter returns 422 with the error attached to metrics. An artist-name filter doesn’t satisfy the requirement.
Rows are ordered by date, then platform, then ISRC, and a day with no activity carries no row rather than a zero. Listener figures are per-day counts and are not summable across dates — the same person listening on two days is one listener on each of them.
Worked example: streams per track for an album
Section titled “Worked example: streams per track for an album”You have an album with a UPC of 0123456789012 and you want June’s numbers broken down by track.
For a ranked list of per-track totals, ask the leaderboards endpoint for the album’s tracks:
GET /analytics/leaderboards?type=tracks&filter[start_date]=2026-06-01&filter[end_date]=2026-06-30&filter[upc]=0123456789012&limit=50{ "data": [ { "name": "Nine Roses", "artistName": "Wiguez", "streams": 41208, "release_id": 88213, "identifier": "3n2f9xk2p1", "isrc": "USABC2600001" }, { "name": "Harbour Lights", "artistName": "Wiguez", "streams": 18740, "release_id": 88213, "identifier": "7b1q4mz8v2", "isrc": "USABC2600002" } ], "meta": { "type": "tracks", "start_date": "2026-06-01", "end_date": "2026-06-30", "limit": 50 }}For each track’s day-by-day series, ask the summary endpoint for the per-track section:
GET /analytics/summary?filter[start_date]=2026-06-01&filter[end_date]=2026-06-30&filter[upc]=0123456789012&metrics[]=track-streams-daily{ "data": { "track-streams-daily": [ { "date": "2026-06-01", "platform": "SPOTIFY", "isrc": "USABC2600001", "streams": 1412 }, { "date": "2026-06-01", "platform": "DEEZER", "isrc": "USABC2600001", "streams": 78 }, { "date": "2026-06-01", "platform": "SPOTIFY", "isrc": "USABC2600002", "streams": 392 } ] }}Add metrics[]=track-listeners-daily to the same call for listeners alongside streams. And if you only care about one track on the album, drop the UPC and pass that track’s filter[isrc] instead — every analytics endpoint then reports on that recording alone.
Related
Section titled “Related”- Analytics API: Platforms, Availability, and Limits — which platforms report which metrics, reporting cadence, and the date-range caps
- API Overview and Quickstart — authentication, sandbox, and the full endpoint reference
- Analytics — the same data in the dashboard
- Connect Your AI Assistant to LabelGrid (MCP) — query your analytics in plain language
Not using LabelGrid yet?
Everything you just read about is available on our platform.
See what LabelGrid can do →