From 729d3eaadfcd5f9a64d0e6faec989662b2ddc5b0 Mon Sep 17 00:00:00 2001 From: Sweets Sweetman Date: Tue, 9 Jun 2026 09:06:21 -0500 Subject: [PATCH 1/3] docs(research): add GET /api/research/track/stats (per-track Songstats stats) Contract-first for recoupable/chat#1789. Adds the OpenAPI path + ResearchTrackStatsResponse/ResearchTrackStat schemas (mirrors /api/research/metrics, scoped to one track), the reference page, and the Catalog nav entry (surfaces in the generated llms.txt). Co-Authored-By: Claude Opus 4.8 (1M context) --- api-reference/openapi/research.json | 227 +++++++++++++++++++++++++ api-reference/research/track-stats.mdx | 4 + docs.json | 3 +- 3 files changed, 233 insertions(+), 1 deletion(-) create mode 100644 api-reference/research/track-stats.mdx diff --git a/api-reference/openapi/research.json b/api-reference/openapi/research.json index 93eb9f59..bdbe2e68 100644 --- a/api-reference/openapi/research.json +++ b/api-reference/openapi/research.json @@ -2500,6 +2500,174 @@ } } } + }, + "/api/research/track/stats": { + "get": { + "description": "Get per-track, per-source current stats by ISRC (or Songstats/Spotify/Apple Music track id) — absolute `streams_total` plus playlist and chart reach. Thin passthrough to Songstats Get Track Current Stats; mirrors the `stats[].data` envelope of `GET /api/research/metrics`, but scoped to a single recording instead of the whole artist catalog.", + "parameters": [ + { + "name": "isrc", + "in": "query", + "required": false, + "description": "Track ISRC. Provide exactly one of `isrc`, `songstats_track_id`, `spotify_track_id`, or `apple_music_track_id`.", + "schema": { + "type": "string", + "example": "USQY51771120" + } + }, + { + "name": "songstats_track_id", + "in": "query", + "required": false, + "description": "Songstats track id (returned by `GET /api/research/tracks`). Alternative to `isrc`.", + "schema": { + "type": "string" + } + }, + { + "name": "spotify_track_id", + "in": "query", + "required": false, + "description": "Spotify track id. Alternative to `isrc`.", + "schema": { + "type": "string" + } + }, + { + "name": "apple_music_track_id", + "in": "query", + "required": false, + "description": "Apple Music track id. Alternative to `isrc`.", + "schema": { + "type": "string" + } + }, + { + "name": "source", + "in": "query", + "required": true, + "description": "Comma-separated list of sources, or `all`. e.g. `spotify`, `apple_music`, `deezer`, `youtube`.", + "schema": { + "type": "string", + "example": "spotify" + } + }, + { + "name": "with_playlists", + "in": "query", + "required": false, + "description": "Include the per-source `playlists[]` array in `data`.", + "schema": { + "type": "boolean" + } + }, + { + "name": "with_charts", + "in": "query", + "required": false, + "description": "Include the per-source `charts[]` array in `data`.", + "schema": { + "type": "boolean" + } + }, + { + "name": "with_stations", + "in": "query", + "required": false, + "description": "Include radio `stations[]` data where available.", + "schema": { + "type": "boolean" + } + }, + { + "name": "with_videos", + "in": "query", + "required": false, + "description": "Include video data where available.", + "schema": { + "type": "boolean" + } + }, + { + "name": "with_links", + "in": "query", + "required": false, + "description": "Include external links where available.", + "schema": { + "type": "boolean" + } + }, + { + "name": "only_current", + "in": "query", + "required": false, + "description": "Return only current counters, omitting historical/total breakdowns where applicable.", + "schema": { + "type": "boolean" + } + }, + { + "name": "limit", + "in": "query", + "required": false, + "description": "Max items in toggled arrays (e.g. `playlists[]`).", + "schema": { + "type": "integer" + } + }, + { + "name": "offset", + "in": "query", + "required": false, + "description": "Pagination offset for toggled arrays.", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "Per-track current stats, one entry per requested source.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ResearchTrackStatsResponse" + } + } + } + }, + "400": { + "description": "Validation error — missing identifier or `source`.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ResearchErrorResponse" + } + } + } + }, + "401": { + "description": "Authentication failed — invalid or missing API key.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ResearchErrorResponse" + } + } + } + }, + "404": { + "description": "No track matched the supplied identifier.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ResearchErrorResponse" + } + } + } + } + } + } } }, "components": { @@ -4472,6 +4640,65 @@ } }, "additionalProperties": true + }, + "ResearchTrackStatsResponse": { + "type": "object", + "description": "Per-track current statistics for the requested `source`(s). Mirrors `ResearchMetricsResponse`, scoped to a single recording.", + "properties": { + "status": { + "type": "string", + "example": "success" + }, + "result": { + "type": "string", + "example": "success" + }, + "message": { + "type": "string", + "example": "Data Retrieved." + }, + "stats": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ResearchTrackStat" + } + }, + "track_info": { + "$ref": "#/components/schemas/ResearchTrackInfo" + } + } + }, + "ResearchTrackStat": { + "type": "object", + "description": "Per-source current statistics for a single track.", + "properties": { + "source": { + "type": "string", + "description": "Platform the stats belong to, e.g. `spotify`, `apple_music`, `deezer`, `youtube`." + }, + "data": { + "type": "object", + "description": "Per-source counters for this track. `streams_total` is the absolute play count. Keys vary by `source`; optional `playlists[]` / `charts[]` / `reposters[]` arrays appear when the matching `with_*` toggle is set.", + "additionalProperties": true, + "example": { + "streams_total": 84213771, + "popularity_current": 64, + "playlists_current": 1200, + "playlists_total": 4830, + "playlist_reach_current": 18450201, + "playlist_reach_total": 51200988, + "charts_current": 3, + "charts_total": 41, + "shazams_total": 220145, + "charted_cities_total": 87, + "charted_countries_total": 23, + "favorites_total": 0, + "reposts_total": 0, + "engagement_rate_total": 0 + } + } + }, + "additionalProperties": true } }, "responses": { diff --git a/api-reference/research/track-stats.mdx b/api-reference/research/track-stats.mdx new file mode 100644 index 00000000..808b5774 --- /dev/null +++ b/api-reference/research/track-stats.mdx @@ -0,0 +1,4 @@ +--- +title: 'Track Stats' +openapi: "/api-reference/openapi/research.json GET /api/research/track/stats" +--- diff --git a/docs.json b/docs.json index 952e4a19..0f5e64c1 100644 --- a/docs.json +++ b/docs.json @@ -118,7 +118,8 @@ "pages": [ "api-reference/research/albums", "api-reference/research/tracks", - "api-reference/research/track" + "api-reference/research/track", + "api-reference/research/track-stats" ] }, { From f990e5e51f08be56c95e16022d257b8fc6550e80 Mon Sep 17 00:00:00 2001 From: Sweets Sweetman Date: Tue, 9 Jun 2026 09:29:40 -0500 Subject: [PATCH 2/3] docs(research): reconcile track/stats contract with verified live response Verified GET /api/research/track/stats against the api#653 preview with the real Mac Miller ISRC (USQY51771120). Adjusted the contract to match reality: - add top-level `source_ids` (response returns it; superset of `stats` sources) - add `songstats_track_id` to ResearchTrackInfo (track_info returns it) - round out the stat example with editorial-playlist counters 404-on-unknown-ISRC confirmed accurate (kept as documented). Co-Authored-By: Claude Opus 4.8 (1M context) --- api-reference/openapi/research.json | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/api-reference/openapi/research.json b/api-reference/openapi/research.json index bdbe2e68..6931e18f 100644 --- a/api-reference/openapi/research.json +++ b/api-reference/openapi/research.json @@ -4579,6 +4579,10 @@ "type": "object", "description": "Full track record.", "properties": { + "songstats_track_id": { + "type": "string", + "description": "Songstats track id — pass to `GET /api/research/track/stats` or `GET /api/research/track`." + }, "title": { "type": "string" }, @@ -4665,6 +4669,13 @@ }, "track_info": { "$ref": "#/components/schemas/ResearchTrackInfo" + }, + "source_ids": { + "type": "array", + "description": "All source identifiers Songstats tracks for this recording — a superset of the sources returned in `stats`.", + "items": { + "type": "string" + } } } }, @@ -4694,7 +4705,9 @@ "charted_countries_total": 23, "favorites_total": 0, "reposts_total": 0, - "engagement_rate_total": 0 + "engagement_rate_total": 0, + "playlists_editorial_current": 12, + "playlists_editorial_total": 58 } } }, From 352e87ccc37fc802ea09899f3a91ae423b200cf9 Mon Sep 17 00:00:00 2001 From: Sweets Sweetman Date: Tue, 9 Jun 2026 09:45:34 -0500 Subject: [PATCH 3/3] docs(research): simplify track/stats descriptions (KISS, match neighbors) Address review: trim the endpoint description to neighbor brevity (drop passthrough/envelope/comparison detail) and shorten the identifier params (state 'exactly one' once on isrc instead of repeating the full list). Co-Authored-By: Claude Opus 4.8 (1M context) --- api-reference/openapi/research.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/api-reference/openapi/research.json b/api-reference/openapi/research.json index 6931e18f..896c338a 100644 --- a/api-reference/openapi/research.json +++ b/api-reference/openapi/research.json @@ -2503,13 +2503,13 @@ }, "/api/research/track/stats": { "get": { - "description": "Get per-track, per-source current stats by ISRC (or Songstats/Spotify/Apple Music track id) — absolute `streams_total` plus playlist and chart reach. Thin passthrough to Songstats Get Track Current Stats; mirrors the `stats[].data` envelope of `GET /api/research/metrics`, but scoped to a single recording instead of the whole artist catalog.", + "description": "Get per-track, per-source current stats by ISRC or track id — absolute `streams_total`, playlist reach, and chart counts.", "parameters": [ { "name": "isrc", "in": "query", "required": false, - "description": "Track ISRC. Provide exactly one of `isrc`, `songstats_track_id`, `spotify_track_id`, or `apple_music_track_id`.", + "description": "Track ISRC. Provide exactly one track identifier.", "schema": { "type": "string", "example": "USQY51771120" @@ -2519,7 +2519,7 @@ "name": "songstats_track_id", "in": "query", "required": false, - "description": "Songstats track id (returned by `GET /api/research/tracks`). Alternative to `isrc`.", + "description": "Songstats track id (from `GET /api/research/tracks`).", "schema": { "type": "string" } @@ -2528,7 +2528,7 @@ "name": "spotify_track_id", "in": "query", "required": false, - "description": "Spotify track id. Alternative to `isrc`.", + "description": "Spotify track id.", "schema": { "type": "string" } @@ -2537,7 +2537,7 @@ "name": "apple_music_track_id", "in": "query", "required": false, - "description": "Apple Music track id. Alternative to `isrc`.", + "description": "Apple Music track id.", "schema": { "type": "string" }