Skip to documentation
DEVELOPER DOCUMENTATION v1

Podstatus API v1

Your podcasts. Your dashboard.
Rankings, reviews and analytics, ready to build with.

BASE URLhttps://podstatus.com/api/v1

Available on Professional and Network with API access enabled. All examples use fictitious IDs.

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.

curl --fail-with-body \
  -H "Authorization: Bearer $PODSTATUS_API_KEY" \
  -H 'Accept: application/json' \
  'https://podstatus.com/api/v1/podcasts?per_page=25'

Use rankings:read to list supported combinations before requesting a chart.

curl --fail-with-body \
  -H "Authorization: Bearer $PODSTATUS_API_KEY" \
  -H 'Accept: application/json' \
  'https://podstatus.com/api/v1/podcasts/123/rankings/latest?platform=apple&country=ca&category=26'

Example response with fictitious values and abbreviated metadata follows.

{
  "data": [{"podcast_id":123,"platform":"apple","country":"ca","source_country":"ca","category":26,"position":12,"status":"ranked"}],
  "meta": {"snapshot_at":"2026-11-03T11:00:00Z","timezone":"UTC","stale":false,"cache_hit":false,"request_id":"example"}
}

Find your endpoint

All paths below are relative to the base URL. Examples show representative data and abbreviated metadata.

17 endpoints

List podcasts accessible to the account.

Required permission podcasts:read

Parameters

Parameters for GET /podcasts
NameType / locationDetails
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.
Request & response example 200 OK
cURL
curl --fail-with-body \
  -H "Authorization: Bearer $PODSTATUS_API_KEY" \
  -H 'Accept: application/json' \
  'https://podstatus.com/api/v1/podcasts?per_page=25'
JSON response
{
    "data": [
        {
            "id": 123,
            "name": "The Example Podcast",
            "artwork_url": null,
            "apple_id": 123456,
            "spotify_id": null,
            "ivoox_id": null,
            "integrations": {
                "op3": {
                    "connected": true,
                    "sync_status": "completed",
                    "available_from": "2026-10-01",
                    "available_to": "2026-11-02"
                }
            }
        }
    ],
    "meta": {
        "page": 1,
        "per_page": 25,
        "has_more": false,
        "request_id": "example-request-id",
        "usage": {
            "limits": {
                "minute": 20,
                "day": 1000
            },
            "remaining": {
                "minute": 19,
                "day": 999
            },
            "minute_reset_at": "2026-11-03T12:01:00Z",
            "daily_reset_at": "2026-11-04T00:00:00Z"
        }
    }
}

Podcast metadata and connected integrations.

Required permission podcasts:read

Parameters

Parameters for GET /podcasts/{id}
NameType / locationDetails
idRequired integerpath Minimum 1. Podstatus ID from GET /podcasts; this is not the Apple Podcasts ID.
Request & response example 200 OK
cURL
curl --fail-with-body \
  -H "Authorization: Bearer $PODSTATUS_API_KEY" \
  -H 'Accept: application/json' \
  'https://podstatus.com/api/v1/podcasts/123'
JSON response
{
    "data": {
        "id": 123,
        "name": "The Example Podcast",
        "artwork_url": null,
        "apple_id": 123456,
        "spotify_id": null,
        "ivoox_id": null,
        "integrations": {
            "op3": {
                "connected": true,
                "sync_status": "completed",
                "available_from": "2026-10-01",
                "available_to": "2026-11-02"
            }
        }
    },
    "meta": {
        "request_id": "example-request-id",
        "usage": {
            "limits": {
                "minute": 20,
                "day": 1000
            },
            "remaining": {
                "minute": 19,
                "day": 999
            },
            "minute_reset_at": "2026-11-03T12:01:00Z",
            "daily_reset_at": "2026-11-04T00:00:00Z"
        }
    }
}

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
NameType / locationDetails
platformOptional stringquery apple, spotify, ivoox Chart provider. Use ranking-dimensions to find supported combinations.
Request & response example 200 OK
cURL
curl --fail-with-body \
  -H "Authorization: Bearer $PODSTATUS_API_KEY" \
  -H 'Accept: application/json' \
  'https://podstatus.com/api/v1/ranking-dimensions?platform=apple'
JSON response
{
    "data": [
        {
            "platform": "apple",
            "countries": {
                "ca": "Canada"
            },
            "categories": {
                "26": "Podcasts"
            },
            "category_countries": [
                "ca"
            ],
            "country_aliases": {}
        }
    ],
    "meta": {
        "request_id": "example-request-id",
        "usage": {
            "limits": {
                "minute": 20,
                "day": 1000
            },
            "remaining": {
                "minute": 19,
                "day": 999
            },
            "minute_reset_at": "2026-11-03T12:01:00Z",
            "daily_reset_at": "2026-11-04T00:00:00Z"
        }
    }
}

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
NameType / locationDetails
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.
Request & response example 200 OK
cURL
curl --fail-with-body \
  -H "Authorization: Bearer $PODSTATUS_API_KEY" \
  -H 'Accept: application/json' \
  'https://podstatus.com/api/v1/podcasts/123/rankings/latest?platform=apple&country=ca&category=26'
