Recoup

Get started with Recoup.

API REFERENCE

Catalog Playcount History

On this page
GET/api/catalogs/{catalogId}/playcount-history

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.

Authentication

x-api-key in header

bearerAuth bearer

Request

cURL
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 for this request
curl --request GET \
  --url 'https://recoup-api.vercel.app/api/catalogs/{catalogId}/playcount-history'

Parameters

Path parameters

catalogIdstringrequired

Catalog owned by the authenticated account or an organization it currently belongs to.

Query parameters

sincestringrequired

Current UTC observation-period start. Previous period immediately precedes it. Boundaries use the latest observation on that date, not midnight stream totals.

daysintegerrequired

Equal length of both periods. Every UTC observation day through the final boundary must be finished and captured.

pageinteger

Default: 1

limitinteger

Default: 25

Responses

200Saved observations and page-scoped comparison coverage
Cache-Controlresponse header

private, no-store

application/json

statusstring · enum

Values: "success"

catalog_idstring

format: uuid

platformstring · enum

Values: "spotify"

metricstring · enum

Values: "platform_displayed_play_count"

data_sourcestring · enum

Values: "apify_spotify_playcount"

semanticsstring
source_timestamp_availableboolean · enum

Values: false

provider_identity_availableboolean · enum

Values: false

comparison_qualitystring · enum

Values: "legacy_recording_series_identity_unverified"

missing_observation_reason_availableboolean · enum

Values: false

collection_enabledboolean · enum

Values: false

periodsobject
Properties for periods
previousobject
Properties for previous
startstring

format: date

endstring

format: date

currentobject
Properties for current
startstring

format: date

endstring

format: date

daysinteger
timezonestring · enum

Values: "UTC"

boundary_semanticsstring
alignment_tolerance_secondsinteger · enum

Values: 3600

paginationobject
Properties for pagination
pageinteger
limitinteger
total_countinteger
total_pagesinteger
has_moreboolean
summary_scopestring · enum

Values: "page"

comparable_recordingsinteger
recordingsarray<object>
Item properties for recordings
isrcstring
namestring | null
statestring · enum

Values: "comparable", "incomplete", "counter_correction", "invalid_observation", "unaligned_observations"

observationsarray<object>
Item properties for observations
datestring

format: date

captured_atstring

format: date-time

valuenumber
missing_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 | null
current_changenumber | null
absolute_growthnumber | null
percentage_growthnumber | null

Percentage change in period counter movement; null for a zero baseline or non-comparable series.

zero_baselineboolean
400Invalid 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.json
View operation source
json
{
  "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"
    }
  }
}