Available on Professional and Network with API access enabled. All examples use fictitious IDs.
01 / FIRST REQUEST
From key to first response
Create a key, save it as PODSTATUS_API_KEY on your server, then list your podcasts. Use an ID from that response in subsequent requests.
Keep the key on your backend. Send Authorization: Bearer YOUR_KEY with each request. Choose only the permissions your integration needs.
If API access has not been enabled for your Professional or Network account, contact Podstatus support to request activation. Then open API keys, choose a name, permissions and expiry (1–365 days), and copy the key shown once. Keep it in an environment variable on your server. Revoke unused keys from the same page. An account can have at most 20 active keys.
Every API request requires Authorization: Bearer YOUR_KEY and should send Accept: application/json. Keys identify the current account and retain its current subscription restrictions. A standalone trial without a qualifying subscription does not grant API access. Existing subscribed-trial and grace-period behavior follows the account's subscription rules.
The base URL is https://podstatus.com/api/v1. IDs below are fictitious. Use the ID returned by the podcast list.
Run this on your backend with an environment variable. Serve only the selected dashboard results to your visitors and authenticate access to a private dashboard.
const base = 'https://podstatus.com/api/v1';
const headers = {
Authorization: `Bearer ${process.env.PODSTATUS_API_KEY}`,
Accept: 'application/json'
};
const response = await fetch(`${base}/podcasts`, { headers });
const body = await response.json();
if (!response.ok) throw new Error(body.error?.code ?? `HTTP ${response.status}`);
const podcasts = body.data;
This example uses the Python standard library.
import json
import os
from urllib.request import Request, urlopen
from urllib.error import HTTPError
request = Request(
'https://podstatus.com/api/v1/podcasts',
headers={
'Authorization': 'Bearer ' + os.environ['PODSTATUS_API_KEY'],
'Accept': 'application/json',
},
)
try:
with urlopen(request, timeout=30) as response:
podcasts = json.load(response)['data']
except HTTPError as error:
details = json.load(error)
raise RuntimeError(details['error']['code']) from error
02 / REFERENCE
Find your endpoint
All paths below are relative to the base URL. Examples show representative data and abbreviated metadata.
Find supported countries and categories for Apple Podcasts, Spotify and iVoox. Spotify category charts are limited to category_countries. iVoox Chile and Peru share the Spain chart, as shown in country_aliases.
Required permission rankings:read
Parameters
Parameters for GET /ranking-dimensions
Name
Type / location
Details
platformOptional
stringquery
apple, spotify, ivoox Chart provider. Use ranking-dimensions to find supported combinations.
Get the latest chart positions of one podcast in a country, across all categories or a selected category. Supports Apple Podcasts, Spotify and iVoox. Uses the latest import marker; missing podcasts are not_observed because a marker does not prove every chart was imported successfully.
Required permission rankings:read
Parameters
Parameters for GET /podcasts/{id}/rankings/latest
Name
Type / location
Details
idRequired
integerpath
Minimum 1. Podstatus ID from GET /podcasts; this is not the Apple Podcasts ID.
platformRequired
stringquery
apple, spotify, ivoox Chart provider. Use ranking-dimensions to find supported combinations.
countryRequired
stringquery
Maximum 2 characters. Two-letter country code, case-insensitive. Chart support varies by platform.
categoryOptional
integerquery
Minimum 0. Category ID from ranking-dimensions. Latest rankings include all supported categories when omitted.
Find every country and category where a podcast has an observed ranking in the latest snapshots, in one call. Omit platform to include all connected chart platforms, or select apple, spotify or ivoox. Returns only observed positions, with snapshot dates in meta.snapshots. An absent chart does not prove the podcast is unranked. iVoox cl and pe share source_country es; do not count them as independent charts.
Required permission rankings:read
Parameters
Parameters for GET /podcasts/{id}/rankings/overview
Name
Type / location
Details
idRequired
integerpath
Minimum 1. Podstatus ID from GET /podcasts; this is not the Apple Podcasts ID.
platformOptional
stringquery
apple, spotify, ivoox Chart provider. Use ranking-dimensions to find supported combinations.
Track a podcast chart position over a date range in a country and category. Apple supports hourly positions and daily best observed ranks; Spotify and iVoox provide daily snapshots. Missing samples are null, not_observed.
Required permission rankings:read
Parameters
Parameters for GET /podcasts/{id}/rankings/history
Name
Type / location
Details
idRequired
integerpath
Minimum 1. Podstatus ID from GET /podcasts; this is not the Apple Podcasts ID.
platformRequired
stringquery
apple, spotify, ivoox Chart provider. Use ranking-dimensions to find supported combinations.
countryRequired
stringquery
Maximum 2 characters. Two-letter country code, case-insensitive. Chart support varies by platform.
categoryRequired
integerquery
Minimum 0. Category ID from ranking-dimensions. Latest rankings include all supported categories when omitted.
fromOptional
stringquery
YYYY-MM-DD; for resolution=hour use YYYY-MM-DDTHH:MM:SSZ and supply both bounds, aligned to whole hours. Maximum 90 daily samples or 24 elapsed hours.
toOptional
stringquery
YYYY-MM-DD; for resolution=hour use YYYY-MM-DDTHH:MM:SSZ and supply both bounds, aligned to whole hours. Maximum 90 daily samples or 24 elapsed hours.
resolutionOptional
stringquery
day, hour Default: day. Hourly data is available for Apple rankings only and is retained for about the last 27 hours; older hours return not_observed. Use daily resolution for longer periods.
Summarize a podcast chart performance over a date range: best observed rank and change from start to end, optionally by month. Positive change means improvement. Missing boundaries produce null change.
Required permission rankings:read
Parameters
Parameters for GET /podcasts/{id}/rankings/summary
Name
Type / location
Details
idRequired
integerpath
Minimum 1. Podstatus ID from GET /podcasts; this is not the Apple Podcasts ID.
platformRequired
stringquery
apple, spotify, ivoox Chart provider. Use ranking-dimensions to find supported combinations.
countryRequired
stringquery
Maximum 2 characters. Two-letter country code, case-insensitive. Chart support varies by platform.
categoryRequired
integerquery
Minimum 0. Category ID from ranking-dimensions. Latest rankings include all supported categories when omitted.
fromOptional
stringquery
YYYY-MM-DD; for resolution=hour use YYYY-MM-DDTHH:MM:SSZ and supply both bounds, aligned to whole hours. Maximum 90 daily samples or 24 elapsed hours.
toOptional
stringquery
YYYY-MM-DD; for resolution=hour use YYYY-MM-DDTHH:MM:SSZ and supply both bounds, aligned to whole hours. Maximum 90 daily samples or 24 elapsed hours.
resolutionOptional
stringquery
day, hour Default: day. Hourly data is available for Apple rankings only and is retained for about the last 27 hours; older hours return not_observed. Use daily resolution for longer periods.
group_byOptional
stringquery
period, month Aggregation of the requested interval; monthly groups may cover partial months.
Published episodes with downloads matched by exact audio URL. Unmatched or unavailable downloads are null. from/to filter downloads, published_from/to filter publication.
Required permission analytics:read
Parameters
Parameters for GET /podcasts/{id}/episodes
Name
Type / location
Details
idRequired
integerpath
Minimum 1. Podstatus ID from GET /podcasts; this is not the Apple Podcasts ID.
fromOptional
stringquery
Inclusive UTC date in YYYY-MM-DD format. Maximum 90 days. Defaults to the 30 days ending yesterday; a supplied to sets the default from.
toOptional
stringquery
Inclusive UTC date in YYYY-MM-DD format. Maximum 90 days. Defaults to the 30 days ending yesterday; a supplied to sets the default from.
pageOptional
integerquery
Minimum 1. Maximum 10000. Page number. Default: 1. The offset (page - 1) × per_page must not exceed 10000.
per_pageOptional
integerquery
Minimum 1. Maximum 50. Items per page. Default: 25.
sortOptional
stringquery
published, downloads Default: published (newest first). downloads sorts by descending downloads for the requested period.
published_fromOptional
stringquery
Inclusive episode publication date in YYYY-MM-DD format.
published_toOptional
stringquery
Inclusive episode publication date in YYYY-MM-DD format.
Read stored listener reviews filtered by date, country, rating or provider. provider is apple, stitcher or podchaser (unknown for unrecognized legacy values); provider_id retains the numeric source code. Follow pagination for review text.
Required permission reviews:read
Parameters
Parameters for GET /podcasts/{id}/reviews
Name
Type / location
Details
idRequired
integerpath
Minimum 1. Podstatus ID from GET /podcasts; this is not the Apple Podcasts ID.
fromOptional
stringquery
Inclusive UTC date in YYYY-MM-DD format. Maximum 90 days. Defaults to the 30 days ending yesterday; a supplied to sets the default from.
toOptional
stringquery
Inclusive UTC date in YYYY-MM-DD format. Maximum 90 days. Defaults to the 30 days ending yesterday; a supplied to sets the default from.
pageOptional
integerquery
Minimum 1. Maximum 10000. Page number. Default: 1. The offset (page - 1) × per_page must not exceed 10000.
per_pageOptional
integerquery
Minimum 1. Maximum 50. Items per page. Default: 25.
countryOptional
stringquery
Maximum 2 characters. Two-letter country code, case-insensitive. Chart support varies by platform.
ratingOptional
integerquery
Minimum 1. Maximum 5. Exact review rating, from 1 to 5.
providerOptional
stringquery
apple, stitcher, podchaser Review source: apple, stitcher or podchaser.
Count stored listener reviews and summarize their star ratings, overall and by provider, without downloading every review. Omit dates for all stored history; optional from/to are inclusive and can span more than 90 days. Only ratings 1 through 5 contribute to the average. These are recorded review statistics, not the platforms total ratings or an analysis of the review text.
Required permission reviews:read
Parameters
Parameters for GET /podcasts/{id}/reviews/summary
Name
Type / location
Details
idRequired
integerpath
Minimum 1. Podstatus ID from GET /podcasts; this is not the Apple Podcasts ID.
fromOptional
stringquery
Inclusive UTC date in YYYY-MM-DD format. Omit either bound to leave that side unbounded; omit both for all stored reviews. No 90-day limit for this aggregate.
toOptional
stringquery
Inclusive UTC date in YYYY-MM-DD format. Omit either bound to leave that side unbounded; omit both for all stored reviews. No 90-day limit for this aggregate.
countryOptional
stringquery
Maximum 2 characters. Two-letter country code, case-insensitive. Chart support varies by platform.
providerOptional
stringquery
apple, stitcher, podchaser Review source: apple, stitcher or podchaser.
Queue a monthly or yearly report and spend one report credit. Requires explicit user confirmation, reports:write and Idempotency-Key. A completed report credit cannot be undone. Replays return the original report without consuming another credit; terminal failures refund once.
Required permission reports:write
Parameters
Parameters for POST /podcasts/{id}/reports
Name
Type / location
Details
idRequired
integerpath
Minimum 1. Podstatus ID from GET /podcasts; this is not the Apple Podcasts ID.
Idempotency-KeyRequired
stringheader
Maximum 128 characters. Pattern: ^[a-zA-Z0-9._-]+$
JSON body
Name
Type
Details
period_typeRequired
string
monthly, yearly Calendar period covered by the report.
periodRequired
string
YYYY-MM for monthly reports, YYYY for yearly reports. From 2019 through the current UTC period; future periods are rejected. The unfinished current period is allowed and marked partial_period.
optionsOptional
object
Boolean flags; omit a flag to use the existing report default.
Available flags
No matching endpoints. Try a resource name such as “rankings” or “reports”.
03 / BUILD WITH AI
Give your assistant the full reference
The text version includes authentication, limits, errors, every endpoint and its schema. The OpenAPI contract includes the same request and response examples as this page.
Keys can grant podcasts:read, rankings:read, analytics:read, reviews:read, keywords:read, reports:read and reports:write. Each endpoint below identifies its permission.
Lists accept page and per_page, defaulting to page 1 and 25 items, with a maximum of 50. Responses include meta.page, meta.per_page and meta.has_more. Request the next page only while has_more is true. Pagination can shift as new records arrive. Podcasts sort by ID ascending; reports by ID descending; reviews by date then ID descending; keywords by date descending then ID ascending; episodes by publication descending then ID ascending (download sorting is descending, with that publication/ID order for ties). Audience groups sort by downloads descending then their dimension columns ascending. The maximum offset, (page - 1) * per_page, is 10,000. Narrow date or publication filters to retrieve older data.
Daily filters use YYYY-MM-DD, inclusive on both ends. When omitted, to defaults to yesterday in UTC and from to 29 days before to. Supply both dates for reproducible reporting. Detailed date ranges are limited to 90 days.
For hourly Apple rankings, set resolution=hour and supply both bounds as whole-hour UTC timestamps such as 2026-11-02T12:00:00Z. The maximum elapsed interval is 24 hours. Hourly positions are retained for about the last 27 hours, so older hourly ranges return not_observed; use daily resolution for earlier periods. Hourly ranges include their endpoints. The API converts the application's stored hourly times to UTC. Spotify and iVoox support daily resolution.
Latest rankings use the snapshot selected by the existing importer marker. snapshot_at (UTC timestamp) or snapshot_date (daily date) identifies that snapshot, even when an import is delayed. This marker does not prove that every chart page was downloaded successfully. A found podcast is ranked; an absent podcast is not_observed, never evidence that it left the chart. v1 does not emit not_ranked until per-chart completeness can be established.
Apple daily positions are the best observed positions for each day. Spotify and iVoox daily positions are stored chart snapshots. Historical gaps use null and not_observed, because a missing podcast row cannot prove that the entire chart was collected successfully. Daily and hourly samples may have different historical availability.
Use GET /podcasts/{id}/rankings/overview to find all observed countries and categories in one request. Omit platform for all connected chart platforms, or select apple, spotify or ivoox. meta.snapshots identifies the source snapshot for each connected platform, including null when no marker exists. Only observed rankings are returned; a missing country or category is not proof of being outside the chart. No historical position is substituted for a missing current observation.
iVoox requires a linked ivoox_id in the podcast record. Its Chile (cl) and Peru (pe) charts share Spain's stored chart. source_country identifies the actual chart source in latest, batch, overview and historical responses; ranking-dimensions.country_aliases describes these aliases. Do not count these as three independent charts.
Summaries expose best_observed_rank, its date, the requested boundary positions and change. Change equals start position minus end position. A positive value indicates improvement. If either boundary is missing, change is null. group_by=month splits the requested range into calendar-month portions.
OP3 provides downloads, which do not establish completed listening or unique people over a multi-day period. Distributions return downloads and the denominator recorded for the requested dimension. Unknown categories are retained, and percentages are rounded individually. They can differ slightly from 100 after rounding. An empty distribution has no observed dimension rows.
OP3 series preserve missing days as null. observed_total sums available observations only. A zero is returned when an actual stored row has zero. Monthly values aggregate only days within the requested range. Each month includes from, to, requested_days and available_days. available means all requested days were observed, even if the request covers only part of a calendar month. partial means some requested days are missing. not_observed means no requested day was observed and the value is null.
Episode from and to filter downloads. Use published_from and published_to for publication dates. Download matching uses the exact audio URL. Unmatched episodes retain null downloads. Keyword results include only keywords tracked for this account and podcast. Keyword country filters accept either case and treat GB and UK as aliases.
GET /podcasts/{id}/keywords lists tracked keywords, even when no ranking has been observed. It paginates by keyword ID ascending; /keyword-rankings continues to return dated observations.
GET /podcasts/{id}/reviews/summary returns counts, star distribution and average rating overall and by provider. Dates are optional: omit both to include all stored reviews, or use either or both bounds. This SQL aggregate can span more than 90 days. Only valid 1–5 star ratings contribute to rated_reviews and average_rating; the average is null when none exist. These statistics describe reviews recorded in Podstatus, not all platform ratings. Textual analysis still requires reading the relevant paginated reviews. Both endpoints support country and provider filters; only the review list accepts rating, because the summary already returns the star distribution. Review provider is apple, stitcher, podchaser or unknown; provider_id retains the legacy source code.
Professional starts with 20 requests per minute and 1,000 per UTC day. Network starts with 40 per minute and 2,000 per UTC day. These limits may be adjusted after load measurements. All keys share the account quota.
Minute windows start at the beginning of each UTC minute. Day windows reset at 00:00 UTC. Successful data responses expose meta.usage.limits and meta.usage.remaining (both with minute and day), plus UTC minute_reset_at and daily_reset_at. These counters belong to the current request, including cache hits.
REST and MCP HTTP responses also expose X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset for the minute window, and X-RateLimit-Daily-Limit, X-RateLimit-Daily-Remaining and X-RateLimit-Daily-Reset for the UTC day. Reset values are Unix timestamps in seconds. Headers are present after the account quota is checked, including account quota rejections; earlier authentication or admission failures may omit them. Daily remaining capacity reflects a completed internal-failure refund. Retry-After remains the delay to follow on rejected requests. Counters are a snapshot, not a guarantee against concurrent use.
Requests are also subject to shared entry limits before authentication, including invalid requests and unknown routes. These protections may return 429 or 503 without consuming an account request. Request bodies and query strings are limited to 8 KiB, with at most eight JSON nesting levels. Oversized requests return 413.
A REST request that passes plan and permission checks reserves quota before parameter validation and podcast access checks. MCP tool calls reserve after authentication, account eligibility and basic JSON-RPC validation, before checking the tool's name, permission and arguments. Admitted MCP tool calls therefore consume quota even when the tool is unknown or returns insufficient_scope. This includes validation errors, cache hits and report replays. A completed internal 5xx response releases its daily reservation once; it still consumes minute and ingress capacity. If the process crashes or the quota database is unavailable, the reservation can remain conservatively charged for that day; request support with the request ID rather than retrying indefinitely. Authentication, plan and already-exhausted quota errors do not consume account quota; permission errors also avoid quota consumption on REST. Separately, after 30 failed authentication attempts from one IP within a minute, further invalid authentication attempts receive 429 and Retry-After. Valid credentials from the same IP continue to work, subject to the usual entry and account limits.
A batch of at most five podcasts consumes one normal request. All podcasts must be accessible and use one platform and country. Internal calculation limits are common to both plans. Rebuild budgets count only cold calculations, including attempts that fail. A busy rebuild budget serves an acceptable stale result when one exists, or returns 503. Responses are capped at 2 MB; narrow the filters or page size if a response is too large. Cached responses do not reserve a calculation slot. Materialized result sets are bounded to 10,000 rows: exceeding the bound returns 422 query_too_large. Audience sums and episode download sums are aggregated in SQL over the complete requested interval before checking the number of result groups. They never silently truncate the source observations. Audience meta.truncated is always false in v1; a successful denominator covers all returned groups, including unknown categories. SQL time and query-count budgets also apply.
Fresh results are normally cached for five minutes. Historical corrections become visible after cache refresh. When calculation capacity is busy, an older result may be served for up to an additional 30 minutes with stale: true. Its source dates remain unchanged. A later request attempts another refresh. There is no promise of a scheduled background refresh.
When no usable cached result exists, a busy server returns 503. Account limits return 429. Honor Retry-After, add a small random delay, and cap retries. Avoid launching all cold dashboard requests at once. Cache results on your backend so each visitor can use the same prepared dashboard. Daily analytics normally need daily refreshes; ranking refreshes should follow available snapshot dates.
List the account's podcasts and inspect their connected integrations.
Fetch valid chart dimensions, then request latest rankings, history and summary for the selected chart.
For OP3-enabled podcasts, request downloads, country distribution, device distribution and episodes.
Display unavailable metrics explicitly and preserve source dates.
Refresh sequentially with bounded retries and reuse your backend's saved results between visits.
Request a monthly report once and poll its status with increasing delays.
The API can supply rankings, downloads, device and country distributions, episode downloads and reviews. Listening hours, completion rates, audio starts and occupational audience claims require other sources.
Reports consume the existing monthly or flex report allowance. Only one queued or processing report is admitted per account. Generation runs asynchronously and can take several minutes. Shared queue capacity and report-start limits protect the server; queue admission can return 503 with Retry-After, and accepted jobs may wait for capacity. Account access, podcast access and the linked source are checked again before generation. If that check fails while the report record still exists, generation fails and refunds one flex credit. Deleting the podcast also deletes its report records; that deletion does not refund a queued report credit, even if generation has not started.
Use the same Idempotency-Key and identical parameters when retrying. Keys are scoped to the account and retained with the API request record, including after the PDF expires. Reusing a key for different parameters returns 409. A new intentional report requires a new key.
POST requests for queued or processing reports return 202. GET always returns 200 for an accessible report, including while pending. Poll GET /podcasts/123/reports?report_id=456 using the returned ID. Replays of terminal reports return 200. A permanently failed generation refunds one flex credit exactly once. This preserves the credit even if the billing period changed during processing.
Report status is one of queued, processing, completed or failed. A failed report returns failure_code: report_generation_failed; internal renderer errors are not exposed. credit_refunded records the API refund (null for reports without an API request receipt). Retry the same key to retrieve a failed request; a new intentional generation needs a new key. An expired PDF may still have status: completed: expired and expires_at describe file availability independently.
All report show_* options default to true except show_geographic_opportunities, which defaults to false. Defaults are fixed by the API and included when normalizing idempotency. Future months/years are rejected. The current month/year is allowed; partial_period indicates that it was unfinished when the report was requested. It does not guarantee that all source observations were available.
Completed documents follow the existing seven-day deletion policy. The response includes expires_at and a download URL when available. Treat that URL as a secret capability and save the PDF on your backend for permanent archives. The legacy download URL returns HTTP 410 when the PDF has expired. JSON clients receive report_expired.
Responses contain error.code, error.message, error.details and meta.request_id. Supply the request ID when asking for support.
HTTP
Code
Meaning
400
invalid_request
The HTTP request could not be processed.
401
unauthenticated
Missing, invalid, expired or revoked key.
403
forbidden
Access was denied by an HTTP access control.
403
api_access_disabled
API access is disabled.
403
insufficient_scope
Add the endpoint's permission to a new key.
403
subscription_required
An active qualifying subscription is required.
403
plan_upgrade_required
Professional or Network is required.
404
not_found
Unknown API route.
405
method_not_allowed
Use the documented HTTP method.
413
request_too_large
Reduce the request body, query string or parameter structure.
404
podcast_not_found
Podcast does not exist or is inaccessible.
404
report_not_found
Report does not exist or is inaccessible.
409
op3_not_connected
Connect OP3 before using download analytics.
409
platform_not_connected
Link the requested ranking platform.
409
data_sync_pending
A known first OP3 synchronization is pending.
409
idempotency_conflict
The key was used for different report parameters.
409
report_generation_in_progress
Wait for the existing report.
410
report_expired
The requested PDF has expired.
422
validation_error
Invalid or unknown parameters.
422
unsupported_combination
Unsupported chart dimensions or resolution.
422
query_too_large
Narrow the range or filters.
429
rate_limit_exceeded
Minute request limit reached.
429
quota_exceeded
Daily data-request allowance exhausted; see details.window and reset_at.
429
report_credits_exhausted
Report credits exhausted; see next_reset_date when available.
503
api_busy
Internal calculation or accounting capacity is occupied.
503
data_temporarily_unavailable
Temporary data access problem.
A valid collection with no reviews or tracked keywords returns an empty list. A configured source without recorded data can return empty or unobserved results. data_sync_pending is used only when existing state identifies an initial synchronization.
Connect to the Podstatus MCP server with OAuth sign-in or the same scoped API keys. OAuth access tokens work only on MCP, not on the REST API.
REST and MCP share account request quotas, cache capacity and report credits.
Every authenticated MCP POST consumes minute capacity. Initialization, discovery, pings and notifications do not consume daily data quota; tool calls consume one shared daily request.
Read the plain-text MCP guide for client setup, errors and tool schemas.
Podstatus uses its own and third party cookies to allow the website function properly, for analytical purposes and to show you personalized advertising based on a profile drawn up from your browsing habits. You can configure or reject all cookies by clicking the "Manage" button. Or you can accept all cookies by pressing the "Accept" button. For further information you can visit our Cookie Policy.
Cookie settings
Our website keeps three levels of cookies. You can adjust your preferences at any time. If you want more information about what cookies are and which cookies we collect, please read our cookie policy.
are essential cookies that ensure that the website functions properly and that your preferences are saved.
allow us to analyse website use and to improve the visitor's experience.
allow us to personalise your experience and to send you relevant content and offers, on this website and other websites.