JSON response
{
    "data": [
        {
            "podcast_id": 123,
            "platform": "apple",
            "country": "ca",
            "source_country": "ca",
            "category": 26,
            "position": 12,
            "status": "ranked"
        }
    ],
    "meta": {
        "snapshot_at": "2026-11-03T11:00:00Z",
        "timezone": "UTC",
        "request_id": "example-request-id",
        "usage": {
            "limits": {
                "minute": 20,
                "day": 1000
            },
            "remaining": {
                "minute": 19,
                "day": 999
            },
            "minute_reset_at": "2026-11-03T12:01:00Z",
            "daily_reset_at": "2026-11-04T00:00:00Z"
        }
    }
}
iVoox daily chart shared with Spain Fictitious example
{
    "data": [
        {
            "podcast_id": 123,
            "platform": "ivoox",
            "country": "cl",
            "source_country": "es",
            "category": 31,
            "position": 12,
            "status": "ranked"
        }
    ],
    "meta": {
        "snapshot_date": "2026-11-02",
        "timezone": "UTC",
        "request_id": "example-request-id",
        "usage": {
            "limits": {
                "minute": 20,
                "day": 1000
            },
            "remaining": {
                "minute": 19,
                "day": 999
            },
            "minute_reset_at": "2026-11-03T12:01:00Z",
            "daily_reset_at": "2026-11-04T00:00:00Z"
        }
    }
}

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
NameType / locationDetails
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.
Request & response example 200 OK
cURL
curl --fail-with-body \
  -H "Authorization: Bearer $PODSTATUS_API_KEY" \
  -H 'Accept: application/json' \
  'https://podstatus.com/api/v1/podcasts/123/rankings/overview'
JSON response
{
    "data": [
        {
            "podcast_id": 123,
            "platform": "apple",
            "country": "ca",
            "source_country": "ca",
            "category": 26,
            "position": 12,
            "status": "ranked"
        }
    ],
    "meta": {
        "snapshots": [
            {
                "platform": "apple",
                "snapshot_at": "2026-11-03T11:00:00Z",
                "timezone": "UTC"
            }
        ],
        "timezone": "UTC",
        "coverage": "observed_rankings_only",
        "request_id": "example-request-id",
        "usage": {
            "limits": {
                "minute": 20,
                "day": 1000
            },
            "remaining": {
                "minute": 19,
                "day": 999
            },
            "minute_reset_at": "2026-11-03T12:01:00Z",
            "daily_reset_at": "2026-11-04T00:00:00Z"
        }
    }
}
iVoox daily chart shared with Spain Fictitious example
{
    "data": [
        {
            "podcast_id": 123,
            "platform": "ivoox",
            "country": "cl",
            "source_country": "es",
            "category": 31,
            "position": 12,
            "status": "ranked"
        },
        {
            "podcast_id": 123,
            "platform": "ivoox",
            "country": "pe",
            "source_country": "es",
            "category": 31,
            "position": 12,
            "status": "ranked"
        },
        {
            "podcast_id": 123,
            "platform": "ivoox",
            "country": "es",
            "source_country": "es",
            "category": 31,
            "position": 12,
            "status": "ranked"
        }
    ],
    "meta": {
        "snapshots": [
            {
                "platform": "ivoox",
                "snapshot_date": "2026-11-02",
                "timezone": "UTC"
            }
        ],
        "timezone": "UTC",
        "coverage": "observed_rankings_only",
        "request_id": "example-request-id",
        "usage": {
            "limits": {
                "minute": 20,
                "day": 1000
            },
            "remaining": {
                "minute": 19,
                "day": 999
            },
            "minute_reset_at": "2026-11-03T12:01:00Z",
            "daily_reset_at": "2026-11-04T00:00:00Z"
        }
    }
}

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
NameType / locationDetails
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.
Request & response example 200 OK
cURL
curl --fail-with-body \
  -H "Authorization: Bearer $PODSTATUS_API_KEY" \
  -H 'Accept: application/json' \
  'https://podstatus.com/api/v1/podcasts/123/rankings/history?platform=apple&country=ca&category=26&from=2026-11-01&to=2026-11-02'
