-
Notifications
You must be signed in to change notification settings - Fork 4
docs(research): add GET /api/research/track/stats — per-track Songstats stats #236
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
729d3ea
f990e5e
352e87c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -2500,6 +2500,174 @@ | |||||
| } | ||||||
| } | ||||||
| } | ||||||
| }, | ||||||
| "/api/research/track/stats": { | ||||||
| "get": { | ||||||
| "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 track identifier.", | ||||||
| "schema": { | ||||||
| "type": "string", | ||||||
| "example": "USQY51771120" | ||||||
| } | ||||||
| }, | ||||||
| { | ||||||
| "name": "songstats_track_id", | ||||||
| "in": "query", | ||||||
| "required": false, | ||||||
| "description": "Songstats track id (from `GET /api/research/tracks`).", | ||||||
| "schema": { | ||||||
| "type": "string" | ||||||
| } | ||||||
| }, | ||||||
| { | ||||||
| "name": "spotify_track_id", | ||||||
| "in": "query", | ||||||
| "required": false, | ||||||
| "description": "Spotify track id.", | ||||||
| "schema": { | ||||||
| "type": "string" | ||||||
| } | ||||||
| }, | ||||||
| { | ||||||
| "name": "apple_music_track_id", | ||||||
| "in": "query", | ||||||
| "required": false, | ||||||
| "description": "Apple Music track id.", | ||||||
| "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": { | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: Missing Prompt for AI agents |
||||||
| "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" | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
|
Comment on lines
+2628
to
+2669
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add the standard
Proposed fix "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`"
}
}
}
+ },
+ "501": {
+ "$ref": "`#/components/responses/ResearchDataSourceUnsupported`"
}
}🤖 Prompt for AI Agents |
||||||
| } | ||||||
| } | ||||||
| }, | ||||||
| "components": { | ||||||
|
|
@@ -4411,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" | ||||||
| }, | ||||||
|
|
@@ -4472,6 +4644,74 @@ | |||||
| } | ||||||
| }, | ||||||
| "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" | ||||||
| }, | ||||||
| "source_ids": { | ||||||
| "type": "array", | ||||||
| "description": "All source identifiers Songstats tracks for this recording — a superset of the sources returned in `stats`.", | ||||||
| "items": { | ||||||
| "type": "string" | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
| }, | ||||||
| "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.", | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: The Prompt for AI agents
Suggested change
|
||||||
| "additionalProperties": true, | ||||||
|
coderabbitai[bot] marked this conversation as resolved.
|
||||||
| "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, | ||||||
| "playlists_editorial_current": 12, | ||||||
| "playlists_editorial_total": 58 | ||||||
| } | ||||||
| } | ||||||
| }, | ||||||
| "additionalProperties": true | ||||||
| } | ||||||
| }, | ||||||
| "responses": { | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,4 @@ | ||||||
| --- | ||||||
| title: 'Track Stats' | ||||||
| openapi: "/api-reference/openapi/research.json GET /api/research/track/stats" | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use the required API-reference Line 3 uses a non-standard Proposed fix-openapi: "/api-reference/openapi/research.json GET /api/research/track/stats"
+openapi: 'GET /api/research/track/stats'As per coding guidelines, API reference MDX pages must be frontmatter-only and use 📝 Committable suggestion
Suggested change
🤖 Prompt for AI AgentsSource: Coding guidelines |
||||||
| --- | ||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this endpoint structure follow REST best practices?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes — it follows the established convention:
/api/research/track/playlists("Track Playlists") is the direct precedent for atrack/<sub-resource>endpoint, and like the rest of the research family it takes the identifier as a query param (?isrc=) rather than a path param. So/api/research/track/statsmirrors/api/research/track/playlistsexactly — no change.