Skip to content

Analytics

Generate and download daily streaming analytics for Spotify, YouTube Content ID, YouTube Art Track, Apple Music, Amazon Music and TikTok.

Resource Overview

Analytics Reports allow you to export full daily streaming statistics for a specific DSP as a downloadable ZIP file containing JSON data. Reports are generated asynchronously — you request the report, then poll for its status until it completes, and finally download the file via a pre-signed URL.

Base Endpoint
/analytics-reports
Methods
POST, GET
Format
ZIP (JSON inside)
User Scope
Owner-restricted

How It Works

Unlike standard JSON:API resources that return data synchronously, Analytics Reports use an asynchronous workflow because the datasets can be very large (10,000+ records per day). The process has three steps:

1

Request a report

Send a POST with the DSP ID and date. You receive a report ID and status pending.

2

Poll for status

Send a GET with the report ID. The status progresses: pending → processing → completed (or failed).

3

Download the file

When completed, the response includes a download_url — a pre-signed URL valid for 12 hours. The file is a ZIP containing a single JSON file.

Supported DSPs

DSP dsp_id Description
Spotify 1 Daily track stats including streams, skips, demographics, device/source breakdown, engagement metrics.
YouTube Content ID 2 Daily track stats including views, watch time, engagement, estimated revenue, and country, source, device and playback breakdowns.
YouTube Art Track 3 Same record shape as YouTube Content ID, for Art Track content.
Apple Music 4 Daily track stats including plays, listeners, and storefront, device, source, format, subscription, city and demographic breakdowns.
Amazon Music 6 Daily track stats including streams, offline plays, play duration, and country, device, source, quality, and subscription-plan breakdowns.
TikTok 9 Daily track stats including views, watch time, video creations, engagement (likes, comments, shares, favourites), and country breakdown.
You can retrieve the full list of DSPs from /dsps. Only the DSPs listed above are supported for analytics reports.

Step 1: Create a Report

POST /analytics-reports
POST /analytics-reports
Authorization: Bearer {token}
Content-Type: application/json
Accept: application/json

Request Body

Field Type Required Description
dsp_id integer Yes The DSP to generate the report for. Must be 1 (Spotify), 2 (YouTube Content ID), 3 (YouTube Art Track), 4 (Apple Music), 6 (Amazon Music) or 9 (TikTok).
date string Yes The date to export in YYYY-MM-DD format. Only a single day can be requested per report.

Example Request

POST /analytics-reports
Authorization: Bearer {token}
Content-Type: application/json

{
    "dsp_id": 1,
    "date": "2026-03-28"
}

Example Response (201 Created)

{
    "data": {
        "id": 6,
        "dsp_id": 1,
        "dsp_name": "Spotify",
        "date": "2026-03-28",
        "status": "pending",
        "total_records": null,
        "file_size_bytes": null,
        "error": null,
        "download_url": null,
        "started_at": null,
        "completed_at": null,
        "created_at": "2026-04-01T16:00:39+00:00"
    }
}

Error: Unsupported DSP (422)

{
    "error": "DSP not supported for analytics export",
    "supported_dsp_ids": [1, 2, 3, 4, 6, 9]
}

Error: Too many active reports (409)

{
    "error": "You have reached the maximum of 20 active reports",
    "active_count": 20
}

Error: Report could not be created (502)

{
    "error": "Report could not be created"
}

The report was not created. Retry later.

Step 2: Poll Report Status

GET /analytics-reports/{id}
GET /analytics-reports/{id}
Authorization: Bearer {token}
Accept: application/json

Status Values

Status Description Action
pending Report is queued and waiting to be processed. Poll again in 1 minute.
processing Report is currently being generated. Poll again in 1 minute.
completed Report is ready. download_url is available. Download the file from download_url.
failed Report generation failed. The error field reads Report generation failed. Request the report again.

Response Fields

Both POST (201) and GET (200) answer the same report object.

Field Type Description
id integer Report ID
dsp_id integer DSP the report was requested for
dsp_name string DSP name
date string Requested date, YYYY-MM-DD
status string pending, processing, completed or failed
total_records integer or null Number of records in the file. A date with no data produces a completed report with total_records: 0 and an empty data array — it is not an error
file_size_bytes integer or null Size in bytes of the JSON file before compression (uncompressed)
error string or null Present only when status is failed, and always the text Report generation failed
download_url string or null Pre-signed URL valid for 12 hours, present only when status is completed. Each GET returns a fresh URL
started_at string or null When generation started
completed_at string or null When generation finished
created_at string When the report was requested