JSON response
{
    "data": [
        {
            "date": "2026-11-01",
            "position": 15,
            "status": "ranked"
        },
        {
            "date": "2026-11-02",
            "position": 12,
            "status": "ranked"
        }
    ],
    "meta": {
        "platform": "apple",
        "country": "ca",
        "source_country": "ca",
        "category": 26,
        "from": "2026-11-01",
        "to": "2026-11-02",
        "resolution": "day",
        "aggregation": "best_observed_rank",
        "timezone": "UTC",
        "request_id": "example-request-id",
        "usage": {
            "limits": {
                "minute": 20,
                "day": 1000
            },
            "remaining": {
                "minute": 19,
                "day": 999
            },
            "minute_reset_at": "2026-11-03T12:01:00Z",
            "daily_reset_at": "2026-11-04T00:00:00Z"
        }
    }
}
iVoox daily chart shared with Spain Fictitious example
{
    "data": [
        {
            "date": "2026-11-01",
            "position": 15,
            "status": "ranked"
        },
        {
            "date": "2026-11-02",
            "position": 12,
            "status": "ranked"
        }
    ],
    "meta": {
        "platform": "ivoox",
        "country": "cl",
        "source_country": "es",
        "category": 31,
        "from": "2026-11-01",
        "to": "2026-11-02",
        "resolution": "day",
        "aggregation": "stored_snapshot",
        "timezone": "UTC",
        "request_id": "example-request-id",
        "usage": {
            "limits": {
                "minute": 20,
                "day": 1000
            },
            "remaining": {
                "minute": 19,
                "day": 999
            },
            "minute_reset_at": "2026-11-03T12:01:00Z",
            "daily_reset_at": "2026-11-04T00:00:00Z"
        }
    }
}
Hourly data with an unobserved boundary Fictitious example
{
    "data": [
        {
            "timestamp": "2026-11-02T10:00:00Z",
            "position": 12,
            "status": "ranked"
        },
        {
            "timestamp": "2026-11-02T11:00:00Z",
            "position": null,
            "status": "not_observed"
        }
    ],
    "meta": {
        "platform": "apple",
        "country": "ca",
        "source_country": "ca",
        "category": 26,
        "from": "2026-11-02T10:00:00Z",
        "to": "2026-11-02T11:00:00Z",
        "resolution": "hour",
        "aggregation": "stored_snapshot",
        "timezone": "UTC",
        "request_id": "example-request-id",
        "usage": {
            "limits": {
                "minute": 20,
                "day": 1000
            },
            "remaining": {
                "minute": 19,
                "day": 999
            },
            "minute_reset_at": "2026-11-03T12:01:00Z",
            "daily_reset_at": "2026-11-04T00:00:00Z"
        }
    }
}

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
NameType / locationDetails
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.
Request & response example 200 OK
cURL
curl --fail-with-body \
  -H "Authorization: Bearer $PODSTATUS_API_KEY" \
  -H 'Accept: application/json' \
  'https://podstatus.com/api/v1/podcasts/123/rankings/summary?platform=apple&country=ca&category=26&from=2026-11-01&to=2026-11-02'
JSON response
{
    "data": [
        {
            "period": "period",
            "best_observed_rank": 12,
            "best_date": "2026-11-02",
            "start_position": 15,
            "end_position": 12,
            "change": 3,
            "observations": 2,
            "first_available": "2026-11-01",
            "last_available": "2026-11-02"
        }
    ],
    "meta": {
        "platform": "apple",
        "country": "ca",
        "source_country": "ca",
        "category": 26,
        "from": "2026-11-01",
        "to": "2026-11-02",
        "resolution": "day",
        "aggregation": "best_observed_rank",
        "timezone": "UTC",
        "change_definition": "start_position minus end_position; positive means improvement; null if either boundary is missing",
        "request_id": "example-request-id",
        "usage": {
            "limits": {
                "minute": 20,
                "day": 1000
            },
            "remaining": {
                "minute": 19,
                "day": 999
            },
            "minute_reset_at": "2026-11-03T12:01:00Z",
            "daily_reset_at": "2026-11-04T00:00:00Z"
        }
    }
}
iVoox daily chart shared with Spain Fictitious example
{
    "data": [
        {
            "period": "period",
            "best_observed_rank": 12,
            "best_date": "2026-11-02",
            "start_position": 15,
            "end_position": 12,
            "change": 3,
            "observations": 2,
            "first_available": "2026-11-01",
            "last_available": "2026-11-02"
        }
    ],
    "meta": {
        "platform": "ivoox",
        "country": "cl",
        "source_country": "es",
        "category": 31,
        "from": "2026-11-01",
        "to": "2026-11-02",
        "resolution": "day",
        "aggregation": "stored_snapshot",
        "timezone": "UTC",
        "change_definition": "start_position minus end_position; positive means improvement; null if either boundary is missing",
        "request_id": "example-request-id",
        "usage": {
            "limits": {
                "minute": 20,
                "day": 1000
            },
            "remaining": {
                "minute": 19,
                "day": 999
            },
            "minute_reset_at": "2026-11-03T12:01:00Z",
            "daily_reset_at": "2026-11-04T00:00:00Z"
        }
    }
}
Hourly data with an unobserved boundary Fictitious example
{
    "data": [
        {
            "period": "period",
            "best_observed_rank": 12,
            "best_date": "2026-11-02T10:00:00Z",
            "start_position": 12,
            "end_position": null,
            "change": null,
            "observations": 1,
            "first_available": "2026-11-02T10:00:00Z",
            "last_available": "2026-11-02T10:00:00Z"
        }
    ],
    "meta": {
        "platform": "apple",
        "country": "ca",
        "source_country": "ca",
        "category": 26,
        "from": "2026-11-02T10:00:00Z",
        "to": "2026-11-02T11:00:00Z",
        "resolution": "hour",
        "aggregation": "stored_snapshot",
        "timezone": "UTC",
        "change_definition": "start_position minus end_position; positive means improvement; null if either boundary is missing",
        "request_id": "example-request-id",
        "usage": {
            "limits": {
                "minute": 20,
                "day": 1000
            },
            "remaining": {
                "minute": 19,
                "day": 999
            },
            "minute_reset_at": "2026-11-03T12:01:00Z",
            "daily_reset_at": "2026-11-04T00:00:00Z"
        }
    }
}

Latest rankings for up to five accessible podcasts in one platform and country.

Required permission rankings:read

Parameters

