Appearance
API reference · API keys · Analytics API guide
Apple and Spotify engagement imports
- Method:
GET - Path:
/v1/me/podcasts/{podcast_id}/analytics/platforms - Operation ID:
getPlatformMetrics - Tags: Analytics
The latest Apple Podcasts or Spotify export you imported in the app, show-level and per episode. Each import replaces the previous one for that source, so there is no date range. Requires permission to see live analytics and use the analytics API (analytics.view), and a plan with engagement imports.
Scope: analytics:read.
Make a request
Set these environment variables before running a sample. Each language reads the same values; no identifier needs replacing inside the code.
| Variable | Value to supply |
|---|---|
SHOWFM_API_KEY | Your API key. Keep it on your server. |
PODCAST_ID | A podcast UUID returned by List your podcasts. |
Samples run on your server: curl 7.76+, Node.js 22+ (TypeScript via your TypeScript runner), Python 3, or PHP 8 with the cURL extension. Never put your API key in browser code.
bash
: "${SHOWFM_API_KEY:?Set SHOWFM_API_KEY in your environment}"
: "${PODCAST_ID:?Set PODCAST_ID in your environment}"
curl --fail-with-body --silent --show-error --get \
"https://api.show.fm/v1/me/podcasts/${PODCAST_ID}/analytics/platforms" \
--header "Authorization: Bearer ${SHOWFM_API_KEY}" \
--data-urlencode "source=apple" \
--data-urlencode "limit=20"ts
function env(name: string): string {
const value = process.env[name];
if (!value) throw new Error(`Set ${name} in your environment`);
return value;
}
async function main() {
const url = new URL(`https://api.show.fm/v1/me/podcasts/${encodeURIComponent(env("PODCAST_ID"))}/analytics/platforms`);
url.searchParams.set("source", "apple");
url.searchParams.set("limit", "20");
const response = await fetch(url, {
headers: { Authorization: `Bearer ${env("SHOWFM_API_KEY")}` },
signal: AbortSignal.timeout(30_000)
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${await response.text()}`);
}
console.log(JSON.stringify(await response.json(), null, 2));
}
main().catch(error => {
console.error(error);
process.exitCode = 1;
});js
function env(name) {
const value = process.env[name];
if (!value) throw new Error(`Set ${name} in your environment`);
return value;
}
async function main() {
const url = new URL(`https://api.show.fm/v1/me/podcasts/${encodeURIComponent(env("PODCAST_ID"))}/analytics/platforms`);
url.searchParams.set("source", "apple");
url.searchParams.set("limit", "20");
const response = await fetch(url, {
headers: { Authorization: `Bearer ${env("SHOWFM_API_KEY")}` },
signal: AbortSignal.timeout(30_000)
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${await response.text()}`);
}
console.log(JSON.stringify(await response.json(), null, 2));
}
main().catch(error => {
console.error(error);
process.exitCode = 1;
});python
import os
import json
from urllib.parse import quote, urlencode
from urllib.request import Request, urlopen
from urllib.error import HTTPError
url = f'https://api.show.fm/v1/me/podcasts/{quote(os.environ["PODCAST_ID"], safe="")}/analytics/platforms'
url += "?" + urlencode({
"source": "apple",
"limit": "20",
})
request = Request(url, headers={
"Authorization": "Bearer " + os.environ["SHOWFM_API_KEY"]
})
try:
with urlopen(request, timeout=30) as response:
print(json.dumps(json.load(response), indent=2))
except HTTPError as error:
raise RuntimeError(f"HTTP {error.code}: {error.read().decode()}") from errorphp
<?php
function requiredEnv(string $name): string {
$value = getenv($name);
if ($value === false || $value === '') {
throw new RuntimeException("Set $name in your environment");
}
return $value;
}
$url = "https://api.show.fm" . "/v1/me/podcasts/" . rawurlencode(requiredEnv("PODCAST_ID")) . "/analytics/platforms";
$url .= '?' . http_build_query([
"source" => "apple",
"limit" => "20",
], '', '&', PHP_QUERY_RFC3986);
$curl = curl_init($url);
curl_setopt_array($curl, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ["Authorization: Bearer " . requiredEnv("SHOWFM_API_KEY")],
]);
$body = curl_exec($curl);
if ($body === false) {
throw new RuntimeException(curl_error($curl));
}
$status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
if ($status < 200 || $status >= 300) {
throw new RuntimeException("HTTP $status: $body");
}
echo json_encode(json_decode($body, false, 512, JSON_THROW_ON_ERROR), JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR) . PHP_EOL;Base URL
https://api.show.fm
Authentication
apiKey: HTTP bearer
An API key from Settings → API keys.
showfm_live_in production.
Path parameters
podcast_id(required):string, format:uuidThe podcast id (UUID).
Query parameters
source(required):string, possible values:"apple", "spotify"limit:integer, default:20, minimum:1, maximum:100Page size, 1 to 100. Default 20.
cursor:stringThe
pagination.next_cursorvalue from the previous page.
Responses
Browse the documented response statuses, headers and body fields.
200 OK
Response headers
RateLimit-PolicyThe bucket that applied, e.g.
"account";q=60;w=60.stringRateLimitRequests left and seconds to reset, e.g.
"account";r=59;t=42.stringETagstring
Response body
Content type: application/json
no additional properties
data(required): PlatformMetricspagination(required): PaginationOpaque keyset cursor; pass it back as
cursor.
Example response
Illustrative data; your identifiers and figures will differ.
json
{
"data": {
"podcast_id": "123e4567-e89b-12d3-a456-426614174000",
"source": "apple",
"imported_at": null,
"as_of": null,
"show_metrics": {},
"episodes": []
},
"pagination": {
"next_cursor": null
}
}304 Not modified (matching If-None-Match).
If-None-Match). This response has no body.
400, 401, 403, 404, 500, 503
400invalid_request: a bad parameter, or a key sent in the URL.401unauthorized(no Bearer key) orinvalid_api_key.403insufficient_scope,forbidden(role or team-key setting) orplan_upgrade_required.404not_found: the key cannot reach this resource.500internal_error. Retry; quoteX-Request-Idif it persists.503service_unavailable. Retry shortly.
Response body
Content type: application/json
schema: Error, no additional properties
error(required):object, no additional propertiescode(required):string, possible values:"invalid_request", "not_found", "unavailable", "unauthorized", "invalid_api_key", "insufficient_scope", "forbidden", "plan_upgrade_required", "rate_limited", "service_unavailable", "internal_error", "not_implemented"message(required):stringdetails:arrayItems:
no additional properties
field(required):stringmessage(required):stringreason:stringA stable reason a client may branch on, for example
outside_retention.
docs_url:stringrequest_id:string
429 rate_limited. Wait for Retry-After seconds.
rate_limited. Wait for Retry-After seconds. Response headers
Retry-AfterintegerRateLimit-PolicyThe bucket that applied, e.g.
"account";q=60;w=60.stringRateLimitRequests left and seconds to reset, e.g.
"account";r=59;t=42.string
Response body
Content type: application/json
Uses the shared Error schema. View response fields.