Example Response (completed)

{
    "data": {
        "id": 6,
        "dsp_id": 1,
        "dsp_name": "Spotify",
        "date": "2026-03-28",
        "status": "completed",
        "total_records": 9938,
        "file_size_bytes": 3355443,
        "error": null,
        "download_url": "https://s3.amazonaws.com/...?X-Amz-Signature=...",
        "started_at": "2026-04-01T16:00:41+00:00",
        "completed_at": "2026-04-01T16:00:49+00:00",
        "created_at": "2026-04-01T16:00:39+00:00"
    }
}

Example Response (failed)

{
    "data": {
        "id": 7,
        "dsp_id": 4,
        "dsp_name": "Apple Music",
        "date": "2026-03-28",
        "status": "failed",
        "total_records": null,
        "file_size_bytes": null,
        "error": "Report generation failed",
        "download_url": null,
        "started_at": "2026-04-01T16:00:42+00:00",
        "completed_at": null,
        "created_at": "2026-04-01T16:00:40+00:00"
    }
}

Error: Report not found (404)

{
    "error": "Report not found"
}

The report does not exist or is not yours.

Error: Report not available (502)

{
    "error": "Report not available"
}

The report status could not be read right now. Retry later.

Polling Recommendation

We recommend polling every 1 minute.

If you use webhooks, the analytics_report.completed and analytics_report.failed events are sent when a report finishes.

Step 3: Download the File

The download_url is a pre-signed URL that does not require authentication. Simply perform a GET request to download the ZIP file.

# No authorization header needed — the URL is pre-signed
curl -o report.zip "https://s3.amazonaws.com/...?X-Amz-Signature=..."

Download URL Expiration

The download URL is valid for 12 hours. Each GET /analytics-reports/{id} returns a fresh URL, so after expiration simply poll the report again.

File Format

The ZIP file contains a single JSON file named analytics-report-{date}.json. The JSON structure is:

{
    "meta": {
        "dsp_id": 1,
        "dsp_name": "Spotify",
        "date": "2026-03-28",
        "total_records": 1250,
        "generated_at": "2026-03-29T10:15:30+00:00",
        "generated_by": 42
    },
    "data": [
        { ... },
        ...
    ]
}

The data array contains all records for the requested date, with every nested object fully expanded. The record shape depends on the DSP and is described below.

  • Records have no id field. A record is identified by track_id + data_date, plus content_type for YouTube and TikTok.
  • data_date uses the format YYYY-MM-DDT00:00:00.000000Z.
  • created_at and updated_at are the time the report was generated.
  • A date with no data produces an empty data array.

Spotify Records (dsp_id 1)