Parameters for GET /rankings/latest
NameType / locationDetails
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.
podcast_idsRequired arrayquery At least 1 item. Up to 5 items, separated by commas.
Request & response example 200 OK
cURL
curl --fail-with-body \
  -H "Authorization: Bearer $PODSTATUS_API_KEY" \
  -H 'Accept: application/json' \
  'https://podstatus.com/api/v1/rankings/latest?platform=apple&country=ca&category=26&podcast_ids=123%2C124'
JSON response
{
    "data": [
        {
            "podcast_id": 123,
            "data": [
                {
                    "podcast_id": 123,
                    "platform": "apple",
                    "country": "ca",
                    "source_country": "ca",
                    "category": 26,
                    "position": 12,
                    "status": "ranked"
                }
            ],
            "meta": {
                "snapshot_at": "2026-11-03T11:00:00Z",
                "timezone": "UTC"
            }
        },
        {
            "podcast_id": 124,
            "data": [
                {
                    "podcast_id": 124,
                    "platform": "apple",
                    "country": "ca",
                    "source_country": "ca",
                    "category": 26,
                    "position": null,
                    "status": "not_observed"
                }
            ],
            "meta": {
                "snapshot_at": "2026-11-03T11:00:00Z",
                "timezone": "UTC"
            }
        }
    ],
    "meta": {
        "request_id": "example-request-id",
        "usage": {
            "limits": {
                "minute": 20,
                "day": 1000
            },
            "remaining": {
                "minute": 19,
                "day": 999
            },
            "minute_reset_at": "2026-11-03T12:01:00Z",
            "daily_reset_at": "2026-11-04T00:00:00Z"
        }
    }
}
iVoox daily chart shared with Spain Fictitious example
{
    "data": [
        {
            "podcast_id": 123,
            "data": [
                {
                    "podcast_id": 123,
                    "platform": "ivoox",
                    "country": "cl",
                    "source_country": "es",
                    "category": 31,
                    "position": 12,
                    "status": "ranked"
                }
            ],
            "meta": {
                "snapshot_date": "2026-11-02",
                "timezone": "UTC"
            }
        }
    ],
    "meta": {
        "request_id": "example-request-id",
        "usage": {
            "limits": {
                "minute": 20,
                "day": 1000
            },
            "remaining": {
                "minute": 19,
                "day": 999
            },
            "minute_reset_at": "2026-11-03T12:01:00Z",
            "daily_reset_at": "2026-11-04T00:00:00Z"
        }
    }
}

OP3 downloads by UTC day or month. Null indicates an unobserved day. Requires OP3.

Required permission analytics:read

Parameters

Parameters for GET /podcasts/{id}/downloads
NameType / locationDetails
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.
group_byOptional stringquery day, month Aggregation of the requested interval; monthly groups may cover partial months.
Request & response example 200 OK
cURL
curl --fail-with-body \
  -H "Authorization: Bearer $PODSTATUS_API_KEY" \
  -H 'Accept: application/json' \
  'https://podstatus.com/api/v1/podcasts/123/downloads?from=2026-11-01&to=2026-11-02'
JSON response
{
    "data": [
        {
            "date": "2026-11-01",
            "value": 240,
            "status": "available"
        },
        {
            "date": "2026-11-02",
            "value": null,
            "status": "not_observed"
        }
    ],
    "meta": {
        "source": "op3",
        "metric": "downloads",
        "from": "2026-11-01",
        "to": "2026-11-02",
        "latest_available_date": "2026-11-01",
        "observed_total": 240,
        "available_days": 1,
        "timezone": "UTC",
        "request_id": "example-request-id",
        "usage": {
            "limits": {
                "minute": 20,
                "day": 1000
            },
            "remaining": {
                "minute": 19,
                "day": 999
            },
            "minute_reset_at": "2026-11-03T12:01:00Z",
            "daily_reset_at": "2026-11-04T00:00:00Z"
        }
    }
}
Monthly group: available Fictitious example
{
    "data": [
        {
            "date": "2026-11",
            "value": 240,
            "status": "available",
            "from": "2026-11-01",
            "to": "2026-11-02",
            "requested_days": 2,
            "available_days": 2
        }
    ],
    "meta": {
        "source": "op3",
        "metric": "downloads",
        "from": "2026-11-01",
        "to": "2026-11-02",
        "latest_available_date": "2026-11-02",
        "observed_total": 240,
        "available_days": 2,
        "timezone": "UTC",
        "request_id": "example-request-id",
        "usage": {
            "limits": {
                "minute": 20,
                "day": 1000
            },
            "remaining": {
                "minute": 19,
                "day": 999
            },
            "minute_reset_at": "2026-11-03T12:01:00Z",
            "daily_reset_at": "2026-11-04T00:00:00Z"
        }
    }
}
Monthly group: partial Fictitious example
{
    "data": [
        {
            "date": "2026-11",
            "value": 240,
            "status": "partial",
            "from": "2026-11-01",
            "to": "2026-11-02",
            "requested_days": 2,
            "available_days": 1
        }
    ],
    "meta": {
        "source": "op3",
        "metric": "downloads",
        "from": "2026-11-01",
        "to": "2026-11-02",
        "latest_available_date": "2026-11-01",
        "observed_total": 240,
        "available_days": 1,
        "timezone": "UTC",
        "request_id": "example-request-id",
        "usage": {
            "limits": {
                "minute": 20,
                "day": 1000
            },
            "remaining": {
                "minute": 19,
                "day": 999
            },
            "minute_reset_at": "2026-11-03T12:01:00Z",
            "daily_reset_at": "2026-11-04T00:00:00Z"
        }
    }
}
Monthly group: not_observed Fictitious example
{
    "data": [
        {
            "date": "2026-11",
            "value": null,
            "status": "not_observed",
            "from": "2026-11-01",
            "to": "2026-11-02",
            "requested_days": 2,
            "available_days": 0
        }
    ],
    "meta": {
        "source": "op3",
        "metric": "downloads",
        "from": "2026-11-01",
        "to": "2026-11-02",
        "latest_available_date": "2026-10-31",
        "observed_total": null,
        "available_days": 0,
        "timezone": "UTC",
        "request_id": "example-request-id",
        "usage": {
            "limits": {
                "minute": 20,
                "day": 1000
            },
            "remaining": {
                "minute": 19,
                "day": 999
            },
            "minute_reset_at": "2026-11-03T12:01:00Z",
            "daily_reset_at": "2026-11-04T00:00:00Z"
        }
    }
}

