Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 74 additions & 6 deletions api-reference/openapi/releases.json
Original file line number Diff line number Diff line change
Expand Up @@ -1480,7 +1480,7 @@
}
}
},
"/api/catalogs/measurements": {
"/api/catalogs/{catalogId}/measurements": {
"get": {
"description": "Get the latest play counts and a derived valuation band for a catalog. Measurements are captured by [Create measurement job](/api-reference/research/measurement-jobs) runs; the band is computed at read time from the latest capture per song.",
"security": [
Expand All @@ -1494,13 +1494,46 @@
"parameters": [
{
"name": "catalogId",
"in": "query",
"description": "The unique identifier of the catalog to read measurements for. The catalog must belong to the authenticated account.",
"in": "path",
"description": "The unique identifier of the catalog to read measurements for. The catalog must belong to the authenticated account. Malformed (non-uuid) values are rejected with 400.",
"required": true,
"schema": {
"type": "string",
"format": "uuid"
}
},
{
"name": "artist_account_id",
"in": "query",
"description": "Optional artist account to scope the read to. When present, measurements and the valuation band cover only the catalog's songs linked to this artist account (via song_artists); when absent, the whole catalog. A song linked to multiple artists counts for each of them. An artist with no linked measured songs in the catalog yields measured_song_count 0 and an empty measurements array. Malformed (non-uuid) values are rejected with 400.",
"required": false,
"schema": {
"type": "string",
"format": "uuid"
}
},
{
"name": "page",
"in": "query",
"description": "Page number of the measurements array (default 1). Pagination only windows the measurements rows - the aggregates always cover the entire scope. Invalid values are rejected with 400.",
"required": false,
"schema": {
"type": "integer",
"minimum": 1,
"default": 1
}
},
{
"name": "limit",
"in": "query",
"description": "Number of measurement rows per page (default 50, max 100). Invalid values are rejected with 400.",
"required": false,
"schema": {
"type": "integer",
"minimum": 1,
"maximum": 100,
"default": 50
}
}
],
"responses": {
Expand All @@ -1515,7 +1548,7 @@
}
},
"400": {
"description": "Bad request - missing or invalid catalogId",
"description": "Bad request - malformed catalogId or artist_account_id, or invalid page/limit",
"content": {
"application/json": {
"schema": {
Expand Down Expand Up @@ -2456,14 +2489,27 @@
"items": {
"$ref": "#/components/schemas/CatalogTrackMeasurement"
},
"description": "One entry per catalog song that has at least one measurement: the newest capture per ISRC. Songs never measured are omitted."
"description": "One page of latest-per-ISRC measurements (the requested page/limit window), sorted by play count descending. One entry per measured song: the newest capture per ISRC; songs never measured are omitted. Use measured_song_count / pagination for the size of the full set - the aggregates are NOT limited to this page."
},
"pagination": {
"$ref": "#/components/schemas/CatalogMeasurementsPagination"
},
"measured_song_count": {
"type": "integer",
"description": "Total number of measured songs in scope (the whole catalog, or the artist subset when filtered), computed over the entire scope regardless of pagination - no row cap"
},
"valuation": {
"$ref": "#/components/schemas/ValuationBand"
},
"total_streams": {
"type": "integer",
"description": "Sum of the latest play counts across all measured songs"
"description": "Sum of the latest play counts across ALL measured songs in scope, computed in a single SQL aggregate regardless of pagination - no row cap"
},
"artist_account_id": {
"type": "string",
"format": "uuid",
"nullable": true,
"description": "Echoes the applied artist_account_id filter: the uuid when the response was scoped to that artist, null when the response covers the whole catalog. Clients that request an artist scope should verify this echo before treating the numbers as artist-scoped."
},
"catalog_age_years": {
"type": "integer",
Expand All @@ -2475,6 +2521,28 @@
}
}
},
"CatalogMeasurementsPagination": {
"type": "object",
"description": "Pagination metadata for the measurements array - the same envelope as catalog songs",
"properties": {
"total_count": {
"type": "integer",
"description": "Total number of measured songs in scope (equals measured_song_count)"
},
"page": {
"type": "integer",
"description": "Current page number"
},
"limit": {
"type": "integer",
"description": "Number of measurement rows per page"
},
"total_pages": {
"type": "integer",
"description": "Total number of pages available"
}
}
},
"CatalogTrackMeasurement": {
"type": "object",
"description": "The most recent Spotify play-count measurement for one song (ISRC) in the catalog",
Expand Down
2 changes: 1 addition & 1 deletion api-reference/songs/catalog-measurements.mdx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
---
title: Get Catalog Measurements
openapi: get /api/catalogs/measurements
openapi: get /api/catalogs/{catalogId}/measurements
---