{
    "data_date": "2026-03-28T00:00:00.000000Z",
    "dsp_name": "Spotify",
    "track_id": 5012,
    "track_name": "Summer Breeze",
    "album_id": 1830,
    "album_name": "Coastal Dreams",
    "spotify_track_id": "4a7b9c2d1e0f3a5b8c6d7e9f",
    "isrc": "USRC12345678",
    "country_stats": {
        "US": {
            "streams_count": 142,
            "completion_rate": 72.5,
            "valid_streams_count": 142,
            "track_length_seconds": 215,
            "total_reproduction_time": 24.831,
            "significant_streams_count": 103,
            "avg_reproduction_percentage": 81.2
        },
        "GB": {
            "streams_count": 38,
            "completion_rate": 68.4,
            "valid_streams_count": 38,
            "track_length_seconds": 215,
            "total_reproduction_time": 6.107,
            "significant_streams_count": 26,
            "avg_reproduction_percentage": 74.8
        }
    },
    "demographics": {
        "age_groups": {"18-22": 45, "23-27": 62, "28-34": 38, "35-44": 21, "45-59": 14},
        "genders": {"female": 88, "male": 79}
    },
    "aggregated_data": {
        "total_streams": 180,
        "total_skips": 47,
        "total_saves": 0,
        "total_shares": 0
    },
    "content_type_stats": {
        "audio_streams": 172,
        "video_streams": 8,
        "total_streams": 180,
        "audio_percentage": 96,
        "video_percentage": 4
    },
    "geographic_stats": {
        "connection_countries": {"US": 142, "GB": 38},
        "reporting_countries": {"US": 142, "GB": 38},
        "total_geolocated_streams": 180
    },
    "device_platform_stats": {
        "device_types": {"cell phone": 112, "computer": 48, "tablet": 20},
        "operating_systems": {"iOS": 78, "Android": 54, "Windows": 32, "macOS": 16},
        "shuffle_usage": {"enabled": 95, "disabled": 85},
        "repeat_usage": {"enabled": 12, "disabled": 168}
    },
    "source_stats": {
        "sources": {"others_playlist": 72, "collection": 45, "radio": 33, "search": 30},
        "source_uris": {"spotify:playlist:37i9dQZF1DX...": 42}
    },
    "engagement_stats": {
        "completion_rate": 73.89,
        "lyrics_viewed": 8,
        "canvas_viewed": 3,
        "discovery_streams": 33,
        "total_analyzed": 180
    },
    "timestamp_reliability": {
        "online_streams": 165,
        "offline_streams": 15,
        "total_streams": 180,
        "online_percentage": 92,
        "offline_percentage": 8
    },
    "created_at": "2026-03-29T10:15:30.000000Z",
    "updated_at": "2026-03-29T10:15:30.000000Z"
}
Field Type Description
data_date string Date of the statistics, as YYYY-MM-DDT00:00:00.000000Z
dsp_name string DSP name (Spotify)
track_id integer Limbo track ID
track_name string Track title
album_id integer Limbo album ID
album_name string Album title
spotify_track_id string Spotify track identifier
isrc string International Standard Recording Code
country_stats object Per-country breakdown keyed by country code: streams_count, completion_rate, valid_streams_count, track_length_seconds, total_reproduction_time, significant_streams_count, avg_reproduction_percentage
demographics object Listener demographics: age_groups and genders
aggregated_data object Totals: total_streams, total_skips, total_saves, total_shares
content_type_stats object Audio vs video stream breakdown: audio_streams, video_streams, total_streams, audio_percentage, video_percentage
geographic_stats object connection_countries, reporting_countries and total_geolocated_streams
device_platform_stats object device_types, operating_systems, shuffle_usage and repeat_usage (each with enabled / disabled)
source_stats object Playback sources and source_uris. Each one may be an empty array [] when there is nothing to report
engagement_stats object completion_rate, lyrics_viewed, canvas_viewed, discovery_streams, total_analyzed
timestamp_reliability object Online vs offline streams: online_streams, offline_streams, total_streams, online_percentage, offline_percentage
created_at string Time the report was generated
updated_at string Time the report was generated

YouTube Content ID and YouTube Art Track Records (dsp_id 2 and 3)

Both DSPs share the same record shape. content_type is content_id for dsp_id 2 and art_track for dsp_id 3.

