API REFERENCE
Catalog Playcount History
On this page
/api/catalogs/{catalogId}/playcount-historyRead-only, store-backed public Spotify observations for current catalog membership, including unmeasured recordings. No collection, provider calls, charges or cron activation. API keys and Privy bearer tokens supported; identity overrides, unknown and duplicate query parameters rejected. Compare two adjacent equal observation periods with complete daily coverage and at most one hour capture drift. Missing days, invalid counts, negative corrections and excessive drift suppress growth. Real zero values remain zero; zero prior movement produces null percentage growth. Summaries apply only to this page; increment page while pagination.has_more. Catalog edits can change paging. Legacy rows lack historical provider counter identity and upstream update time: comparisons are provisional, not verified same-counter history, exact daily streams, royalties or causal marketing uplift. This endpoint does not import private artist analytics. Standard MCP tool: get_catalog_playcount_history; unavailable for delegated OAuth pending organization-grant audit. Proposed endpoint requires the linked API feature release; documentation is not proof of production availability.
Authentication
x-api-key in header
bearerAuth bearer
Request
curl --request GET \
--url 'https://api.recoupable.dev/api/catalogs/YOUR_CATALOG_ID/playcount-history?since=YOUR_SINCE&days=YOUR_DAYS' \
--header 'x-api-key: YOUR_API_KEY'Replace the YOUR_ placeholders with your values. Required query parameters are included; optional parameters are listed below.
Try it
Fill in the fields, send the request from your browser, and read the live response. The curl below updates as you type.
Kept in this browser tab only and cleared when it closes.
curl --request GET \
--url 'https://recoup-api.vercel.app/api/catalogs/{catalogId}/playcount-history'Parameters
Path parameters
catalogIdstringrequiredCatalog owned by the authenticated account or an organization it currently belongs to.
Query parameters
sincestringrequiredCurrent UTC observation-period start. Previous period immediately precedes it. Boundaries use the latest observation on that date, not midnight stream totals.
daysintegerrequiredEqual length of both periods. Every UTC observation day through the final boundary must be finished and captured.
pageintegerDefault: 1
limitintegerDefault: 25
Responses
200Saved observations and page-scoped comparison coverage+
Cache-Controlresponse headerprivate, no-store
application/json
statusstring · enumValues: "success"
catalog_idstringformat: uuid
platformstring · enumValues: "spotify"
metricstring · enumValues: "platform_displayed_play_count"
data_sourcestring · enumValues: "apify_spotify_playcount"
semanticsstringsource_timestamp_availableboolean · enumValues: false
provider_identity_availableboolean · enumValues: false
comparison_qualitystring · enumValues: "legacy_recording_series_identity_unverified"
missing_observation_reason_availableboolean · enumValues: false
collection_enabledboolean · enumValues: false
periodsobjectProperties for periods
previousobjectProperties for previous
startstringformat: date
endstringformat: date
currentobjectProperties for current
startstringformat: date
endstringformat: date
daysintegertimezonestring · enumValues: "UTC"
boundary_semanticsstringalignment_tolerance_secondsinteger · enumValues: 3600
paginationobjectProperties for pagination
pageintegerlimitintegertotal_countintegertotal_pagesintegerhas_morebooleansummary_scopestring · enumValues: "page"
comparable_recordingsintegerrecordingsarray<object>Item properties for recordings
isrcstringnamestring | nullstatestring · enumValues: "comparable", "incomplete", "counter_correction", "invalid_observation", "unaligned_observations"
observationsarray<object>Item properties for observations
datestringformat: date
captured_atstringformat: date-time
valuenumbermissing_daysarray<string>Item properties for missing_days
string
invalid_daysarray<string>Item properties for invalid_days
string
correction_daysarray<string>Item properties for correction_days
string
previous_changenumber | nullcurrent_changenumber | nullabsolute_growthnumber | nullpercentage_growthnumber | nullPercentage change in period counter movement; null for a zero baseline or non-comparable series.
zero_baselineboolean400Invalid or unfinished observation-period query+
No response body schema is specified.
401Missing or invalid credentials+
No response body schema is specified.
404Catalog missing or inaccessible+
No response body schema is specified.
503Store unavailable or history exceeds bounded read; use a shorter period+
No response body schema is specified.
Full specification
Download the OpenAPI file for complete schemas, constraints, and examples.
Download releases.jsonView operation source
{
"summary": "Read catalog playcount history and period comparisons",
"description": "Read-only, store-backed public Spotify observations for current catalog membership, including unmeasured recordings. No collection, provider calls, charges or cron activation. API keys and Privy bearer tokens supported; identity overrides, unknown and duplicate query parameters rejected. Compare two adjacent equal observation periods with complete daily coverage and at most one hour capture drift. Missing days, invalid counts, negative corrections and excessive drift suppress growth. Real zero values remain zero; zero prior movement produces null percentage growth. Summaries apply only to this page; increment page while pagination.has_more. Catalog edits can change paging. Legacy rows lack historical provider counter identity and upstream update time: comparisons are provisional, not verified same-counter history, exact daily streams, royalties or causal marketing uplift. This endpoint does not import private artist analytics. Standard MCP tool: get_catalog_playcount_history; unavailable for delegated OAuth pending organization-grant audit. Proposed endpoint requires the linked API feature release; documentation is not proof of production availability.",
"security": [
{
"apiKeyAuth": []
},
{
"bearerAuth": []
}
],
"parameters": [
{
"name": "catalogId",
"in": "path",
"required": true,
"schema": {
"type": "string",
"format": "uuid"
},
"description": "Catalog owned by the authenticated account or an organization it currently belongs to."
},
{
"name": "since",
"in": "query",
"required": true,
"schema": {
"type": "string",
"format": "date"
},
"description": "Current UTC observation-period start. Previous period immediately precedes it. Boundaries use the latest observation on that date, not midnight stream totals."
},
{
"name": "days",
"in": "query",
"required": true,
"schema": {
"type": "integer",
"minimum": 1,
"maximum": 31
},
"description": "Equal length of both periods. Every UTC observation day through the final boundary must be finished and captured."
},
{
"name": "page",
"in": "query",
"schema": {
"type": "integer",
"minimum": 1,
"maximum": 1000000,
"default": 1
}
},
{
"name": "limit",
"in": "query",
"schema": {
"type": "integer",
"minimum": 1,
"maximum": 25,
"default": 25
}
}
],
"responses": {
"200": {
"description": "Saved observations and page-scoped comparison coverage",
"headers": {
"Cache-Control": {
"schema": {
"type": "string"
},
"description": "private, no-store"
}
},
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"status": {
"type": "string",
"enum": [
"success"
]
},
"catalog_id": {
"type": "string",
"format": "uuid"
},
"platform": {
"type": "string",
"enum": [
"spotify"
]
},
"metric": {
"type": "string",
"enum": [
"platform_displayed_play_count"
]
},
"data_source": {
"type": "string",
"enum": [
"apify_spotify_playcount"
]
},
"semantics": {
"type": "string"
},
"source_timestamp_available": {
"type": "boolean",
"enum": [
false
]
},
"provider_identity_available": {
"type": "boolean",
"enum": [
false
]
},
"comparison_quality": {
"type": "string",
"enum": [
"legacy_recording_series_identity_unverified"
]
},
"missing_observation_reason_available": {
"type": "boolean",
"enum": [
false
]
},
"collection_enabled": {
"type": "boolean",
"enum": [
false
]
},
"periods": {
"type": "object",
"properties": {
"previous": {
"type": "object",
"properties": {
"start": {
"type": "string",
"format": "date"
},
"end": {
"type": "string",
"format": "date"
}
}
},
"current": {
"type": "object",
"properties": {
"start": {
"type": "string",
"format": "date"
},
"end": {
"type": "string",
"format": "date"
}
}
},
"days": {
"type": "integer"
},
"timezone": {
"type": "string",
"enum": [
"UTC"
]
},
"boundary_semantics": {
"type": "string"
},
"alignment_tolerance_seconds": {
"type": "integer",
"enum": [
3600
]
}
}
},
"pagination": {
"type": "object",
"properties": {
"page": {
"type": "integer"
},
"limit": {
"type": "integer"
},
"total_count": {
"type": "integer"
},
"total_pages": {
"type": "integer"
},
"has_more": {
"type": "boolean"
}
}
},
"summary_scope": {
"type": "string",
"enum": [
"page"
]
},
"comparable_recordings": {
"type": "integer"
},
"recordings": {
"type": "array",
"items": {
"type": "object",
"properties": {
"isrc": {
"type": "string"
},
"name": {
"type": [
"string",
"null"
]
},
"state": {
"type": "string",
"enum": [
"comparable",
"incomplete",
"counter_correction",
"invalid_observation",
"unaligned_observations"
]
},
"observations": {
"type": "array",
"items": {
"type": "object",
"properties": {
"date": {
"type": "string",
"format": "date"
},
"captured_at": {
"type": "string",
"format": "date-time"
},
"value": {
"type": "number"
}
}
}
},
"missing_days": {
"type": "array",
"items": {
"type": "string",
"format": "date"
}
},
"invalid_days": {
"type": "array",
"items": {
"type": "string",
"format": "date"
}
},
"correction_days": {
"type": "array",
"items": {
"type": "string",
"format": "date"
}
},
"previous_change": {
"type": [
"number",
"null"
]
},
"current_change": {
"type": [
"number",
"null"
]
},
"absolute_growth": {
"type": [
"number",
"null"
]
},
"percentage_growth": {
"type": [
"number",
"null"
],
"description": "Percentage change in period counter movement; null for a zero baseline or non-comparable series."
},
"zero_baseline": {
"type": "boolean"
}
}
}
}
}
}
}
}
},
"400": {
"description": "Invalid or unfinished observation-period query"
},
"401": {
"description": "Missing or invalid credentials"
},
"404": {
"description": "Catalog missing or inaccessible"
},
"503": {
"description": "Store unavailable or history exceeds bounded read; use a shorter period"
}
}
}