Distribution of recorded downloads, including unknown values. Percentages use the returned denominator. Requires OP3.

Required permission analytics:read

Parameters

Parameters for GET /podcasts/{id}/audience
NameType / locationDetails
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.
dimensionRequired stringquery country, region, device, app Group recorded downloads by this audience dimension.
Request & response example 200 OK
cURL
curl --fail-with-body \
  -H "Authorization: Bearer $PODSTATUS_API_KEY" \
  -H 'Accept: application/json' \
  'https://podstatus.com/api/v1/podcasts/123/audience?from=2026-11-01&to=2026-11-02&dimension=country'
JSON response
{
    "data": [
        {
            "country_code": "CA",
            "downloads": 240,
            "percentage": 100
        }
    ],
    "meta": {
        "source": "op3",
        "metric": "downloads",
        "dimension": "country",
        "denominator": 240,
        "denominator_definition": "sum of downloads recorded for this dimension",
        "from": "2026-11-01",
        "to": "2026-11-02",
        "truncated": false,
        "timezone": "UTC",
        "request_id": "example-request-id",
        "usage": {
            "limits": {
                "minute": 20,
                "day": 1000
            },
            "remaining": {
                "minute": 19,
                "day": 999
            },
            "minute_reset_at": "2026-11-03T12:01:00Z",
            "daily_reset_at": "2026-11-04T00:00:00Z"
        }
    }
}
Distribution by region Fictitious example
{
    "data": [
        {
            "country_code": "CA",
            "region_name": "Ontario",
            "downloads": 240,
            "percentage": 100
        }
    ],
    "meta": {
        "source": "op3",
        "metric": "downloads",
        "dimension": "region",
        "denominator": 240,
        "denominator_definition": "sum of downloads recorded for this dimension",
        "from": "2026-11-01",
        "to": "2026-11-02",
        "truncated": false,
        "timezone": "UTC",
        "request_id": "example-request-id",
        "usage": {
            "limits": {
                "minute": 20,
                "day": 1000
            },
            "remaining": {
                "minute": 19,
                "day": 999
            },
            "minute_reset_at": "2026-11-03T12:01:00Z",
            "daily_reset_at": "2026-11-04T00:00:00Z"
        }
    }
}
Distribution by device Fictitious example
{
    "data": [
        {
            "device_type": "mobile",
            "downloads": 240,
            "percentage": 100
        }
    ],
    "meta": {
        "source": "op3",
        "metric": "downloads",
        "dimension": "device",
        "denominator": 240,
        "denominator_definition": "sum of downloads recorded for this dimension",
        "from": "2026-11-01",
        "to": "2026-11-02",
        "truncated": false,
        "timezone": "UTC",
        "request_id": "example-request-id",
        "usage": {
            "limits": {
                "minute": 20,
                "day": 1000
            },
            "remaining": {
                "minute": 19,
                "day": 999
            },
            "minute_reset_at": "2026-11-03T12:01:00Z",
            "daily_reset_at": "2026-11-04T00:00:00Z"
        }
    }
}
Distribution by app Fictitious example
{
    "data": [
        {
            "agent_name": "unknown",
            "downloads": 240,
            "percentage": 100
        }
    ],
    "meta": {
        "source": "op3",
        "metric": "downloads",
        "dimension": "app",
        "denominator": 240,
        "denominator_definition": "sum of downloads recorded for this dimension",
        "from": "2026-11-01",
        "to": "2026-11-02",
        "truncated": false,
        "timezone": "UTC",
        "request_id": "example-request-id",
        "usage": {
            "limits": {
                "minute": 20,
                "day": 1000
            },
            "remaining": {
                "minute": 19,
                "day": 999
            },
            "minute_reset_at": "2026-11-03T12:01:00Z",
            "daily_reset_at": "2026-11-04T00:00:00Z"
        }
    }
}

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
NameType / locationDetails
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.
Request & response example 200 OK
cURL
curl --fail-with-body \
  -H "Authorization: Bearer $PODSTATUS_API_KEY" \
  -H 'Accept: application/json' \
  'https://podstatus.com/api/v1/podcasts/123/episodes?from=2026-11-01&to=2026-11-02'
