Skip to content

📊 Analytics Reports

Generate and download daily streaming analytics for Spotify, Apple Music and YouTube.

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: pendingprocessingcompleted (or failed).

3

Download the file

When completed, the response includes a download_url — a pre-signed S3 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 3 Daily track stats including views, watch time, geography, and playback metrics.
Apple Music 4 Daily track stats including plays, listeners, and Shazam data.
You can retrieve the full list of DSPs from /api/v1/dsps. Only the DSPs listed above are supported for analytics reports.

Step 1: Create a Report

POST /analytics-reports
POST /api/v1/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), 3 (YouTube), or 4 (Apple).
date string Yes The date to export in YYYY-MM-DD format. Only a single day can be requested per report.

Example Request

POST /api/v1/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, 3, 4]
}

Error: Too many active reports (409)

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

Step 2: Poll Report Status

GET /analytics-reports/{id}
GET /api/v1/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. error field contains the reason. Check the error message and try again.

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"
    }
}

Polling Recommendation

We recommend polling every 1 minute. Most reports complete within 5–20 seconds depending on the DSP and dataset size.

Step 3: Download the File

The download_url is a pre-signed S3 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. After expiration, poll the report again with GET /analytics-reports/{id} to obtain a fresh URL. The file on S3 is not deleted — only the signed URL expires.

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": [
        {
            "id": 100001,
            "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, "": 13},
                "products": {"duo-master": 12, "family-sub": 28, "": 140},
                "access_levels": {"premium": 124, "free": 56},
                "subscription_types": []
            },
            "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": {
                "regions": {"US-CA": 42, "US-NY": 31, "GB-LON": 22},
                "geohashes": {"9q5c": 18, "dr5r": 14, "gcpu": 10},
                "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-29T02:15:00.000000Z",
            "updated_at": "2026-03-29T02:15:00.000000Z"
        },
        ...
    ]
}

The data array contains all records for the requested date. Each record is the raw model data as stored in the database, with all nested JSON fields (demographics, geographic stats, etc.) fully expanded.

Spotify Record Fields

Field Type Description
id integer Internal record ID
data_date datetime Date of the statistics
track_id integer Limbo track ID
track_name string Track title
album_id integer Limbo album ID
album_name string Album title
isrc string International Standard Recording Code
spotify_track_id string Spotify internal track identifier
country_stats object Per-country breakdown: streams, completion rate, reproduction time
demographics object Listener demographics: age groups, genders, products, access levels
aggregated_data object Totals: total_streams, total_skips, total_saves, total_shares
content_type_stats object Audio vs video stream breakdown
geographic_stats object Regions, geohashes, connection/reporting countries
device_platform_stats object Device types, OS, shuffle/repeat usage
source_stats object Playback sources and source URIs
engagement_stats object Completion rate, lyrics/canvas viewed, discovery streams
timestamp_reliability object Online vs offline stream counts

Limits

  • One day per report — each request generates a report for a single date.
  • Up to 20 concurrent reports per user — you can request reports for all 3 DSPs 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 all 3 DSPs for the same day:

# 1. Request reports for all 3 DSPs
curl -X POST /api/v1/analytics-reports \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{"dsp_id": 1, "date": "2026-03-28"}'
# → {"data": {"id": 6, "status": "pending", ...}}

curl -X POST /api/v1/analytics-reports \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{"dsp_id": 4, "date": "2026-03-28"}'
# → {"data": {"id": 7, "status": "pending", ...}}

curl -X POST /api/v1/analytics-reports \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{"dsp_id": 3, "date": "2026-03-28"}'
# → {"data": {"id": 8, "status": "pending", ...}}

# 2. Poll until completed (every 1 minute)
curl /api/v1/analytics-reports/6 -H "Authorization: Bearer {token}"
# → {"data": {"status": "completed", "download_url": "https://...", ...}}

# 3. Download the ZIP (no auth needed)
curl -o spotify-2026-03-28.zip "{download_url}"
curl -o apple-2026-03-28.zip "{download_url}"
curl -o youtube-2026-03-28.zip "{download_url}"