{
    "data_date": "2026-03-28T00:00:00.000000Z",
    "track_id": 5012,
    "dsp_id": 3,
    "album_id": 1830,
    "genre_id": 12,
    "content_type": "art_track",
    "views": 80,
    "watch_time_minutes": 120,
    "likes": 6,
    "dislikes": 0,
    "comments": 1,
    "shares": 1,
    "subscribers_gained": 0,
    "subscribers_lost": 0,
    "estimated_revenue": "0.09",
    "estimated_ad_revenue": "0.01",
    "estimated_red_partner_revenue": "0.08",
    "country_stats": {
        "US": {"views": 50, "watch_time_minutes": 70},
        "GB": {"views": 30, "watch_time_minutes": 50}
    },
    "source_stats": {
        "SEARCH": {"views": 20, "watch_time_minutes": 30},
        "ADVERTISING": {"views": 60, "watch_time_minutes": 90}
    },
    "demographics": {
        "AGE_25_34_MALE": {"views": 2},
        "AGE_25_34_FEMALE": {"views": 1}
    },
    "device_stats": {
        "TABLET": {"views": 70, "watch_time_minutes": 110},
        "COMPUTER": {"views": 10, "watch_time_minutes": 10}
    },
    "playback_stats": {
        "0": {"views": 62, "watch_time_minutes": 121}
    },
    "created_at": "2026-03-29T10:15:30.000000Z",
    "updated_at": "2026-03-29T10:15:30.000000Z"
}
Field Type Description
data_date string Date of the statistics, as YYYY-MM-DDT00:00:00.000000Z
track_id integer Limbo track ID
dsp_id integer 2 (YouTube Content ID) or 3 (YouTube Art Track)
album_id integer Limbo album ID
genre_id integer Genre ID
content_type string content_id for dsp_id 2, art_track for dsp_id 3
views integer Views
watch_time_minutes integer Watch time in minutes
likes integer Likes
dislikes integer Dislikes
comments integer Comments
shares integer Shares
subscribers_gained integer Subscribers gained
subscribers_lost integer Subscribers lost
estimated_revenue string Estimated revenue, as a decimal string (e.g. "0.09")
estimated_ad_revenue string Estimated ad revenue, as a decimal string
estimated_red_partner_revenue string Estimated Red partner revenue, as a decimal string
country_stats object Keyed by country code: {"US": {"views": n, "watch_time_minutes": n}}
source_stats object Keyed by traffic source: {"SEARCH": {"views": n, "watch_time_minutes": n}}
demographics object or null Keyed by age group and gender: {"AGE_25_34_MALE": {"views": n}}. null when empty
device_stats object Keyed by device type: {"TABLET": {"views": n, "watch_time_minutes": n}}
playback_stats object Keyed by playback type, each with views and watch_time_minutes
created_at string Time the report was generated
updated_at string Time the report was generated

Apple Music Records (dsp_id 4)

Every *_stats, city_stats and demographics entry has the shape {KEY: {"plays": n, "listeners": n}}.

{
    "data_date": "2026-03-28T00:00:00.000000Z",
    "track_id": 5012,
    "apple_track_id": "1600000001",
    "apple_album_id": null,
    "apple_artist_id": null,
    "listener_count": 3,
    "play_count": 5,
    "storefront_stats": {"US": {"plays": 5, "listeners": 3}},
    "device_stats": {"MOBILE": {"plays": 5, "listeners": 3}},
    "source_stats": {"DISCOVERY": {"plays": 5, "listeners": 3}},
    "audio_format_stats": {"COMPRESSED_AUDIO": {"plays": 5, "listeners": 3}},
    "end_reason_stats": {"SKIP": {"plays": 2, "listeners": 1}},
    "container_stats": {"RADIO_STATION": {"plays": 5, "listeners": 3}},
    "container_sub_stats": {"DISCOVERY_RADIO_STATION": {"plays": 5, "listeners": 3}},
    "device_os_stats": {"IOS": {"plays": 5, "listeners": 3}},
    "subscription_type_stats": {"INDIVIDUAL_MONTHLY": {"plays": 5, "listeners": 3}},
    "subscription_mode_stats": {"PAID": {"plays": 5, "listeners": 3}},
    "city_stats": {"1000001": {"plays": 2, "listeners": 1}},
    "demographics": {"AGE_25_TO_34_FEMALE": {"plays": 2, "listeners": 1}},
    "created_at": "2026-03-29T10:15:30.000000Z",
    "updated_at": "2026-03-29T10:15:30.000000Z"
}
Field Type Description
data_date string Date of the statistics, as YYYY-MM-DDT00:00:00.000000Z
track_id integer Limbo track ID
apple_track_id string Apple Music track identifier
apple_album_id string or null Apple Music album identifier. May be null
apple_artist_id string or null Apple Music artist identifier. May be null
listener_count integer Listeners
play_count integer Plays
storefront_stats object Keyed by storefront (country code)
device_stats object Keyed by device type
source_stats object Keyed by playback source
audio_format_stats object Keyed by audio format
end_reason_stats object Keyed by the reason playback ended
container_stats object Keyed by playback container
container_sub_stats object Keyed by playback container subtype
device_os_stats object Keyed by device operating system
subscription_type_stats object Keyed by subscription type
subscription_mode_stats object Keyed by subscription mode
city_stats object Keyed by city ID
demographics object Keyed by age group and gender
created_at string Time the report was generated
updated_at string Time the report was generated