JSON response
{
    "data": [
        {
            "id": 789,
            "title": "An example episode",
            "published_at": "2026-11-01T08:00:00Z",
            "audio_url": "https://example.com/episode.mp3",
            "downloads": 240
        }
    ],
    "meta": {
        "page": 1,
        "per_page": 25,
        "has_more": false,
        "downloads_available": true,
        "from": "2026-11-01",
        "to": "2026-11-02",
        "episode_matching": "exact audio URL; unmatched episodes have null downloads",
        "request_id": "example-request-id",
        "usage": {
            "limits": {
                "minute": 20,
                "day": 1000
            },
            "remaining": {
                "minute": 19,
                "day": 999
            },
            "minute_reset_at": "2026-11-03T12:01:00Z",
            "daily_reset_at": "2026-11-04T00:00:00Z"
        }
    }
}

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
NameType / locationDetails
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.
Request & response example 200 OK
cURL
curl --fail-with-body \
  -H "Authorization: Bearer $PODSTATUS_API_KEY" \
  -H 'Accept: application/json' \
  'https://podstatus.com/api/v1/podcasts/123/reviews?from=2026-11-01&to=2026-11-02'
JSON response
{
    "data": [
        {
            "id": 321,
            "provider": "apple",
            "provider_id": 0,
            "country_code": "ca",
            "rating": 5,
            "title": "A helpful show",
            "content": "I enjoyed this episode.",
            "author": "Example listener",
            "date": "2026-11-01T12:00:00Z"
        }
    ],
    "meta": {
        "page": 1,
        "per_page": 25,
        "has_more": false,
        "request_id": "example-request-id",
        "usage": {
            "limits": {
                "minute": 20,
                "day": 1000
            },
            "remaining": {
                "minute": 19,
                "day": 999
            },
            "minute_reset_at": "2026-11-03T12:01:00Z",
            "daily_reset_at": "2026-11-04T00:00:00Z"
        }
    }
}

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
NameType / locationDetails
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.
Request & response example 200 OK
cURL
curl --fail-with-body \
  -H "Authorization: Bearer $PODSTATUS_API_KEY" \
  -H 'Accept: application/json' \
  'https://podstatus.com/api/v1/podcasts/123/reviews/summary'
JSON response
{
    "data": {
        "total_reviews": 3,
        "rated_reviews": 2,
        "average_rating": 4.5,
        "rating_counts": {
            "1": 0,
            "2": 0,
            "3": 0,
            "4": 1,
            "5": 1
        },
        "by_provider": [
            {
                "provider": "apple",
                "provider_id": 0,
                "total_reviews": 3,
                "rated_reviews": 2,
                "average_rating": 4.5,
                "rating_counts": {
                    "1": 0,
                    "2": 0,
                    "3": 0,
                    "4": 1,
                    "5": 1
                }
            }
        ]
    },
    "meta": {
        "from": null,
        "to": null,
        "coverage": "stored_reviews",
        "timezone": "UTC",
        "request_id": "example-request-id",
        "usage": {
            "limits": {
                "minute": 20,
                "day": 1000
            },
            "remaining": {
                "minute": 19,
                "day": 999
            },
            "minute_reset_at": "2026-11-03T12:01:00Z",
            "daily_reset_at": "2026-11-04T00:00:00Z"
        }
    }
}

List keywords the account tracks for this podcast, including those without ranking observations. Follow pagination to see all tracked keywords.

Required permission keywords:read

Parameters

Parameters for GET /podcasts/{id}/keywords
NameType / locationDetails
idRequired integerpath Minimum 1. Podstatus ID from GET /podcasts; this is not the Apple Podcasts ID.
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.
Request & response example 200 OK
cURL
curl --fail-with-body \
  -H "Authorization: Bearer $PODSTATUS_API_KEY" \
  -H 'Accept: application/json' \
  'https://podstatus.com/api/v1/podcasts/123/keywords?per_page=25'
JSON response
{
    "data": [
        {
            "keyword_id": 42,
            "keyword": "podcast example"
        }
    ],
    "meta": {
        "page": 1,
        "per_page": 25,
        "has_more": false,
        "request_id": "example-request-id",
        "usage": {
            "limits": {
                "minute": 20,
                "day": 1000
            },
            "remaining": {
                "minute": 19,
                "day": 999
            },
            "minute_reset_at": "2026-11-03T12:01:00Z",
            "daily_reset_at": "2026-11-04T00:00:00Z"
        }
    }
}

Only keywords tracked by this account for this podcast. Paginates individual dated observations.

Required permission keywords:read

Parameters

Parameters for GET /podcasts/{id}/keyword-rankings
NameType / locationDetails
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.
platformOptional stringquery apple, spotify Chart provider. Use ranking-dimensions to find supported combinations.
countryOptional stringquery Maximum 2 characters. Two-letter country code, case-insensitive. Chart support varies by platform.
Request & response example 200 OK
cURL
curl --fail-with-body \
  -H "Authorization: Bearer $PODSTATUS_API_KEY" \
  -H 'Accept: application/json' \
  'https://podstatus.com/api/v1/podcasts/123/keyword-rankings?from=2026-11-01&to=2026-11-02'
JSON response
{
    "data": [
        {
            "keyword": "podcast example",
            "keyword_id": 42,
            "platform": "apple",
            "country": "ca",
            "date": "2026-11-01",
            "position": 3
        }
    ],
    "meta": {
        "page": 1,
        "per_page": 25,
        "has_more": false,
        "request_id": "example-request-id",
        "usage": {
            "limits": {
                "minute": 20,
                "day": 1000
            },
            "remaining": {
                "minute": 19,
                "day": 999
            },
            "minute_reset_at": "2026-11-03T12:01:00Z",
            "daily_reset_at": "2026-11-04T00:00:00Z"
        }
    }
}

