Developers
OnRepeat Stats API
Listening statistics for the signed-in owner, and shared public profiles under /v1/public/{handle}. /v1 changes are additive; removals and changes of meaning go to /v2.
Version 1.7.0 · base URL https://api.onrepeatstats.com · OpenAPI document · generated from it, so this page always matches the API.
Authentication
Owner operations read the signed-in account’s own data. Apps sign in with OAuth 2.1: OAuth 2.1 with PKCE (S256). Ask for resource https://api.onrepeatstats.com/v1 to get a 15-minute JWT access token; offline_access adds a 60-day refresh token that rotates on use. Claude and ChatGPT identify themselves with a client metadata document (an HTTPS client_id on their own host); other clients register dynamically (RFC 7591) or are registered by OnRepeat. Metadata: https://onrepeatstats.com/.well-known/oauth-authorization-server.
history:read: Read the listening history and stats, account and import statusoffline_access: A refresh token, to stay connected for 60 days
Send the access token as Authorization: Bearer …. Operations marked “Session only” are for the website and app. Public operations need nothing and answer only for shared profiles. AI assistants use the MCP server instead.
Operations
- Stats
GET /v1/stats/summaryA range at a glance: totals, top artists, albums and songs, trend and hoursGET /v1/stats/compareTwo ranges side by side: totals, and how the top entities movedGET /v1/stats/discoveriesArtists, albums or songs first heard in a rangeGET /v1/stats/rankingsTop artists, albums or songsGET /v1/stats/entities/{id}One entity: totals, first and last plays, trend and top childrenGET /v1/stats/trendListening by day, week, month or yearGET /v1/stats/historyListening history, newest firstGET /v1/stats/recentThe latest playsGET /v1/stats/searchFind artists, albums and songs by nameGET /v1/stats/resolveEntities for an ID, an external ID or a title and artistGET /v1/stats/exclusionsArtists, albums and songs left out of statsPOST /v1/stats/exclusionsLeave an artist, album or song out of statsDELETE /v1/stats/exclusions/{id}Count an artist, album or song in stats again
- History
GET /v1/history/rulesChanges to the history: removals, merges and splitsPOST /v1/history/rulesRemove plays, or merge or keep apart artists, albums and songsDELETE /v1/history/rules/{id}Undo a change to the history
- Imports and setup
GET /v1/export-guides/{provider}How to get a listening history export from Apple Music, Spotify or Last.fmGET /v1/handoffs/uploadA link to the upload page for this accountGET /v1/handoffs/apple-musicA link to connect Apple Music detection, or why it isn’t availableGET /v1/importsThe account’s imports, newest first, and its import limitsGET /v1/imports/{id}One importGET /v1/apple-music/connectionApple Music detection: availability, connection, and its checksGET /v1/handoffs/checkWhether a link from an assistant was made for the signed-in account
- Account
GET /v1/settingsThe account's settingsPATCH /v1/settingsChange the account's settingsGET /v1/account/statusWhere the account stands: history, imports, Apple Music and next stepsPOST /v1/settings/defaultsSet the time zone and week start where they are still unsetGET /v1/account/connectionsApps connected to the accountPOST /v1/account/connections/revokeDisconnect an appGET /v1/billingThe account's subscriptionPOST /v1/billing/offerWhich subscription the app offersPOST /v1/billing/apple/transactionsSend a purchase from the app
- Public profiles
GET /v1/public/{handle}/profileA public profile: totals and the top 5 artists, albums and songs of a periodGET /v1/public/{handle}/profile/detailsA public profile’s slower sections: every day, records, hours, weekdays, new artistsGET /v1/public/{handle}/profile/topA public profile’s top 25 artists, albums or songs of a period, and how its top 5 movedGET /v1/public/{handle}/profile/recentA public profile’s recently played songs (when it shows them)
- Profile settings
GET /v1/profileThe account's public profile settingsPATCH /v1/profileChange the account's public profileGET /v1/profile/handleWhether a handle is availableGET /v1/profile/viewA public profile: totals and the top 5 artists, albums and songs of a periodGET /v1/profile/detailsA public profile’s slower sections: every day, records, hours, weekdays, new artistsGET /v1/profile/topA public profile’s top 25 artists, albums or songs of a period, and how its top 5 movedGET /v1/profile/recentA public profile’s recently played songs (when it shows them)
- Data export
POST /v1/exportsStart a download of all the account’s dataGET /v1/exports/statusThe account’s data download: the latest one and whether another can startGET /v1/exports/{id}/downloadDownload a finished data export (a ZIP)
Stats
GET /v1/stats/summary
A range at a glance: totals, top artists, albums and songs, trend and hours getPeriodSummary
Totals and active days, the top artists, albums and songs, the trend (days, months or years by the length of the listening), the peak bucket, and listening by local hour (estimated from a sample on long ranges).
Ownerscope history:readOAuth or sessionMCP tool
Parameters
| Name | Type | Description |
|---|---|---|
from | date | First local day, inclusive. Absent: from the beginning. |
to | date | Last local day, inclusive. Absent: through today. |
by | "minutes" | "plays" | default "minutes" |
combineSongVersions | boolean | Group a song's versions (live, remixes) together. Default: the account setting. |
combineAlbumEditions | boolean | Group an album's editions together. Default: the account setting. |
provider | "apple" | "spotify" | Count one provider only: apple or spotify. Default: both. |
limit | integer | Items in each top list. · default 5 · 1–10 |
Returns StatsSummary · other statuses 400, 401
GET /v1/stats/compare
Two ranges side by side: totals, and how the top entities moved compareRanges
Each range’s totals, and the top `limit` artists, albums or songs of each, with their position and numbers in both and whether they are new, gone, up, down or the same from a to b.
Ownerscope history:readOAuth or sessionMCP tool
Parameters
| Name | Type | Description |
|---|---|---|
kind required | "artist" | "album" | "song" | |
aFrom | date | First range, first local day. Absent: from the beginning. |
aTo | date | First range, last local day. Absent: through today. |
bFrom | date | Second range, first local day. Absent: from the beginning. |
bTo | date | Second range, last local day. Absent: through today. |
by | "minutes" | "plays" | default "minutes" |
combineSongVersions | boolean | Group a song's versions (live, remixes) together. Default: the account setting. |
combineAlbumEditions | boolean | Group an album's editions together. Default: the account setting. |
provider | "apple" | "spotify" | Count one provider only: apple or spotify. Default: both. |
limit | integer | The top of each range to compare. · default 10 · 1–25 |
Returns StatsComparison · other statuses 400, 401
GET /v1/stats/discoveries
Artists, albums or songs first heard in a range listDiscoveries
Entities whose first listening ever falls within the range, ranked by their listening within it, each with the day it was first heard.
Ownerscope history:readOAuth or sessionMCP tool
Parameters
| Name | Type | Description |
|---|---|---|
kind required | "artist" | "album" | "song" | |
from | date | First local day, inclusive. Absent: from the beginning. |
to | date | Last local day, inclusive. Absent: through today. |
by | "minutes" | "plays" | default "minutes" |
combineSongVersions | boolean | Group a song's versions (live, remixes) together. Default: the account setting. |
combineAlbumEditions | boolean | Group an album's editions together. Default: the account setting. |
provider | "apple" | "spotify" | Count one provider only: apple or spotify. Default: both. |
limit | integer | default 20 · 1–50 |
cursor | string | nextCursor from the previous page. |
Returns StatsDiscoveries · other statuses 400, 401
GET /v1/stats/rankings
Top artists, albums or songs getRankings
A chart for a range of local days, by listening time or plays, with stable entity IDs.
Ownerscope history:readOAuth or sessionMCP tool
Parameters
| Name | Type | Description |
|---|---|---|
kind required | "artist" | "album" | "song" | |
from | date | First local day, inclusive. Absent: from the beginning. |
to | date | Last local day, inclusive. Absent: through today. |
by | "minutes" | "plays" | default "minutes" |
combineSongVersions | boolean | Group a song's versions (live, remixes) together. Default: the account setting. |
combineAlbumEditions | boolean | Group an album's editions together. Default: the account setting. |
within | string | Only count listening within this entity (an artist, album, song or recording ID). |
provider | "apple" | "spotify" | Count one provider only: apple or spotify. Default: both. |
limit | integer | default 50 · 1–100 |
cursor | string | nextCursor from the previous page. |
Returns StatsRankings · other statuses 400, 401, 404
GET /v1/stats/entities/{id}
One entity: totals, first and last plays, trend and top children getEntityStats
Ownerscope history:readOAuth or sessionMCP tool
Parameters
| Name | Type | Description |
|---|---|---|
id required path | string | |
from | date | First local day, inclusive. Absent: from the beginning. |
to | date | Last local day, inclusive. Absent: through today. |
by | "minutes" | "plays" | default "minutes" |
combineSongVersions | boolean | Group a song's versions (live, remixes) together. Default: the account setting. |
combineAlbumEditions | boolean | Group an album's editions together. Default: the account setting. |
bucket | "day" | "week" | "month" | "year" | default "month" |
weekStart | integer | First day of week buckets: 0 Sunday … 6 Saturday. Default: the account's setting, else Sunday. · 0–6 |
children | integer | Top entities of each other kind to include. · default 10 · 1–50 |
Returns StatsEntity · other statuses 400, 401, 404
GET /v1/stats/trend
Listening by day, week, month or year getTrend
Ownerscope history:readOAuth or sessionMCP tool
Parameters
| Name | Type | Description |
|---|---|---|
from | date | First local day, inclusive. Absent: from the beginning. |
to | date | Last local day, inclusive. Absent: through today. |
by | "minutes" | "plays" | default "minutes" |
combineSongVersions | boolean | Group a song's versions (live, remixes) together. Default: the account setting. |
combineAlbumEditions | boolean | Group an album's editions together. Default: the account setting. |
bucket | "day" | "week" | "month" | "year" | default "month" |
weekStart | integer | First day of week buckets: 0 Sunday … 6 Saturday. Default: the account's setting, else Sunday. · 0–6 |
within | string | Only count listening within this entity (an artist, album, song or recording ID). |
provider | "apple" | "spotify" | Count one provider only: apple or spotify. Default: both. |
cursor | string | nextCursor from the previous page. |
Returns StatsTrend · other statuses 400, 401, 404
GET /v1/stats/history
Listening history, newest first listHistory
Source rows with their time window and precision, cursor-paged.
Ownerscope history:readOAuth or sessionMCP tool
Parameters
| Name | Type | Description |
|---|---|---|
from | date | First local day, inclusive. Absent: from the beginning. |
to | date | Last local day, inclusive. Absent: through today. |
by | "minutes" | "plays" | default "minutes" |
combineSongVersions | boolean | Group a song's versions (live, remixes) together. Default: the account setting. |
combineAlbumEditions | boolean | Group an album's editions together. Default: the account setting. |
within | string | Only count listening within this entity (an artist, album, song or recording ID). |
provider | "apple" | "spotify" | Count one provider only: apple or spotify. Default: both. |
cursor | string | nextCursor from the previous page. |
limit | integer | default 50 · 1–100 |
include | "music" | "all" | music: the app's list (music rows with a play or listening time). all: every source row. · default "music" |
Returns StatsHistory · other statuses 400, 401, 404
GET /v1/stats/recent
The latest plays listRecent
The first page of history without a range.
Ownerscope history:readOAuth or sessionMCP tool
Parameters
| Name | Type | Description |
|---|---|---|
by | "minutes" | "plays" | default "minutes" |
combineSongVersions | boolean | Group a song's versions (live, remixes) together. Default: the account setting. |
combineAlbumEditions | boolean | Group an album's editions together. Default: the account setting. |
provider | "apple" | "spotify" | Count one provider only: apple or spotify. Default: both. |
cursor | string | nextCursor from the previous page. |
limit | integer | default 20 · 1–50 |
include | "music" | "all" | music: the app's list (music rows with a play or listening time). all: every source row. · default "music" |
Returns StatsHistory · other statuses 400, 401
GET /v1/stats/search
Find artists, albums and songs by name search
Ownerscope history:readOAuth or sessionMCP tool
Parameters
| Name | Type | Description |
|---|---|---|
by | "minutes" | "plays" | default "minutes" |
combineSongVersions | boolean | Group a song's versions (live, remixes) together. Default: the account setting. |
combineAlbumEditions | boolean | Group an album's editions together. Default: the account setting. |
q required | string | Words to find. · up to 100 characters |
kind | string | Comma-separated kinds: artist, album, song. Default: all. |
limit | integer | default 5 · 1–20 |
Returns StatsSearch · other statuses 400, 401
GET /v1/stats/resolve
Entities for an ID, an external ID or a title and artist resolve
Shared tracks: a trk_ ID resolves to the recordings of its track family; an external ID also finds recordings that joined its track through another ID.
Ownerscope history:readOAuth or sessionMCP tool
Parameters
| Name | Type | Description |
|---|---|---|
by | "minutes" | "plays" | default "minutes" |
combineSongVersions | boolean | Group a song's versions (live, remixes) together. Default: the account setting. |
combineAlbumEditions | boolean | Group an album's editions together. Default: the account setting. |
id | string | An entity ID, or a shared track ID (trk_…). |
scheme | string | External ID scheme: apple-catalog, apple-library, isrc, spotify or mbid. |
value | string | The external ID (with scheme). |
title | string | Song title (with artist). |
artist | string | Artist name (with title). |
album | string | Album title (optional, with title and artist). |
Returns StatsResolved · other statuses 400, 401
GET /v1/stats/exclusions
Artists, albums and songs left out of stats listExclusions
Each rule with what it names now and its metadata key, so a client can skip those recordings.
Ownerscope history:readOAuth or session
Returns StatsExclusions · other statuses 400, 401
POST /v1/stats/exclusions
Leave an artist, album or song out of stats addExclusion
By entity ID, or by kind and the metadata key the app has (a credit or grouping key). Its plays are kept. Adding a rule that exists changes nothing.
Ownerscope settings:writeSession only
JSON body
| Name | Type | Description |
|---|---|---|
id | string | An artist, album or song ID (art_, alb_ or song_). |
kind | StatsRankingKind | |
key | string | With kind: the metadata key. · up to 1000 characters |
Returns StatsExclusions · other statuses 400, 401, 402, 404, 409, 413
DELETE /v1/stats/exclusions/{id}
Count an artist, album or song in stats again removeExclusion
By the ID a rule was saved with or the ID its entity has now. Removing a rule that does not exist changes nothing.
Ownerscope settings:writeSession only
Parameters
| Name | Type | Description |
|---|---|---|
id required path | string |
Returns StatsExclusions · other statuses 400, 401, 404
History
GET /v1/history/rules
Changes to the history: removals, merges and splits listHistoryRules
Newest first, a page at a time: every kind, or one kind, optionally only those whose label has some words (q). Counts are by kind, of everything. Each can be undone.
Ownerscope history:readOAuth or session
Parameters
| Name | Type | Description |
|---|---|---|
limit | integer | default 100 · 1–200 |
before | string | The next value of the previous page. · up to 64 characters |
kind | HistoryRuleKind | Only rules of this kind. |
q | string | Only rules whose label has these words: a song or artist, a period, a name. · up to 100 characters |
Returns HistoryRules · other statuses 400, 401
POST /v1/history/rules
Remove plays, or merge or keep apart artists, albums and songs addHistoryRule
A play by its history row (seq), or a period by its first instant and the instant after it, as the person chose it in their time zone, with a label such as “March 2024”. A play is in a period when the middle of its time window is. Removed plays stay removed when imported again. An artist, album or song removes its plays past and future; a merge joins one into another; a split keeps a version or edition apart.
Ownerscope history:writeSession only
JSON body
| Name | Type | Description |
|---|---|---|
kind required | "play" | "period" | "entity" | "merge" | "split" | |
seq | integer | A play: its history row. · at most 9007199254740991 |
startAt | date-time | A period: its first instant. · pattern ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d(?:\.\d+)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$ |
endAt | date-time | A period: the instant after it. · pattern ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d(?:\.\d+)?(?:Z|([+-](?:[01]\d|2[0-3]):[0-5]\d)))$ |
label | string | A period: as it was chosen. · up to 100 characters |
id | string | An artist, album or song: its ID (art_, alb_ or song_). |
entityKind | StatsRankingKind | Or, with key: its kind. |
key | string | With entityKind: the metadata key. · up to 1000 characters |
into | string | A merge: the artist, album or song it joins (its ID). |
intoKey | string | Or, with entityKind: the metadata key of the one it joins. · up to 1000 characters |
Returns HistoryRule · other statuses 400, 401, 402, 404, 409
DELETE /v1/history/rules/{id}
Undo a change to the history undoHistoryRule
Undoes a removal (the plays only it removed come back), a merge or a split.
Ownerscope history:writeSession only
Parameters
| Name | Type | Description |
|---|---|---|
id required path | string | up to 64 characters |
Returns object · other statuses 400, 401, 404
Imports and setup
GET /v1/export-guides/{provider}
How to get a listening history export from Apple Music, Spotify or Last.fm getExportGuide
Ownerscope history:readOAuth or sessionMCP tool
Parameters
| Name | Type | Description |
|---|---|---|
provider required path | Provider |
Returns ExportGuide · other statuses 400, 401, 404
GET /v1/handoffs/upload
A link to the upload page for this account getUploadLink
The website’s Imports page with a `for` value signed for this account (24 hours). It grants nothing: the user signs in there, and the page refuses to upload when the browser is signed in to a different account.
Ownerscope history:readOAuth or sessionMCP tool
Parameters
| Name | Type | Description |
|---|---|---|
provider | Provider | The service the export is from, if known. |
Returns UploadLink · other statuses 400, 401, 402
GET /v1/handoffs/apple-music
A link to connect Apple Music detection, or why it isn’t available getAppleMusicLink
The website’s Live tracking page with a `for` value signed for this account (24 hours), when this account can connect: available is false (with a reason) while detection is full or not set up.
Ownerscope history:readOAuth or sessionMCP tool
Returns AppleMusicLink · other statuses 400, 401, 402
GET /v1/imports
The account’s imports, newest first, and its import limits listImports
Ownerscope history:readOAuth or sessionMCP tool
Parameters
| Name | Type | Description |
|---|---|---|
limit | integer | At most this many imports (1–100, default 100). · default 100 · 1–100 |
Returns ImportList · other statuses 400, 401
GET /v1/imports/{id}
One import getImport
Ownerscope history:readOAuth or sessionMCP tool
Parameters
| Name | Type | Description |
|---|---|---|
id required path | string | pattern ^[\w-]{1,100}$ |
Returns ImportDetail · other statuses 400, 401, 404
GET /v1/apple-music/connection
Apple Music detection: availability, connection, and its checks getAppleMusicStatus
No tokens. Connecting and stopping are on the website and in the app.
Ownerscope history:readOAuth or sessionMCP tool
Returns AppleMusicStatus · other statuses 400, 401
GET /v1/handoffs/check
Whether a link from an assistant was made for the signed-in account checkHandoff
The website’s Imports and Live tracking pages ask before acting on a link with `for`: valid, expired, or made for another account (the page then offers nothing).
Ownerscope accountSession only
Parameters
| Name | Type | Description |
|---|---|---|
purpose required | "upload" | "apple-music" | |
for required | string | up to 200 characters |
Returns HandoffCheck · other statuses 400, 401
Account
GET /v1/settings
The account's settings getSettings
Ownerscope history:readOAuth or session
Returns AccountSettings · other statuses 400, 401
PATCH /v1/settings
Change the account's settings updateSettings
Fields absent from the body keep their value; a set value is never unset.
Ownerscope settings:writeSession only
JSON body
| Name | Type | Description |
|---|---|---|
timeZone | string | IANA time zone, such as America/Chicago. |
combineSongVersions | boolean | |
combineAlbumEditions | boolean | |
weekStart | integer | First day of week buckets: 0 Sunday … 6 Saturday. · 0–6 |
Returns AccountSettings · other statuses 400, 401, 413
GET /v1/account/status
Where the account stands: history, imports, Apple Music and next steps getAccountStatus
Ownerscope history:readOAuth or sessionMCP tool
Returns AccountStatus · other statuses 400, 401
POST /v1/settings/defaults
Set the time zone and week start where they are still unset fillSettings
For the website: sent from the browser after each sign-in (so a new account has them before its first import) and from the Imports and Live tracking pages. A value already set (by the app or earlier) is never changed.
Ownerscope accountSession only
JSON body
| Name | Type | Description |
|---|---|---|
timeZone | string | |
weekStart | integer | 0–6 |
Returns AccountSettings · other statuses 400, 401, 413
GET /v1/account/connections
Apps connected to the account listConnections
Ownerscope accountSession only
Returns Connections · other statuses 400, 401
POST /v1/account/connections/revoke
Disconnect an app disconnectApp
Removes the consent and tokens; tokens the app already holds stop working at once. The app must ask again to reconnect.
Ownerscope accountSession only
JSON body
| Name | Type | Description |
|---|---|---|
clientId required | string | up to 500 characters |
Returns object · other statuses 400, 401, 413
GET /v1/billing
The account's subscription getBilling
Ownerscope history:readOAuth or session
Returns BillingState · other statuses 400, 401
POST /v1/billing/offer
Which subscription the app offers getBillingOffer
The product the paywall shows: with the 1-month trial, or without it when this account, its email or this device (DeviceCheck) already had one. Also the appAccountToken to purchase with.
Ownerscope accountSession only
JSON body
| Name | Type | Description |
|---|---|---|
deviceToken | string | DCDevice token, base64. · up to 8192 characters |
development | boolean | A development build (DeviceCheck sandbox). |
Returns object · other statuses 400, 401
POST /v1/billing/apple/transactions
Send a purchase from the app recordAppleTransaction
A signed App Store transaction (and optionally its renewal information) the app saw: a purchase, a restore, or an update. Verified with Apple’s certificates before anything is stored. 409 when the subscription belongs to another account.
Ownerscope accountSession only
JSON body
| Name | Type | Description |
|---|---|---|
signedTransaction required | string | up to 32768 characters |
signedRenewal | string | up to 32768 characters |
deviceToken | string | up to 8192 characters |
development | boolean |
Returns BillingState · other statuses 400, 401, 409
Public profiles
GET /v1/public/{handle}/profile
A public profile: totals and the top 5 artists, albums and songs of a period getProfilePublic
The owner previews their own profile whether or not it is shared. Public viewers read it at /v1/public/{handle}/profile once shared; private and missing profiles are the same 404. The account’s hidden entities leave the lists.
Public
Parameters
| Name | Type | Description |
|---|---|---|
handle required path | string | The profile handle, any capitals. · pattern ^[A-Za-z0-9_]{1,40}$ |
period | string | weeks: the past 4 weeks. year: this calendar year (the default). Last year by its number (2025). all: All time. Last year and All time need the profile to show its history. |
Returns PublicProfile · other statuses 301, 400, 404, 429
GET /v1/public/{handle}/profile/details
A public profile’s slower sections: every day, records, hours, weekdays, new artists getProfileDetailsPublic
Public
Parameters
| Name | Type | Description |
|---|---|---|
handle required path | string | The profile handle, any capitals. · pattern ^[A-Za-z0-9_]{1,40}$ |
period | string | weeks: the past 4 weeks. year: this calendar year (the default). Last year by its number (2025). all: All time. Last year and All time need the profile to show its history. |
Returns ProfileDetails · other statuses 301, 400, 404, 429
GET /v1/public/{handle}/profile/top
A public profile’s top 25 artists, albums or songs of a period, and how its top 5 moved getProfileTopPublic
Public
Parameters
| Name | Type | Description |
|---|---|---|
handle required path | string | The profile handle, any capitals. · pattern ^[A-Za-z0-9_]{1,40}$ |
period | string | weeks: the past 4 weeks. year: this calendar year (the default). Last year by its number (2025). all: All time. Last year and All time need the profile to show its history. |
kind | "artist" | "album" | "song" | default "artist" |
Returns ProfileTop · other statuses 301, 400, 404, 429
GET /v1/public/{handle}/profile/recent
A public profile’s recently played songs (when it shows them) getProfileRecentPublic
Public
Parameters
| Name | Type | Description |
|---|---|---|
handle required path | string | The profile handle, any capitals. · pattern ^[A-Za-z0-9_]{1,40}$ |
Returns ProfileRecent · other statuses 301, 400, 404, 429
Profile settings
GET /v1/profile
The account's public profile settings getProfileSettings
Ownerscope profile:readSession only
Returns ProfileSettings · other statuses 400, 401
PATCH /v1/profile
Change the account's public profile updateProfileSettings
Fields absent from the body keep their value. Sharing needs a handle; a new handle keeps the old one as a redirect.
Ownerscope settings:writeSession only
JSON body
| Name | Type | Description |
|---|---|---|
handle | string | |
shared | boolean | |
displayName | string | |
showRecent | boolean | |
showHistory | boolean |
Returns ProfileSettings · other statuses 400, 401, 402, 409, 413
GET /v1/profile/handle
Whether a handle is available checkProfileHandle
Ownerscope profile:readSession only
Parameters
| Name | Type | Description |
|---|---|---|
handle required | string | The handle to check. · up to 40 characters |
Returns HandleCheck · other statuses 400, 401
GET /v1/profile/view
A public profile: totals and the top 5 artists, albums and songs of a period getProfile
The owner previews their own profile whether or not it is shared. Public viewers read it at /v1/public/{handle}/profile once shared; private and missing profiles are the same 404. The account’s hidden entities leave the lists.
Public
Parameters
| Name | Type | Description |
|---|---|---|
period | string | weeks: the past 4 weeks. year: this calendar year (the default). Last year by its number (2025). all: All time. Last year and All time need the profile to show its history. |
Returns PublicProfile · other statuses 400, 401, 404
GET /v1/profile/details
A public profile’s slower sections: every day, records, hours, weekdays, new artists getProfileDetails
Public
Parameters
| Name | Type | Description |
|---|---|---|
period | string | weeks: the past 4 weeks. year: this calendar year (the default). Last year by its number (2025). all: All time. Last year and All time need the profile to show its history. |
Returns ProfileDetails · other statuses 400, 401, 404
GET /v1/profile/top
A public profile’s top 25 artists, albums or songs of a period, and how its top 5 moved getProfileTop
Public
Parameters
| Name | Type | Description |
|---|---|---|
period | string | weeks: the past 4 weeks. year: this calendar year (the default). Last year by its number (2025). all: All time. Last year and All time need the profile to show its history. |
kind | "artist" | "album" | "song" | default "artist" |
Returns ProfileTop · other statuses 400, 401, 404
GET /v1/profile/recent
A public profile’s recently played songs (when it shows them) getProfileRecent
Public
Returns ProfileRecent · other statuses 400, 401, 404
Data export
POST /v1/exports
Start a download of all the account’s data startExport
Writes one ZIP in the background: README.txt, account.json, imports.json, plays.csv and plays.jsonl. One at a time, and at most limits.perDay per 24 hours. 409 while one is being written; 429 over the daily limit (retryAt says when); 403 with reason mfa for a session that needs to pass two-step sign-in.
Ownerscope accountSession only
Returns DataExportStatus · other statuses 400, 401, 403, 409, 429, 503
GET /v1/exports/status
The account’s data download: the latest one and whether another can start exportStatus
Ownerscope accountSession only
Returns DataExportStatus · other statuses 400, 401
GET /v1/exports/{id}/download
Download a finished data export (a ZIP) downloadExport
While the export is ready. 409 before it is; 410 once its week is up and the file is deleted.
Ownerscope accountSession only
Parameters
| Name | Type | Description |
|---|---|---|
id required path | uuid | pattern ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$ |
Returns a file (application/zip) · other statuses 400, 401, 403, 404, 409, 410
Schemas
AccountSettings
Account settings. null: never set; clients then offer their own value or use the default.
| Name | Type | Description |
|---|---|---|
timeZone required | string | null | IANA time zone; null until set. |
combineSongVersions required | boolean | null | |
combineAlbumEditions required | boolean | null | |
weekStart required | integer | null | First day of week buckets: 0 Sunday … 6 Saturday. null: Sunday. |
hiddenEntities required | string[] | IDs of the artists, albums and songs left out of stats (/v1/stats/exclusions). |
updatedAt required | string | null |
AccountStatus
Where the account stands: history, imports, Apple Music, and what to do next.
| Name | Type | Description |
|---|---|---|
account required | object | |
history required | object | |
imports required | object | |
usage required | ImportUsage | |
appleMusic required | object | |
statsReady required | boolean | False while rows are still being placed in the account’s time zone. |
nextSteps required | NextStep[] | |
nextActions required | NextAction[] | nextSteps in the same order, each with the tool to call for it. |
AppleMusicLink
A link to connect Apple Music detection, or why it isn’t available.
| Name | Type | Description |
|---|---|---|
available required | boolean | Whether this account can connect Apple Music detection. |
connected required | boolean | |
url required | string | null | The Live tracking page; null when not available. |
expiresAt required | string | null | |
reason required | AppleMusicUnavailable | null |
AppleMusicStatus
Apple Music detection: whether it can connect, whether it is connected, and its last and next checks. Detection sees new plays from when it connects, not past history; times are approximate, within the check window.
| Name | Type | Description |
|---|---|---|
available required | boolean | Whether this account can connect (or is connected). |
unavailableReason required | AppleMusicUnavailable | null | |
status required | "active" | "reauthorize" | "disconnected" | |
connected required | boolean | |
needsReconnect required | boolean | Apple stopped accepting the connection: the user connects again. |
stopped required | boolean | The user paused detection themselves. |
held required | boolean | Paused while the account has no subscription; it resumes when it has one. |
connectedAt required | string | null | |
lastPolledAt required | string | null | The last check of Apple’s recently played list. |
nextPollAt required | string | null | |
lastResult required | string | null | |
lastError required | string | null | |
pollingIntervalSeconds required | integer | -9007199254740991–9007199254740991 |
playsDetected required | integer | Plays detection has found so far. · -9007199254740991–9007199254740991 |
lastPlayDetectedAt required | string | null | The check that found the newest detected play. |
BillingState
| Name | Type | Description |
|---|---|---|
enforced required | boolean | Whether a missing subscription refuses anything yet. |
access required | boolean | Whether the account may use everything now. |
state required | "none" | "trial" | "active" | "grace" | "granted" | "lapsed" | |
subscription required | object | null | |
grant required | object | null | Access from OnRepeat (no charge). |
endedAt required | string | null | When access ended, once it has. |
keepUntil required | string | null | Until when the history is kept after access ended. |
Connection
| Name | Type | Description |
|---|---|---|
clientId required | string | |
name required | string | The name the app gave itself (or its registration). |
verified required | boolean | Whether OnRepeat confirmed who the app is: an allowed client metadata document, or registered by OnRepeat. |
scopes required | string[] | |
connectedAt required | string | null | |
lastUsedAt required | string | null | When it last got a refresh token. |
Connections
Apps connected to the account.
| Name | Type | Description |
|---|---|---|
connections required | Connection[] |
DataExport
| Name | Type | Description |
|---|---|---|
id required | string | |
status required | DataExportState | |
createdAt required | string | |
readyAt required | string | null | |
expiresAt required | string | null | When the file is deleted (ready exports). |
bytes required | integer | null | Size of the ZIP once ready. |
plays required | integer | null | Rows in plays.csv, once the export has begun. |
progress required | number | null | Share written so far, while running. |
downloadPath required | string | null | The download operation’s path, while ready. |
error required | string | null |
DataExportState
queued and running: being written; ready: can be downloaded until expiresAt; failed: nothing was kept, start again; expired: the file was deleted.
"queued" | "running" | "ready" | "failed" | "expired"
DataExportStatus
The account’s data export: the latest one and whether another can start.
| Name | Type | Description |
|---|---|---|
latest required | DataExport | null | The most recent export, null when there is none. |
canStart required | boolean | Whether startExport would be accepted now. |
blockedBy required | "running" | "daily" | "ceiling" | "off" | null | Why not: one is being written, the daily limit, the limit for all accounts, or exports are turned off. |
nextStartAt required | string | null | When the daily limit allows another. |
limits required | object |
DurationSource
Where durationMs comes from: reported by the source; split (an even share of an Apple day's time); length (the song's length); gap (the time to the next scrobble); gap+length (that, capped by the song's length), or unknown (0 until a length is known).
"reported" | "split" | "length" | "gap" | "gap+length" | "unknown"
Error
| Name | Type | Description |
|---|---|---|
error required | string |
ExclusionGrouping
"track" | "song" | "album" | "albumGroup"
ExportGuide
How to request a listening history export and what to upload.
| Name | Type | Description |
|---|---|---|
provider required | Provider | |
service required | string | Apple Music or Spotify. |
requestUrl required | string | Where the user requests the export, signed in to the service. |
steps required | string[] | What the user does, in order. |
typicalWait required | string | |
files required | object[] | The files OnRepeat reads from the export. |
upload required | string | What to upload, and how (getUploadLink gives the link). |
guideUrl required | string | The full guide on onrepeatstats.com. |
HandleCheck
| Name | Type | Description |
|---|---|---|
handle required | string | |
available required | boolean | |
reason required | "format" | "reserved" | "taken" | null |
HandoffCheck
Whether a link from an assistant was made for the signed-in account.
| Name | Type | Description |
|---|---|---|
result required | "valid" | "expired" | "other_account" |
HistoryRule
A change the owner made to their history: plays removed, or a merge or split.
| Name | Type | Description |
|---|---|---|
id required | string | |
kind required | HistoryRuleKind | |
entityId required | string | null | An artist, album or song: its ID (art_, alb_ or song_). A merge: the one merged. |
entityKind required | StatsRankingKind | null | A removed, merged or kept-apart artist, album or song: which it is. |
label required | string | A play: its song and artist. A period: as it was chosen, such as “March 2024”. An artist, album or song: its title (and artist). A merge: “from → into”; show from and into instead. A split: the version or edition’s title. |
from required | string | null | A merge: the title of the one merged. |
into required | string | null | A merge: the title of the one it joined (null for a merge whose label has none). |
createdAt required | string | |
applying required | boolean | Still being applied: a large removal takes a few seconds to finish. |
earliestAt required | string | null | A play: the start of its time window. |
latestAt required | string | null | A play: the end of its time window. |
startAt required | string | null | A period: its first instant. |
endAt required | string | null | A period: the instant after it. |
HistoryRuleKind
One play, every play in a period, or every play of an artist, album or song (removed); or a merge of two artists, albums or songs, or a version or edition kept apart from its group.
"play" | "period" | "entity" | "merge" | "split"
HistoryRules
Changes to the history, newest first.
| Name | Type | Description |
|---|---|---|
items required | HistoryRule[] | Newest first. |
next required | string | null | Pass as before for the next page. |
total required | integer | Rules of every kind. · -9007199254740991–9007199254740991 |
counts required | object | Rules of each kind. |
limit required | integer | Most rules an account can keep. · -9007199254740991–9007199254740991 |
Import
One import of an export file.
| Name | Type | Description |
|---|---|---|
id required | string | |
filename required | string | |
status required | ImportState | |
digest required | string | null | SHA-256 of the uploaded file. |
objectKey required | string | null | |
checkpoint required | integer | Rows read so far. · -9007199254740991–9007199254740991 |
accepted required | integer | Rows read and kept as listening. · -9007199254740991–9007199254740991 |
rejected required | integer | Rows that could not be read. · -9007199254740991–9007199254740991 |
added required | integer | New listening added to the history. · -9007199254740991–9007199254740991 |
alreadyImported required | integer | Rows already in the history. · -9007199254740991–9007199254740991 |
replaced required | integer | Existing rows replaced by a more complete Apple day. · -9007199254740991–9007199254740991 |
skipped required | integer | Podcast, audiobook and radio rows, not kept. · -9007199254740991–9007199254740991 |
error required | string | null | |
retryable required | boolean | |
duplicateOf required | string | null | |
createdAt required | string | |
groupId required | string | The upload it was part of. Deleting an import deletes its whole upload. |
bytes required | integer | Bytes this import keeps in storage. · -9007199254740991–9007199254740991 |
ImportDetail
One import, and whether stats include everything imported so far.
| Name | Type | Description |
|---|---|---|
id required | string | |
filename required | string | |
status required | ImportState | |
digest required | string | null | SHA-256 of the uploaded file. |
objectKey required | string | null | |
checkpoint required | integer | Rows read so far. · -9007199254740991–9007199254740991 |
accepted required | integer | Rows read and kept as listening. · -9007199254740991–9007199254740991 |
rejected required | integer | Rows that could not be read. · -9007199254740991–9007199254740991 |
added required | integer | New listening added to the history. · -9007199254740991–9007199254740991 |
alreadyImported required | integer | Rows already in the history. · -9007199254740991–9007199254740991 |
replaced required | integer | Existing rows replaced by a more complete Apple day. · -9007199254740991–9007199254740991 |
skipped required | integer | Podcast, audiobook and radio rows, not kept. · -9007199254740991–9007199254740991 |
error required | string | null | |
retryable required | boolean | |
duplicateOf required | string | null | |
createdAt required | string | |
groupId required | string | The upload it was part of. Deleting an import deletes its whole upload. |
bytes required | integer | Bytes this import keeps in storage. · -9007199254740991–9007199254740991 |
statsReady required | boolean | False while rows are still being placed in the account’s time zone. |
ImportList
The account’s imports, newest first, and its import limits.
| Name | Type | Description |
|---|---|---|
imports required | Import[] | Newest first, at most 100. |
usage required | ImportUsage | |
statsReady required | boolean | False while rows are still being placed in the account’s time zone. |
ImportState
awaiting_upload: created, its file not uploaded yet; queued and processing: being read; complete; failed (error says why); duplicate: the same file as an earlier import (duplicateOf); deleting: being deleted with the rest of its upload.
"awaiting_upload" | "queued" | "processing" | "complete" | "failed" | "duplicate" | "deleting"
ImportUsage
The account’s import limits: uploads in the last 24 hours, storage, imports waiting for their file, and rows in the history.
| Name | Type | Description |
|---|---|---|
uploads required | object | |
storage required | object | |
waiting required | object | |
rows required | object | Rows in the history (imported and detected listening) and the most it can hold. An import that would pass the limit fails whole. |
MediaKind
"music" | "podcast" | "audiobook" | "radio" | "unknown"
NextAction
A next step and the tool that carries it out.
| Name | Type | Description |
|---|---|---|
step required | NextStep | |
tool required | string | The MCP tool (and API operation) for this step. |
NextStep
"request_export" | "upload_export" | "wait_for_import" | "connect_apple_music" | "reconnect_apple_music" | "ask_questions"
PlayReported
What the play's source itself reported as its time: end (Spotify), start (a scrobble), window (two checks a detected play fell between), hour or day (Apple's daily file).
"end" | "start" | "window" | "hour" | "day"
ProfileDetails
| Name | Type | Description |
|---|---|---|
asOf required | StatsAsOf | |
handle required | string | |
displayName required | string | |
settingsVersion required | integer | Changes with every profile settings change. · -9007199254740991–9007199254740991 |
showRecent required | boolean | |
showHistory required | boolean | |
period required | ProfilePeriodInfo | |
periods required | ProfilePeriodInfo[] | The periods this profile offers, in order. |
today required | string | Today in the account’s time zone. |
timeZone required | string | |
since required | string | null | First local day with listening. |
isNew required | boolean | |
artworkPending required | boolean | Some artwork is still being looked up: read again shortly for it. |
calendar required | object[] | Listening per day with any, oldest first (All time: the last five years). |
records required | object | |
hours required | ProfileHours | |
weekdays required | number[] | Listening per weekday, Monday first. |
newArtists required | object[] |
ProfileHours
| Name | Type | Description |
|---|---|---|
durationMs required | number[] | Listening per local hour, midnight first. |
timedMs required | number | Listening placed in an hour. |
untimedMs required | number | Listening known only to the day (in no hour). |
sampled required | boolean | Estimated from an even sample of the rows (long ranges); otherwise exact. |
ProfileItem
| Name | Type | Description |
|---|---|---|
plays required | integer | -9007199254740991–9007199254740991 |
durationMs required | integer | -9007199254740991–9007199254740991 |
rows required | integer | Source rows counted (rows with a play or listening time). · -9007199254740991–9007199254740991 |
position required | integer | -9007199254740991–9007199254740991 |
entity required | StatsEntityRef | |
artwork | StatsArtwork | null | The first items of a page only; null when none is known yet. |
line required | number[] | Milliseconds per line bucket (see lineBuckets). |
ProfileListKind
"artist" | "album" | "song"
ProfilePeriod
string
ProfilePeriodInfo
| Name | Type | Description |
|---|---|---|
id required | ProfilePeriod | |
kind required | "weeks" | "year" | "all" | |
label required | string | Past 4 weeks, the year, or All time. |
from required | string | null | First local day; null for All time. |
to required | string | null | Last local day (today for current periods). |
current required | boolean | The period includes today. |
ProfileRecent
| Name | Type | Description |
|---|---|---|
asOf required | StatsAsOf | |
handle required | string | |
today required | string | |
timeZone required | string | |
plays required | ProfileRecentPlay[] | |
todayTotals required | object | |
artworkPending required | boolean |
ProfileRecentPlay
| Name | Type | Description |
|---|---|---|
entity required | StatsEntityRef | null | |
title required | string | |
artist required | string | null | |
album required | string | null | |
artwork required | StatsArtwork | null | |
day required | string | Local day. |
at required | string | null | When it ended (or the middle of a detected window); null when only the day is known. |
approximate required | boolean | The time is an estimate (a detected play’s window). |
ProfileSettings
| Name | Type | Description |
|---|---|---|
handle required | string | null | null until the owner picks one. |
shared required | boolean | Anyone with the link can see the profile. |
displayName required | string | The name the profile shows. |
showRecent required | boolean | The profile shows recently played songs. |
showHistory required | boolean | The profile offers last year and All time. |
url required | string | null | The profile’s address while shared. |
version required | integer | Changes with every settings change. · -9007199254740991–9007199254740991 |
previousHandles required | string[] | Old handles that redirect here. |
ProfileTop
| Name | Type | Description |
|---|---|---|
asOf required | StatsAsOf | |
handle required | string | |
displayName required | string | |
settingsVersion required | integer | Changes with every profile settings change. · -9007199254740991–9007199254740991 |
showRecent required | boolean | |
showHistory required | boolean | |
period required | ProfilePeriodInfo | |
periods required | ProfilePeriodInfo[] | The periods this profile offers, in order. |
today required | string | Today in the account’s time zone. |
timeZone required | string | |
since required | string | null | First local day with listening. |
isNew required | boolean | |
artworkPending required | boolean | Some artwork is still being looked up: read again shortly for it. |
kind required | ProfileListKind | |
totals required | ProfileTotals | |
lineBuckets required | string[] | |
items required | ProfileItem[] | |
ranks required | object |
ProfileTotals
| Name | Type | Description |
|---|---|---|
durationMs required | number | |
plays required | number | |
days required | number | Days with listening. |
artists required | number | |
albums required | number | |
songs required | number | |
newArtists required | number | Artists first heard in the period. |
Provider
"apple" | "spotify" | "lastfm"
PublicProfile
| Name | Type | Description |
|---|---|---|
asOf required | StatsAsOf | |
handle required | string | |
displayName required | string | |
settingsVersion required | integer | Changes with every profile settings change. · -9007199254740991–9007199254740991 |
showRecent required | boolean | |
showHistory required | boolean | |
period required | ProfilePeriodInfo | |
periods required | ProfilePeriodInfo[] | The periods this profile offers, in order. |
today required | string | Today in the account’s time zone. |
timeZone required | string | |
since required | string | null | First local day with listening. |
isNew required | boolean | |
artworkPending required | boolean | Some artwork is still being looked up: read again shortly for it. |
totals required | ProfileTotals | |
lineBuckets required | string[] | Each line value’s first day. |
artists required | ProfileItem[] | |
albums required | ProfileItem[] | |
songs required | ProfileItem[] | |
trend required | object |
StatsArtwork
Artwork from the Apple Music catalog. Absent or null: none known yet.
| Name | Type | Description |
|---|---|---|
url required | string | Image URL template with {w} and {h} (pixels). |
color required | string | null | Background color, six hex digits. |
StatsAsOf
What a result was computed from.
| Name | Type | Description |
|---|---|---|
revision required | integer | -9007199254740991–9007199254740991 |
metadataRevision required | integer | -9007199254740991–9007199254740991 |
timeZone required | string | |
complete required | boolean | False while rows are still being placed in the account time zone. |
StatsBucket
"day" | "week" | "month" | "year"
StatsComparison
Two ranges side by side: totals, and how the top artists, albums or songs moved.
| Name | Type | Description |
|---|---|---|
asOf required | StatsAsOf | |
kind required | StatsRankingKind | |
options required | StatsOptions | |
provider | Provider | |
a required | object | |
b required | object | |
items required | StatsComparisonItem[] | Each top `limit` of a and b together, in b’s order, then a’s. |
StatsComparisonItem
| Name | Type | Description |
|---|---|---|
entity required | StatsEntityRef | |
a required | object | |
b required | object | |
change required | "new" | "gone" | "up" | "down" | "same" | From a to b: new (in b’s top only, not heard in a), gone (in a’s top, not heard in b), up, down or same by position; an entity in one top and outside the other counts as up or down. |
StatsDiscoveries
Artists, albums or songs first heard in a range.
| Name | Type | Description |
|---|---|---|
asOf required | StatsAsOf | |
kind required | StatsRankingKind | |
range required | StatsRange | |
options required | StatsOptions | |
provider | Provider | |
items required | StatsDiscovery[] | First heard within the range, ranked by listening within it. |
nextCursor required | string | null |
StatsDiscovery
| Name | Type | Description |
|---|---|---|
plays required | integer | -9007199254740991–9007199254740991 |
durationMs required | integer | -9007199254740991–9007199254740991 |
rows required | integer | Source rows counted (rows with a play or listening time). · -9007199254740991–9007199254740991 |
position required | integer | -9007199254740991–9007199254740991 |
entity required | StatsEntityRef | |
artwork | StatsArtwork | null | The first items of a page only; null when none is known yet. |
firstDay required | string | The first local day it was ever heard. · pattern ^\d{4}-\d{2}-\d{2}$ |
StatsEntity
| Name | Type | Description |
|---|---|---|
asOf required | StatsAsOf | |
entity required | StatsEntityRef | |
redirectedFrom required | string | null | Set when the requested ID now names this entity. |
range required | StatsRange | |
options required | StatsOptions | |
totals required | StatsTotals | |
firstPlayed required | StatsPlayTime | null | |
lastPlayed required | StatsPlayTime | null | |
providers required | object[] | |
trend required | object | |
top required | map of StatsRankingItem[] | The top entities of each other kind within this one. |
StatsEntityKind
"artist" | "album" | "song" | "recording"
StatsEntityRef
A stable reference to an artist, album, song or recording. IDs survive grouping changes.
| Name | Type | Description |
|---|---|---|
id required | string | |
kind required | StatsEntityKind | |
title required | string | |
subtitle required | string | null | Songs: the source artist. Albums: the album artist. |
albumTitle | string | null | Songs: the most listened release's album. |
trackId | string | null | Recordings: the shared track (trk_…) once resolved. |
StatsExclusion
One artist, album or song left out of stats.
| Name | Type | Description |
|---|---|---|
id required | string | The entity ID the rule was saved with. |
kind required | StatsRankingKind | |
entity required | StatsEntityRef | null | What the ID names now; null while it names no plays (they can return with a later import). |
key required | string | null | The entity's key in the history's metadata: a credit key for artists, a grouping key for songs and albums. Null with entity. |
grouping required | ExclusionGrouping | null | Songs and albums: which grouping key key is, under the account’s grouping options. |
StatsExclusions
The artists, albums and songs left out of stats.
| Name | Type | Description |
|---|---|---|
items required | StatsExclusion[] | In the order they were added. |
limit required | integer | Most rules an account can keep. · -9007199254740991–9007199254740991 |
StatsHistory
| Name | Type | Description |
|---|---|---|
asOf required | StatsAsOf | |
range required | StatsRange | |
provider | Provider | Set when the request counted one provider only. |
within required | StatsEntityRef | null | |
items required | StatsHistoryItem[] | |
nextCursor required | string | null | More rows follow while this is set. |
StatsHistoryItem
A play with its stored columns, placement, time window and chart entities.
| Name | Type | Description |
|---|---|---|
id required | string | |
provider required | Provider | |
kind required | MediaKind | |
day required | string | pattern ^\d{4}-\d{2}-\d{2}$ |
listenedAt required | string | When the play started (UTC), as well as it is known (see precision). |
durationMs required | integer | -9007199254740991–9007199254740991 |
playCount required | integer | -9007199254740991–9007199254740991 |
reported required | PlayReported | |
durationSource required | DurationSource | |
skipped required | boolean | null | Whether the source said it was skipped; null when it does not say. |
title required | string | |
artist required | string | null | |
album required | string | null | |
localDay required | string | pattern ^\d{4}-\d{2}-\d{2}$ |
earliestAt required | string | |
latestAt required | string | |
precision required | TimePrecision | |
entities required | object |
StatsOptions
| Name | Type | Description |
|---|---|---|
by required | "minutes" | "plays" | |
combineSongVersions required | boolean | |
combineAlbumEditions required | boolean |
StatsPeriodTotals
| Name | Type | Description |
|---|---|---|
plays required | integer | -9007199254740991–9007199254740991 |
durationMs required | integer | -9007199254740991–9007199254740991 |
rows required | integer | Source rows counted (rows with a play or listening time). · -9007199254740991–9007199254740991 |
days required | integer | Local days with any listening. · -9007199254740991–9007199254740991 |
StatsPlayTime
When a play happened: between earliestAt and latestAt, with the source precision.
| Name | Type | Description |
|---|---|---|
localDay required | string | pattern ^\d{4}-\d{2}-\d{2}$ |
earliestAt required | string | |
latestAt required | string | |
precision required | TimePrecision |
StatsPoint
| Name | Type | Description |
|---|---|---|
start required | string | First local day of the bucket. · pattern ^\d{4}-\d{2}-\d{2}$ |
plays required | integer | -9007199254740991–9007199254740991 |
durationMs required | integer | -9007199254740991–9007199254740991 |
StatsRange
Inclusive local days; a null end is open. Both null: all time.
| Name | Type | Description |
|---|---|---|
from required | string | null | |
to required | string | null |
StatsRankingItem
| Name | Type | Description |
|---|---|---|
plays required | integer | -9007199254740991–9007199254740991 |
durationMs required | integer | -9007199254740991–9007199254740991 |
rows required | integer | Source rows counted (rows with a play or listening time). · -9007199254740991–9007199254740991 |
position required | integer | -9007199254740991–9007199254740991 |
entity required | StatsEntityRef | |
artwork | StatsArtwork | null | The first items of a page only; null when none is known yet. |
StatsRankingKind
"artist" | "album" | "song"
StatsRankings
| Name | Type | Description |
|---|---|---|
asOf required | StatsAsOf | |
kind required | StatsRankingKind | |
range required | StatsRange | |
options required | StatsOptions | |
provider | Provider | Set when the request counted one provider only. |
within required | StatsEntityRef | null | |
items required | StatsRankingItem[] | |
nextCursor required | string | null |
StatsResolved
| Name | Type | Description |
|---|---|---|
asOf required | StatsAsOf | |
redirectedFrom required | string | null | |
recordings required | StatsEntityRef[] | |
songs required | StatsEntityRef[] | |
albums required | StatsEntityRef[] | |
artists required | StatsEntityRef[] |
StatsSearch
| Name | Type | Description |
|---|---|---|
asOf required | StatsAsOf | |
query required | string | |
results required | map of StatsRankingItem[] |
StatsSummary
A range at a glance: totals, the top artists, albums and songs, the trend, the peak and listening by hour.
| Name | Type | Description |
|---|---|---|
asOf required | StatsAsOf | |
range required | StatsRange | |
options required | StatsOptions | |
provider | Provider | Set when the request counted one provider only. |
totals required | StatsPeriodTotals | |
firstDay required | string | null | The first local day with listening in the range. |
lastDay required | string | null | The last local day with listening in the range. |
artists required | StatsRankingItem[] | |
albums required | StatsRankingItem[] | |
songs required | StatsRankingItem[] | |
trend required | object | |
peak required | StatsPoint | null | The bucket with the most listening (by the ranking measure). |
hours required | ProfileHours |
StatsTotals
| Name | Type | Description |
|---|---|---|
plays required | integer | -9007199254740991–9007199254740991 |
durationMs required | integer | -9007199254740991–9007199254740991 |
rows required | integer | Source rows counted (rows with a play or listening time). · -9007199254740991–9007199254740991 |
StatsTrend
| Name | Type | Description |
|---|---|---|
asOf required | StatsAsOf | |
range required | StatsRange | |
bucket required | StatsBucket | |
weekStart required | integer | First day of week buckets: 0 Sunday … 6 Saturday. · 0–6 |
provider | Provider | Set when the request counted one provider only. |
within required | StatsEntityRef | null | |
points required | StatsPoint[] | |
nextCursor required | string | null |
TimePrecision
How exactly the source dates a play: an exact time, a window between two times, an hour, or a day.
"exact" | "window" | "hour" | "day"
UploadLink
A link to the upload page for this account.
| Name | Type | Description |
|---|---|---|
url required | string | The upload page; the user signs in there if needed. |
expiresAt required | string | When the link stops working (24 hours). |
provider required | Provider | null |