Amazon Music Records (dsp_id 6)

Every breakdown has the shape {KEY: {"streams": n, "offline_plays": n, "play_duration_seconds": n}}.

{
    "data_date": "2026-03-28T00:00:00.000000Z",
    "track_id": 5012,
    "album_id": 1830,
    "genre_id": 12,
    "streams": 4,
    "offline_plays": 1,
    "play_duration_seconds": 800,
    "country_stats": {"US": {"streams": 4, "offline_plays": 1, "play_duration_seconds": 800}},
    "device_stats": {"MOBILE": {"streams": 4, "offline_plays": 1, "play_duration_seconds": 800}},
    "source_stats": {"SONGS": {"streams": 4, "offline_plays": 1, "play_duration_seconds": 800}},
    "quality_stats": {"STANDARD": {"streams": 4, "offline_plays": 1, "play_duration_seconds": 800}},
    "plan_stats": {"Individual Monthly": {"streams": 4, "offline_plays": 1, "play_duration_seconds": 800}},
    "created_at": "2026-03-29T10:15:30.000000Z",
    "updated_at": "2026-03-29T10:15:30.000000Z"
}
Field Type Description
data_date string Date of the statistics, as YYYY-MM-DDT00:00:00.000000Z
track_id integer Limbo track ID
album_id integer Limbo album ID
genre_id integer Genre ID
streams integer Streams
offline_plays integer Offline plays
play_duration_seconds integer Total play duration in seconds
country_stats object Keyed by country code
device_stats object Keyed by device type
source_stats object Keyed by playback source
quality_stats object Keyed by audio quality
plan_stats object Keyed by subscription plan
created_at string Time the report was generated
updated_at string Time the report was generated

TikTok Records (dsp_id 9)

There is one record per track and content_type (UGC or PGC).

{
    "data_date": "2026-03-28T00:00:00.000000Z",
    "track_id": 5012,
    "album_id": 1830,
    "content_type": "UGC",
    "genre_id": 12,
    "views": 300,
    "watch_time_seconds": 900,
    "creations": 2,
    "comments": 4,
    "likes": 20,
    "shares": 3,
    "favourites": 5,
    "country_stats": {
        "US": {
            "views": 300,
            "watch_time_seconds": 900,
            "creations": 2,
            "comments": 4,
            "likes": 20,
            "shares": 3,
            "favourites": 5
        }
    },
    "created_at": "2026-03-29T10:15:30.000000Z",
    "updated_at": "2026-03-29T10:15:30.000000Z"
}
Field Type Description
data_date string Date of the statistics, as YYYY-MM-DDT00:00:00.000000Z
track_id integer Limbo track ID
album_id integer Limbo album ID
content_type string UGC or PGC
genre_id integer Genre ID
views integer Views
watch_time_seconds integer Watch time in seconds
creations integer Videos created with the track
comments integer Comments
likes integer Likes
shares integer Shares
favourites integer Favourites
country_stats object Keyed by country code: views, watch_time_seconds, creations, comments, likes, shares, favourites
created_at string Time the report was generated
updated_at string Time the report was generated

Limits

  • One day per report — each request generates a report for a single date.
  • Up to 20 active reports per user — you can request reports for every supported DSP in parallel.
  • Download URL valid for 12 hours — poll again to get a fresh URL if expired.
  • Owner-restricted — you can only see your own reports and your own tracks' data.

Complete Example

Here's a complete workflow requesting every supported DSP for the same day:

# 1. Request one report per supported DSP
for dsp in 1 2 3 4 6 9; do
  curl -X POST /analytics-reports \
    -H "Authorization: Bearer {token}" \
    -H "Content-Type: application/json" \
    -d "{\"dsp_id\": $dsp, \"date\": \"2026-03-28\"}"
done
# → {"data": {"id": 6, "status": "pending", ...}}  (one response per DSP: keep each id)

# 2. Poll each id from step 1 until it is completed (every 1 minute); report 6 shown
curl /analytics-reports/6 -H "Authorization: Bearer {token}"
# → {"data": {"status": "completed", "download_url": "https://...", ...}}

# 3. Download each completed report's ZIP from its own download_url (no auth needed)
curl -o report-6-2026-03-28.zip "{download_url}"