List existing reports, optionally filtered by report_id. Download links are bearer links and expire with the document.

Required permission reports:read

Parameters

Parameters for GET /podcasts/{id}/reports
NameType / locationDetails
idRequired integerpath Minimum 1. Podstatus ID from GET /podcasts; this is not the Apple Podcasts ID.
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.
report_idOptional integerquery Minimum 1. Only return this report, if it belongs to the selected podcast.
Request & response example 200 OK
cURL
curl --fail-with-body \
  -H "Authorization: Bearer $PODSTATUS_API_KEY" \
  -H 'Accept: application/json' \
  'https://podstatus.com/api/v1/podcasts/123/reports?per_page=25'
JSON response
{
    "data": [
        {
            "id": 456,
            "podcast_id": 123,
            "status": "queued",
            "period_type": "monthly",
            "period": "2026-10",
            "expires_at": null,
            "expired": false,
            "download_url": null,
            "partial_period": false,
            "failure_code": null,
            "credit_refunded": false
        }
    ],
    "meta": {
        "page": 1,
        "per_page": 25,
        "has_more": false,
        "request_id": "example-request-id",
        "usage": {
            "limits": {
                "minute": 20,
                "day": 1000
            },
            "remaining": {
                "minute": 19,
                "day": 999
            },
            "minute_reset_at": "2026-11-03T12:01:00Z",
            "daily_reset_at": "2026-11-04T00:00:00Z"
        }
    }
}
Report processing (fictitious download link) Fictitious example
{
    "data": [
        {
            "id": 456,
            "podcast_id": 123,
            "status": "processing",
            "period_type": "monthly",
            "period": "2026-10",
            "expires_at": null,
            "expired": false,
            "download_url": null,
            "partial_period": false,
            "failure_code": null,
            "credit_refunded": false
        }
    ],
    "meta": {
        "page": 1,
        "per_page": 25,
        "has_more": false,
        "request_id": "example-request-id",
        "usage": {
            "limits": {
                "minute": 20,
                "day": 1000
            },
            "remaining": {
                "minute": 19,
                "day": 999
            },
            "minute_reset_at": "2026-11-03T12:01:00Z",
            "daily_reset_at": "2026-11-04T00:00:00Z"
        }
    }
}
Report completed (fictitious download link) Fictitious example
{
    "data": [
        {
            "id": 456,
            "podcast_id": 123,
            "status": "completed",
            "period_type": "monthly",
            "period": "2026-10",
            "expires_at": "2026-11-10T12:00:00Z",
            "expired": false,
            "download_url": "https://example.com/private-report",
            "partial_period": false,
            "failure_code": null,
            "credit_refunded": false
        }
    ],
    "meta": {
        "page": 1,
        "per_page": 25,
        "has_more": false,
        "request_id": "example-request-id",
        "usage": {
            "limits": {
                "minute": 20,
                "day": 1000
            },
            "remaining": {
                "minute": 19,
                "day": 999
            },
            "minute_reset_at": "2026-11-03T12:01:00Z",
            "daily_reset_at": "2026-11-04T00:00:00Z"
        }
    }
}
Report failed (fictitious download link) Fictitious example
{
    "data": [
        {
            "id": 456,
            "podcast_id": 123,
            "status": "failed",
            "period_type": "monthly",
            "period": "2026-10",
            "expires_at": null,
            "expired": false,
            "download_url": null,
            "partial_period": false,
            "failure_code": "report_generation_failed",
            "credit_refunded": true
        }
    ],
    "meta": {
        "page": 1,
        "per_page": 25,
        "has_more": false,
        "request_id": "example-request-id",
        "usage": {
            "limits": {
                "minute": 20,
                "day": 1000
            },
            "remaining": {
                "minute": 19,
                "day": 999
            },
            "minute_reset_at": "2026-11-03T12:01:00Z",
            "daily_reset_at": "2026-11-04T00:00:00Z"
        }
    }
}
Report expired (fictitious download link) Fictitious example
{
    "data": [
        {
            "id": 456,
            "podcast_id": 123,
            "status": "completed",
            "period_type": "monthly",
            "period": "2026-10",
            "expires_at": "2026-11-02T12:00:00Z",
            "expired": true,
            "download_url": null,
            "partial_period": false,
            "failure_code": null,
            "credit_refunded": false
        }
    ],
    "meta": {
        "page": 1,
        "per_page": 25,
        "has_more": false,
        "request_id": "example-request-id",
        "usage": {
            "limits": {
                "minute": 20,
                "day": 1000
            },
            "remaining": {
                "minute": 19,
                "day": 999
            },
            "minute_reset_at": "2026-11-03T12:01:00Z",
            "daily_reset_at": "2026-11-04T00:00:00Z"
        }
    }
}

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
NameType / locationDetails
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

NameTypeDetails
period_typeRequiredstringmonthly, yearly Calendar period covered by the report.
periodRequiredstringYYYY-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.
optionsOptionalobjectBoolean flags; omit a flag to use the existing report default.
Available flags
  • show_reviews
  • show_apple_rankings
  • show_spotify_rankings
  • show_ivoox_rankings
  • show_podcast_activity
  • show_competitive_analysis
  • show_seasonality_analysis
  • show_sentiment_analysis
  • show_geographic_opportunities
