diff --git a/api-reference/openapi/research.json b/api-reference/openapi/research.json index e321c17f..807f09b8 100644 --- a/api-reference/openapi/research.json +++ b/api-reference/openapi/research.json @@ -2689,102 +2689,102 @@ } } }, - "/api/research/track/historic-stats": { + "/api/research/tracks/{id}/measurements": { "get": { - "description": "Get historic per-track, per-source stats by ISRC or track id over a date range — a daily time-series of cumulative counters (e.g. `streams_total`). Diff the window endpoints for streams gained over a period.", + "summary": "Track measurements", + "description": "Time-series of a track's measured counts.", "parameters": [ { - "name": "isrc", - "in": "query", - "required": false, - "description": "Track ISRC. Provide exactly one track identifier.", + "name": "id", + "in": "path", + "required": true, "schema": { - "type": "string", - "example": "USQY51771120" - } + "type": "string" + }, + "description": "Provider-neutral track id — ISRC or Spotify track id." }, { - "name": "songstats_track_id", + "name": "platform", "in": "query", - "required": false, - "description": "Songstats track id (from `GET /api/research/tracks`).", "schema": { - "type": "string" - } + "type": "string", + "default": "spotify" + }, + "description": "Platform to read. Currently `spotify`." }, { - "name": "spotify_track_id", + "name": "metric", "in": "query", - "required": false, - "description": "Spotify track id.", "schema": { - "type": "string" - } + "type": "string", + "default": "platform_displayed_play_count" + }, + "description": "Metric to read." }, { - "name": "apple_music_track_id", + "name": "from", "in": "query", - "required": false, - "description": "Apple Music track id.", "schema": { - "type": "string" - } + "type": "string", + "format": "date" + }, + "description": "Inclusive start date (ISO) for the series." }, { - "name": "source", + "name": "to", "in": "query", - "required": true, - "description": "Comma-separated list of sources, or `all`. e.g. `spotify`, `apple_music`, `deezer`.", "schema": { "type": "string", - "example": "spotify" - } + "format": "date" + }, + "description": "Inclusive end date (ISO) for the series." }, { - "name": "start_date", + "name": "granularity", "in": "query", - "required": false, - "description": "Start of the historic window (ISO date).", "schema": { "type": "string", - "format": "date", - "example": "2024-06-09" - } + "enum": [ + "daily" + ], + "default": "daily" + }, + "description": "Series granularity. `daily` returns the per-day cumulative series." }, { - "name": "end_date", + "name": "aggregate", "in": "query", - "required": false, - "description": "End of the historic window (ISO date).", "schema": { "type": "string", - "format": "date", - "example": "2025-06-09" - } + "enum": [ + "run_rate" + ] + }, + "description": "Return a derived aggregate instead of the raw series. `run_rate` returns the trailing-window annualized run-rate (a projection of the series)." }, { - "name": "with_aggregates", + "name": "window", "in": "query", - "required": false, - "description": "Include Songstats-aggregated points (`playlists_current`, `charts_current`, …).", "schema": { - "type": "boolean" - } + "type": "string", + "default": "365d" + }, + "description": "Trailing window for `aggregate=run_rate` (e.g. `365d`)." } ], "responses": { "200": { - "description": "Historic per-track stats, one time-series per requested source.", + "description": "The measurement series (or the requested aggregate).", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ResearchTrackHistoricStatsResponse" + "$ref": "#/components/schemas/ResearchMeasurementsResponse" } } } }, "400": { - "description": "Validation error — missing identifier or `source`.", + "description": "Validation error — bad identifier or query param.", "content": { "application/json": { "schema": { @@ -2803,18 +2803,8 @@ } } }, - "404": { - "description": "No track matched the supplied identifier.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ResearchErrorResponse" - } - } - } - }, "402": { - "description": "Insufficient research credits — the body includes a `checkoutUrl` to top up.", + "description": "Insufficient credits.", "content": { "application/json": { "schema": { @@ -2823,8 +2813,8 @@ } } }, - "429": { - "description": "Upstream provider error passthrough — e.g. 429 when the Songstats quota is exhausted.", + "404": { + "description": "No measurements for this track yet — create a `historical` measurement-job to backfill it.", "content": { "application/json": { "schema": { @@ -2836,38 +2826,55 @@ } } }, - "/api/research/playcounts": { + "/api/research/albums/{id}/measurements": { "get": { - "description": "Latest platform-displayed play counts for all tracks on an album, served from the measurement store (populated by snapshots). Counts are scraped from public pages — not royalty-bearing stream counts.", + "summary": "Album measurements", + "description": "Latest measured count per track on an album, from the measurement store. `{id}` is a Spotify album id.", "parameters": [ { - "name": "spotify_album_id", - "in": "query", + "name": "id", + "in": "path", "required": true, - "description": "Spotify album id.", + "schema": { + "type": "string" + }, + "description": "Spotify album id." + }, + { + "name": "platform", + "in": "query", "schema": { "type": "string", - "example": "5SKnXCvB4fcGSZu32o3LRY" - } + "default": "spotify" + }, + "description": "Platform to read. Currently `spotify`." + }, + { + "name": "metric", + "in": "query", + "schema": { + "type": "string", + "default": "platform_displayed_play_count" + }, + "description": "Metric to read." + }, + { + "name": "latest", + "in": "query", + "schema": { + "type": "boolean", + "default": true + }, + "description": "Return the latest capture per track (the only supported mode today)." } ], "responses": { "200": { - "description": "Latest snapshot counts for every track on the album.", + "description": "Latest per-track measured counts for the album.", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ResearchPlaycountsResponse" - } - } - } - }, - "400": { - "description": "Validation error — missing `spotify_album_id`.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ResearchErrorResponse" + "$ref": "#/components/schemas/ResearchAlbumMeasurementsResponse" } } } @@ -2882,22 +2889,22 @@ } } }, - "404": { - "description": "No snapshot exists for this album yet — create one with `POST /api/research/snapshots`.", + "402": { + "description": "Insufficient credits.", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ResearchErrorResponse" + "$ref": "#/components/schemas/ResearchInsufficientCreditsResponse" } } } }, - "402": { - "description": "Insufficient research credits — the body includes a `checkoutUrl` to top up.", + "404": { + "description": "No capture exists for this album yet — create a `current` measurement-job.", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ResearchInsufficientCreditsResponse" + "$ref": "#/components/schemas/ResearchErrorResponse" } } } @@ -2905,32 +2912,33 @@ } } }, - "/api/research/snapshots": { + "/api/research/measurement-jobs": { "post": { - "description": "Capture platform-displayed play counts for a whole catalog, album list, or ISRC list in one server-side snapshot. Executes asynchronously; one album call captures all of its tracks. The cost estimate is returned before any scraper spend. Two snapshots over time yield per-track run-rates via [GET /api/research/track/playcount-deltas](/api-reference/research/track-playcount-deltas).", + "summary": "Create a measurement job", + "description": "One async ingest resource. `source:\"current\"` captures present counts via the snapshot pipeline. `source:\"historical\"` enqueues each resolved recording for Songstats deep backfill ranked by all-time streams (idempotent — songs already carrying `songstats` history are skipped; no track is fetched twice). Provide exactly one of `catalog_id` / `album_ids` / `isrcs` in `scope`.", "requestBody": { "required": true, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ResearchSnapshotRequest" + "$ref": "#/components/schemas/ResearchMeasurementJobRequest" } } } }, "responses": { "202": { - "description": "Snapshot accepted and queued.", + "description": "Job accepted.", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ResearchSnapshotResponse" + "$ref": "#/components/schemas/ResearchMeasurementJobResponse" } } } }, "400": { - "description": "Validation error — provide exactly one of `catalog_id`, `album_ids`, `isrcs`.", + "description": "Validation error — provide a `source` and exactly one of `scope.catalog_id`, `scope.album_ids`, `scope.isrcs`.", "content": { "application/json": { "schema": { @@ -2950,87 +2958,7 @@ } }, "429": { - "description": "Per-organization monthly snapshot cap reached.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ResearchErrorResponse" - } - } - } - } - } - } - }, - "/api/research/track/playcount-deltas": { - "get": { - "description": "Play-count change between [snapshots](/api-reference/research/snapshots) for one recording, with an annualized run-rate. Requires at least two captures in the window; returns an empty `deltas` array (not an error) when history is insufficient — create captures with [POST /api/research/snapshots](/api-reference/research/snapshots).", - "parameters": [ - { - "name": "isrc", - "in": "query", - "required": true, - "description": "Track ISRC.", - "schema": { - "type": "string", - "example": "USUYG1069896" - } - }, - { - "name": "since", - "in": "query", - "required": true, - "description": "Window start (inclusive).", - "schema": { - "type": "string", - "format": "date", - "example": "2026-06-09" - } - }, - { - "name": "until", - "in": "query", - "required": false, - "description": "Window end (inclusive). Defaults to the latest capture.", - "schema": { - "type": "string", - "format": "date" - } - } - ], - "responses": { - "200": { - "description": "Per-platform deltas between the nearest captures.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ResearchPlaycountDeltasResponse" - } - } - } - }, - "400": { - "description": "Validation error — missing `isrc` or `since`.", - "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": "Unknown ISRC.", + "description": "Per-organization monthly cap reached (current jobs).", "content": { "application/json": { "schema": { @@ -3038,16 +2966,6 @@ } } } - }, - "402": { - "description": "Insufficient research credits — the body includes a `checkoutUrl` to top up.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ResearchInsufficientCreditsResponse" - } - } - } } } } @@ -5106,359 +5024,273 @@ }, "additionalProperties": true }, - "ResearchTrackHistoricStatsResponse": { + "ResearchInsufficientCreditsResponse": { "type": "object", - "description": "Historic per-track statistics for the requested `source`(s). Same envelope as `ResearchTrackStatsResponse`, but each source's `data` holds a `history` time-series. When only `spotify` is requested, the response is the minimal envelope `{ result, stats }` served entirely from the measurement store.", + "description": "Returned (402) when the account lacks research credits and auto-recharge did not cover the call.", "properties": { - "status": { + "error": { "type": "string", - "example": "success" + "enum": [ + "insufficient_credits" + ] }, - "result": { - "type": "string", - "example": "success" + "remaining_credits": { + "type": "integer", + "example": 0 }, - "message": { + "required_credits": { + "type": "integer", + "example": 5 + }, + "checkoutUrl": { "type": "string", - "example": "Data Retrieved." + "description": "Stripe checkout link to top up credits." }, - "stats": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ResearchTrackHistoricStat" - } + "declineReason": { + "type": "string", + "description": "Card decline reason when auto-recharge was attempted and failed." + } + }, + "required": [ + "error", + "remaining_credits", + "required_credits", + "checkoutUrl" + ] + }, + "ResearchMeasurementPoint": { + "type": "object", + "properties": { + "date": { + "type": "string", + "format": "date", + "example": "2026-06-12" }, - "track_info": { - "$ref": "#/components/schemas/ResearchTrackInfo" + "value": { + "type": "number", + "description": "Cumulative metric value as of `date`.", + "example": 297289495 }, - "source_ids": { - "type": "array", - "description": "All source identifiers Songstats tracks for this recording.", - "items": { - "type": "string" - } + "data_source": { + "type": "string", + "example": "songstats" } } }, - "ResearchTrackHistoricStat": { + "ResearchMeasurementsResponse": { "type": "object", - "description": "Per-source historic stats for a single track.", + "description": "A track's measured series, or — when `aggregate` is set — the derived projection.", "properties": { - "source": { + "status": { "type": "string", - "description": "Platform the stats belong to, e.g. `spotify`, `apple_music`, `deezer`." + "example": "success" }, - "data": { + "id": { + "type": "string", + "description": "The provider-neutral track id queried.", + "example": "USQY51771120" + }, + "platform": { + "type": "string", + "example": "spotify" + }, + "metric": { + "type": "string", + "example": "platform_displayed_play_count" + }, + "series": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ResearchMeasurementPoint" + }, + "description": "Present unless `aggregate` is set." + }, + "aggregate": { "type": "object", + "nullable": true, + "description": "Present when `aggregate=run_rate`.", "properties": { - "history": { - "type": "array", - "description": "Per-date snapshots, oldest→newest. `streams_total` is the cumulative play count *as of* that date — diff two dates for streams gained between them. Keys vary by source; extra aggregate keys appear when `with_aggregates=true`.", - "items": { - "type": "object", - "additionalProperties": true, - "properties": { - "date": { - "type": "string", - "format": "date" - }, - "streams_total": { - "type": "integer", - "example": 574845367 - }, - "popularity_current": { - "type": "integer" - }, - "playlists_current": { - "type": "integer" - }, - "playlist_reach_current": { - "type": "integer" - }, - "data_source": { - "type": "string", - "description": "Per-point provenance. Provenance of the numbers: `apify_spotify_playcount` (platform-displayed count from the measurement store), `songstats` (Songstats backfill), or `granted_analytics` (customer-granted data).", - "example": "songstats" - } - } - } + "kind": { + "type": "string", + "example": "run_rate" + }, + "window_days": { + "type": "integer", + "example": 365 + }, + "delta": { + "type": "number", + "example": 42000000 + }, + "run_rate_annualized": { + "type": "number", + "example": 42000000 } } } - }, - "additionalProperties": true + } }, - "ResearchPlaycount": { + "ResearchAlbumMeasurementItem": { "type": "object", - "description": "Latest platform-displayed play count for one track, from the most recent album snapshot.", "properties": { "isrc": { "type": "string", - "description": "ISRC of the recording. Tracks are mapped automatically on capture (ISRCs resolved via the Spotify API), so this is always present.", - "example": "USUYG1069896" + "example": "USQY51771120" }, "spotify_track_id": { - "type": [ - "string", - "null" - ], - "description": "Spotify track id; `null` when the recording has an album mapping but no track-id mapping yet." + "type": "string", + "nullable": true }, "name": { "type": "string", - "description": "Track title as displayed on the platform." + "nullable": true }, - "platform_displayed_play_count": { - "type": "integer", - "format": "int64", - "description": "Play count as displayed on the public platform page. Scraped, not a royalty-bearing stream count — calibrate against customer-granted analytics for transaction-grade numbers.", - "example": 1331384578 + "value": { + "type": "number", + "description": "Latest measured count.", + "example": 297289495 }, "captured_at": { "type": "string", - "format": "date-time", - "description": "When the snapshot captured this count." + "format": "date-time" }, "data_source": { "type": "string", - "description": "Provenance of the numbers: `apify_spotify_playcount` (platform-displayed count from the measurement store), `songstats` (Songstats backfill), or `granted_analytics` (customer-granted data).", "example": "apify_spotify_playcount" } } }, - "ResearchPlaycountsResponse": { + "ResearchAlbumMeasurementsResponse": { "type": "object", - "description": "Latest snapshot play counts for every track on an album.", "properties": { "status": { "type": "string", "example": "success" }, - "album": { - "type": "object", - "properties": { - "spotify_album_id": { - "type": "string", - "example": "5SKnXCvB4fcGSZu32o3LRY" - }, - "name": { - "type": [ - "string", - "null" - ], - "description": "Album name from the song record." - }, - "label": { - "type": [ - "string", - "null" - ], - "description": "Currently always `null` — populated once snapshots persist album metadata." - }, - "copyright": { - "type": [ - "string", - "null" - ], - "description": "℗-line for ownership re-verification. Currently always `null` — populated once snapshots persist album metadata." - } - } + "id": { + "type": "string", + "description": "Spotify album id.", + "example": "70Zkfb99ladZ3q0JVg97co" + }, + "platform": { + "type": "string", + "example": "spotify" }, - "playcounts": { + "metric": { + "type": "string", + "example": "platform_displayed_play_count" + }, + "measurements": { "type": "array", "items": { - "$ref": "#/components/schemas/ResearchPlaycount" + "$ref": "#/components/schemas/ResearchAlbumMeasurementItem" } } } }, - "ResearchSnapshotRequest": { + "ResearchMeasurementJobScope": { "type": "object", "description": "Provide exactly one of `catalog_id`, `album_ids`, or `isrcs`.", - "required": [ - "platforms" - ], "properties": { "catalog_id": { "type": "string", - "format": "uuid", - "description": "Catalog to snapshot; resolves to its tracks server-side." + "format": "uuid" }, "album_ids": { "type": "array", "items": { "type": "string" - }, - "description": "Spotify album ids to snapshot (one album call captures all of its tracks)." + } }, "isrcs": { "type": "array", "items": { "type": "string" - }, - "description": "ISRCs to snapshot; resolved to albums server-side where a mapping exists." + } + } + } + }, + "ResearchMeasurementJobRequest": { + "type": "object", + "required": [ + "scope", + "source" + ], + "properties": { + "scope": { + "$ref": "#/components/schemas/ResearchMeasurementJobScope" + }, + "source": { + "type": "string", + "enum": [ + "current", + "historical" + ], + "description": "`current` = capture present counts (Apify snapshot). `historical` = enqueue Songstats deep backfill." }, "platforms": { "type": "array", "items": { "type": "string" }, - "description": "Platforms to capture. Currently `spotify`.", + "default": [ + "spotify" + ], "example": [ "spotify" ] - }, - "schedule": { - "type": "string", - "enum": [ - "once", - "monthly" - ], - "default": "once", - "description": "`once` runs a single snapshot; `monthly` registers a recurring snapshot." } } }, - "ResearchSnapshotResponse": { + "ResearchMeasurementJobResponse": { "type": "object", - "description": "Snapshot accepted. Execution is asynchronous; the cost estimate is returned before any scraper spend.", + "description": "For `current` jobs, `id` is pollable. For `historical` jobs, `enqueued`/`skipped` summarize the backfill enqueue (the queue drains via the daily worker).", "properties": { "status": { "type": "string", "example": "success" }, - "snapshot_id": { + "source": { "type": "string", - "format": "uuid" + "example": "historical" + }, + "id": { + "type": "string", + "format": "uuid", + "nullable": true, + "description": "Job id for `current` jobs; null for `historical`." }, "state": { "type": "string", "enum": [ "queued" ], + "nullable": true, "example": "queued" }, - "album_count": { + "enqueued": { "type": "integer", - "description": "Albums the snapshot will capture.", - "example": 570 - }, - "estimated_cost_usd": { - "type": "number", - "description": "Estimated scraper cost for the full snapshot, returned before execution.", - "example": 1.71 - } - } - }, - "ResearchPlaycountDelta": { - "type": "object", - "description": "Change in a metric between the two snapshots nearest the requested window bounds.", - "properties": { - "platform": { - "type": "string", - "example": "spotify" - }, - "metric": { - "type": "string", - "example": "platform_displayed_play_count" - }, - "since": { - "type": "object", - "description": "Nearest capture at/after the `since` date.", - "properties": { - "captured_at": { - "type": "string", - "format": "date-time" - }, - "value": { - "type": "integer", - "format": "int64" - } - } - }, - "until": { - "type": "object", - "description": "Nearest capture at/before the `until` date (latest capture when `until` is omitted).", - "properties": { - "captured_at": { - "type": "string", - "format": "date-time" - }, - "value": { - "type": "integer", - "format": "int64" - } - } - }, - "delta": { - "type": "integer", - "format": "int64", - "description": "`until.value - since.value`." + "nullable": true, + "description": "`historical`: recordings enqueued for backfill.", + "example": 842 }, - "days": { + "skipped": { "type": "integer", - "description": "Days between the two captures." + "nullable": true, + "description": "`historical`: recordings already backfilled (skipped).", + "example": 3 }, - "run_rate_annualized": { + "album_count": { "type": "integer", - "format": "int64", - "description": "`delta` scaled to 365 days — a TTM proxy once captures span a meaningful window." - } - } - }, - "ResearchPlaycountDeltasResponse": { - "type": "object", - "description": "Per-platform deltas for one recording. `deltas` is empty when fewer than two captures exist in the window — the request never blocks on missing history.", - "properties": { - "status": { - "type": "string", - "example": "success" - }, - "isrc": { - "type": "string", - "example": "USUYG1069896" + "nullable": true, + "description": "`current`: albums the job will capture." }, - "deltas": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ResearchPlaycountDelta" - } + "estimated_cost_usd": { + "type": "number", + "nullable": true, + "description": "`current`: scraper cost estimate before spend." } } - }, - "ResearchInsufficientCreditsResponse": { - "type": "object", - "description": "Returned (402) when the account lacks research credits and auto-recharge did not cover the call.", - "properties": { - "error": { - "type": "string", - "enum": [ - "insufficient_credits" - ] - }, - "remaining_credits": { - "type": "integer", - "example": 0 - }, - "required_credits": { - "type": "integer", - "example": 5 - }, - "checkoutUrl": { - "type": "string", - "description": "Stripe checkout link to top up credits." - }, - "declineReason": { - "type": "string", - "description": "Card decline reason when auto-recharge was attempted and failed." - } - }, - "required": [ - "error", - "remaining_credits", - "required_credits", - "checkoutUrl" - ] } }, "responses": { diff --git a/api-reference/research/playcounts.mdx b/api-reference/research/album-measurements.mdx similarity index 56% rename from api-reference/research/playcounts.mdx rename to api-reference/research/album-measurements.mdx index 0c0311dc..6fd8e6f7 100644 --- a/api-reference/research/playcounts.mdx +++ b/api-reference/research/album-measurements.mdx @@ -1,4 +1,4 @@ --- -title: 'Play Counts' -openapi: "/api-reference/openapi/research.json GET /api/research/playcounts" +title: 'Album measurements' +openapi: "/api-reference/openapi/research.json GET /api/research/albums/{id}/measurements" --- diff --git a/api-reference/research/snapshots.mdx b/api-reference/research/measurement-jobs.mdx similarity index 58% rename from api-reference/research/snapshots.mdx rename to api-reference/research/measurement-jobs.mdx index 6f4438f3..22fcb509 100644 --- a/api-reference/research/snapshots.mdx +++ b/api-reference/research/measurement-jobs.mdx @@ -1,4 +1,4 @@ --- -title: 'Snapshots' -openapi: "/api-reference/openapi/research.json POST /api/research/snapshots" +title: 'Create measurement job' +openapi: "/api-reference/openapi/research.json POST /api/research/measurement-jobs" --- diff --git a/api-reference/research/track-historic-stats.mdx b/api-reference/research/measurements.mdx similarity index 56% rename from api-reference/research/track-historic-stats.mdx rename to api-reference/research/measurements.mdx index 18913ca7..4500e049 100644 --- a/api-reference/research/track-historic-stats.mdx +++ b/api-reference/research/measurements.mdx @@ -1,4 +1,4 @@ --- -title: 'Track Historic Stats' -openapi: "/api-reference/openapi/research.json GET /api/research/track/historic-stats" +title: 'Track measurements' +openapi: "/api-reference/openapi/research.json GET /api/research/tracks/{id}/measurements" --- diff --git a/api-reference/research/track-playcount-deltas.mdx b/api-reference/research/track-playcount-deltas.mdx deleted file mode 100644 index 6c1123f1..00000000 --- a/api-reference/research/track-playcount-deltas.mdx +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: 'Track Play Count Deltas' -openapi: "/api-reference/openapi/research.json GET /api/research/track/playcount-deltas" ---- diff --git a/docs.json b/docs.json index 0576d06e..eb862140 100644 --- a/docs.json +++ b/docs.json @@ -121,10 +121,9 @@ "api-reference/research/tracks", "api-reference/research/track", "api-reference/research/track-stats", - "api-reference/research/track-historic-stats", - "api-reference/research/track-playcount-deltas", - "api-reference/research/playcounts", - "api-reference/research/snapshots" + "api-reference/research/measurements", + "api-reference/research/album-measurements", + "api-reference/research/measurement-jobs" ] }, {