Request & response example 202 Accepted
cURL
curl --fail-with-body -X POST \
  -H "Authorization: Bearer $PODSTATUS_API_KEY" \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: monthly-report-123-2026-10' \
  --data '{"period_type":"monthly","period":"2026-10","options":{"show_reviews":true}}' \
  'https://podstatus.com/api/v1/podcasts/123/reports'
JSON response
{
    "data": {
        "id": 456,
        "podcast_id": 123,
        "status": "queued",
        "period_type": "monthly",
        "period": "2026-10",
        "expires_at": null,
        "expired": false,
        "download_url": null,
        "partial_period": false,
        "failure_code": null,
        "credit_refunded": false
    },
    "meta": {
        "replayed": false,
        "request_id": "example-request-id",
        "usage": {
            "limits": {
                "minute": 20,
                "day": 1000
            },
            "remaining": {
                "minute": 19,
                "day": 999
            },
            "minute_reset_at": "2026-11-03T12:01:00Z",
            "daily_reset_at": "2026-11-04T00:00:00Z"
        }
    }
}
Report processing (fictitious download link) Fictitious example
{
    "data": {
        "id": 456,
        "podcast_id": 123,
        "status": "processing",
        "period_type": "monthly",
        "period": "2026-10",
        "expires_at": null,
        "expired": false,
        "download_url": null,
        "partial_period": false,
        "failure_code": null,
        "credit_refunded": false
    },
    "meta": {
        "replayed": false,
        "request_id": "example-request-id",
        "usage": {
            "limits": {
                "minute": 20,
                "day": 1000
            },
            "remaining": {
                "minute": 19,
                "day": 999
            },
            "minute_reset_at": "2026-11-03T12:01:00Z",
            "daily_reset_at": "2026-11-04T00:00:00Z"
        }
    }
}
Report completed (fictitious download link) Fictitious example
{
    "data": {
        "id": 456,
        "podcast_id": 123,
        "status": "completed",
        "period_type": "monthly",
        "period": "2026-10",
        "expires_at": "2026-11-10T12:00:00Z",
        "expired": false,
        "download_url": "https://example.com/private-report",
        "partial_period": false,
        "failure_code": null,
        "credit_refunded": false
    },
    "meta": {
        "replayed": false,
        "request_id": "example-request-id",
        "usage": {
            "limits": {
                "minute": 20,
                "day": 1000
            },
            "remaining": {
                "minute": 19,
                "day": 999
            },
            "minute_reset_at": "2026-11-03T12:01:00Z",
            "daily_reset_at": "2026-11-04T00:00:00Z"
        }
    }
}
Report failed (fictitious download link) Fictitious example
{
    "data": {
        "id": 456,
        "podcast_id": 123,
        "status": "failed",
        "period_type": "monthly",
        "period": "2026-10",
        "expires_at": null,
        "expired": false,
        "download_url": null,
        "partial_period": false,
        "failure_code": "report_generation_failed",
        "credit_refunded": true
    },
    "meta": {
        "replayed": false,
        "request_id": "example-request-id",
        "usage": {
            "limits": {
                "minute": 20,
                "day": 1000
            },
            "remaining": {
                "minute": 19,
                "day": 999
            },
            "minute_reset_at": "2026-11-03T12:01:00Z",
            "daily_reset_at": "2026-11-04T00:00:00Z"
        }
    }
}
Report expired (fictitious download link) Fictitious example
{
    "data": {
        "id": 456,
        "podcast_id": 123,
        "status": "completed",
        "period_type": "monthly",
        "period": "2026-10",
        "expires_at": "2026-11-02T12:00:00Z",
        "expired": true,
        "download_url": null,
        "partial_period": false,
        "failure_code": null,
        "credit_refunded": false
    },
    "meta": {
        "replayed": false,
        "request_id": "example-request-id",
        "usage": {
            "limits": {
                "minute": 20,
                "day": 1000
            },
            "remaining": {
                "minute": 19,
                "day": 999
            },
            "minute_reset_at": "2026-11-03T12:01:00Z",
            "daily_reset_at": "2026-11-04T00:00:00Z"
        }
    }
}

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.

Permissions and pagination

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.

Dates and data meaning

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.

Limits, caching and retries

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.

Connect a dashboard

  1. List the account's podcasts and inspect their connected integrations.
  2. Fetch valid chart dimensions, then request latest rankings, history and summary for the selected chart.
  3. For OP3-enabled podcasts, request downloads, country distribution, device distribution and episodes.
  4. Display unavailable metrics explicitly and preserve source dates.
  5. Refresh sequentially with bounded retries and reuse your backend's saved results between visits.
  6. 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

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.

curl --fail-with-body -X POST \
  -H "Authorization: Bearer $PODSTATUS_API_KEY" \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: monthly-report-123-2026-10' \
  --data '{"period_type":"monthly","period":"2026-10","options":{"show_reviews":true}}' \
  'https://podstatus.com/api/v1/podcasts/123/reports'

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.

Errors

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.

MCP and assistant plugins

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.

Need help? Include meta.request_id when contacting hello@podstatus.com.