# Podstatus API v1

Connect your own dashboard or automation to the podcasts followed by your Podstatus account. Professional and Network include the same endpoints. API activation is required for each account. Documentation is public.

## Start with a server-side API key

If API access has not been enabled for your Professional or Network account, contact [Podstatus support](mailto:podstatus@tenorsoftware.com) to request activation. Then open [API keys](/settings/api), 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.

```sh
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.

```sh
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.

```json
{
  "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"}
}
```

## Server-side JavaScript

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.

```javascript
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;
```

## Python

This example uses the Python standard library.

```python
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
```

## 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.

```sh
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](/docs/mcp) 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](/docs/mcp.md) for client setup, errors and tool schemas.

## Endpoint reference

The reference below is generated from the same operation catalog and parameter rules used by the application. Download the [OpenAPI contract](/docs/api/openapi.json) for the structured schemas.


## GET /api/v1/podcasts

List podcasts accessible to the account.

Required permission `podcasts:read`.

| Parameter | Location | Required | Definition |
|---|---|---|---|
| `page` | query | No | `{"type":"integer","description":"Page number. Default: 1. The offset (page - 1) \u00d7 per_page must not exceed 10000.","minimum":1,"maximum":10000}` |
| `per_page` | query | No | `{"type":"integer","description":"Items per page. Default: 25.","minimum":1,"maximum":50}` |

Example request (fictitious IDs)

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

Example response (HTTP 200): Default response (fictitious values)

```json
{
    "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"
        }
    }
}
```

Successful response schema

```json
{
    "type": "object",
    "required": [
        "data",
        "meta"
    ],
    "properties": {
        "data": {
            "type": "array",
            "items": {
                "type": "object",
                "required": [
                    "id",
                    "name",
                    "artwork_url",
                    "apple_id",
                    "spotify_id",
                    "ivoox_id",
                    "integrations"
                ],
                "properties": {
                    "id": {
                        "type": "integer"
                    },
                    "name": {
                        "type": "string",
                        "nullable": true
                    },
                    "artwork_url": {
                        "type": "string",
                        "nullable": true
                    },
                    "apple_id": {
                        "type": "integer",
                        "nullable": true
                    },
                    "spotify_id": {
                        "type": "string",
                        "nullable": true
                    },
                    "ivoox_id": {
                        "type": "integer",
                        "nullable": true
                    },
                    "integrations": {
                        "type": "object",
                        "required": [
                            "op3"
                        ],
                        "properties": {
                            "op3": {
                                "type": "object",
                                "required": [
                                    "connected",
                                    "sync_status",
                                    "available_from",
                                    "available_to"
                                ],
                                "properties": {
                                    "connected": {
                                        "type": "boolean"
                                    },
                                    "sync_status": {
                                        "type": "string",
                                        "nullable": true,
                                        "enum": [
                                            null,
                                            "pending",
                                            "in_progress",
                                            "completed",
                                            "failed"
                                        ]
                                    },
                                    "available_from": {
                                        "type": "string",
                                        "nullable": true,
                                        "format": "date"
                                    },
                                    "available_to": {
                                        "type": "string",
                                        "nullable": true,
                                        "format": "date"
                                    }
                                },
                                "additionalProperties": true
                            }
                        },
                        "additionalProperties": true
                    }
                },
                "additionalProperties": true
            }
        },
        "meta": {
            "type": "object",
            "required": [
                "page",
                "per_page",
                "has_more"
            ],
            "properties": {
                "page": {
                    "type": "integer"
                },
                "per_page": {
                    "type": "integer"
                },
                "has_more": {
                    "type": "boolean"
                },
                "request_id": {
                    "type": "string",
                    "description": "REST diagnostic ID; MCP exposes this only in the X-Request-Id HTTP header."
                },
                "usage": {
                    "type": "object",
                    "required": [
                        "remaining",
                        "limits",
                        "minute_reset_at",
                        "daily_reset_at"
                    ],
                    "properties": {
                        "remaining": {
                            "type": "object",
                            "required": [
                                "minute",
                                "day"
                            ],
                            "properties": {
                                "minute": {
                                    "type": "integer",
                                    "minimum": 0
                                },
                                "day": {
                                    "type": "integer",
                                    "minimum": 0
                                }
                            },
                            "additionalProperties": true
                        },
                        "limits": {
                            "type": "object",
                            "required": [
                                "minute",
                                "day"
                            ],
                            "properties": {
                                "minute": {
                                    "type": "integer",
                                    "minimum": 0
                                },
                                "day": {
                                    "type": "integer",
                                    "minimum": 0
                                }
                            },
                            "additionalProperties": true
                        },
                        "minute_reset_at": {
                            "type": "string",
                            "format": "date-time"
                        },
                        "daily_reset_at": {
                            "type": "string",
                            "format": "date-time"
                        }
                    },
                    "additionalProperties": true
                },
                "cache_hit": {
                    "type": "boolean"
                },
                "stale": {
                    "type": "boolean"
                }
            },
            "additionalProperties": true
        }
    }
}
```


## GET /api/v1/podcasts/{id}

Podcast metadata and connected integrations.

Required permission `podcasts:read`.

| Parameter | Location | Required | Definition |
|---|---|---|---|
| `id` | path | Yes | `{"type":"integer","minimum":1,"description":"Podstatus ID from GET /podcasts; this is not the Apple Podcasts ID."}` |

Example request (fictitious IDs)

```sh
curl --fail-with-body \
  -H "Authorization: Bearer $PODSTATUS_API_KEY" \
  -H 'Accept: application/json' \
  'https://podstatus.com/api/v1/podcasts/123'
```

Example response (HTTP 200): Default response (fictitious values)

```json
{
    "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"
        }
    }
}
```

Successful response schema

```json
{
    "type": "object",
    "required": [
        "data",
        "meta"
    ],
    "properties": {
        "data": {
            "type": "object",
            "required": [
                "id",
                "name",
                "artwork_url",
                "apple_id",
                "spotify_id",
                "ivoox_id",
                "integrations"
            ],
            "properties": {
                "id": {
                    "type": "integer"
                },
                "name": {
                    "type": "string",
                    "nullable": true
                },
                "artwork_url": {
                    "type": "string",
                    "nullable": true
                },
                "apple_id": {
                    "type": "integer",
                    "nullable": true
                },
                "spotify_id": {
                    "type": "string",
                    "nullable": true
                },
                "ivoox_id": {
                    "type": "integer",
                    "nullable": true
                },
                "integrations": {
                    "type": "object",
                    "required": [
                        "op3"
                    ],
                    "properties": {
                        "op3": {
                            "type": "object",
                            "required": [
                                "connected",
                                "sync_status",
                                "available_from",
                                "available_to"
                            ],
                            "properties": {
                                "connected": {
                                    "type": "boolean"
                                },
                                "sync_status": {
                                    "type": "string",
                                    "nullable": true,
                                    "enum": [
                                        null,
                                        "pending",
                                        "in_progress",
                                        "completed",
                                        "failed"
                                    ]
                                },
                                "available_from": {
                                    "type": "string",
                                    "nullable": true,
                                    "format": "date"
                                },
                                "available_to": {
                                    "type": "string",
                                    "nullable": true,
                                    "format": "date"
                                }
                            },
                            "additionalProperties": true
                        }
                    },
                    "additionalProperties": true
                }
            },
            "additionalProperties": true
        },
        "meta": {
            "type": "object",
            "required": [],
            "properties": {
                "request_id": {
                    "type": "string",
                    "description": "REST diagnostic ID; MCP exposes this only in the X-Request-Id HTTP header."
                },
                "usage": {
                    "type": "object",
                    "required": [
                        "remaining",
                        "limits",
                        "minute_reset_at",
                        "daily_reset_at"
                    ],
                    "properties": {
                        "remaining": {
                            "type": "object",
                            "required": [
                                "minute",
                                "day"
                            ],
                            "properties": {
                                "minute": {
                                    "type": "integer",
                                    "minimum": 0
                                },
                                "day": {
                                    "type": "integer",
                                    "minimum": 0
                                }
                            },
                            "additionalProperties": true
                        },
                        "limits": {
                            "type": "object",
                            "required": [
                                "minute",
                                "day"
                            ],
                            "properties": {
                                "minute": {
                                    "type": "integer",
                                    "minimum": 0
                                },
                                "day": {
                                    "type": "integer",
                                    "minimum": 0
                                }
                            },
                            "additionalProperties": true
                        },
                        "minute_reset_at": {
                            "type": "string",
                            "format": "date-time"
                        },
                        "daily_reset_at": {
                            "type": "string",
                            "format": "date-time"
                        }
                    },
                    "additionalProperties": true
                },
                "cache_hit": {
                    "type": "boolean"
                },
                "stale": {
                    "type": "boolean"
                }
            },
            "additionalProperties": true
        }
    }
}
```


## GET /api/v1/ranking-dimensions

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`.

| Parameter | Location | Required | Definition |
|---|---|---|---|
| `platform` | query | No | `{"type":"string","description":"Chart provider. Use ranking-dimensions to find supported combinations.","enum":["apple","spotify","ivoox"]}` |

Example request (fictitious IDs)

```sh
curl --fail-with-body \
  -H "Authorization: Bearer $PODSTATUS_API_KEY" \
  -H 'Accept: application/json' \
  'https://podstatus.com/api/v1/ranking-dimensions?platform=apple'
```

Example response (HTTP 200): Default response (fictitious values)

```json
{
    "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"
        }
    }
}
```

Successful response schema

```json
{
    "type": "object",
    "required": [
        "data",
        "meta"
    ],
    "properties": {
        "data": {
            "type": "array",
            "items": {
                "type": "object",
                "required": [
                    "platform",
                    "countries",
                    "categories",
                    "category_countries",
                    "country_aliases"
                ],
                "properties": {
                    "platform": {
                        "type": "string",
                        "enum": [
                            "apple",
                            "spotify",
                            "ivoox"
                        ]
                    },
                    "countries": {
                        "type": "object",
                        "additionalProperties": {
                            "type": "string"
                        }
                    },
                    "categories": {
                        "type": "object",
                        "additionalProperties": {
                            "type": "string"
                        }
                    },
                    "category_countries": {
                        "type": "array",
                        "items": {
                            "type": "string"
                        }
                    },
                    "country_aliases": {
                        "type": "object",
                        "additionalProperties": {
                            "type": "string"
                        }
                    }
                },
                "additionalProperties": true
            }
        },
        "meta": {
            "type": "object",
            "required": [],
            "properties": {
                "request_id": {
                    "type": "string",
                    "description": "REST diagnostic ID; MCP exposes this only in the X-Request-Id HTTP header."
                },
                "usage": {
                    "type": "object",
                    "required": [
                        "remaining",
                        "limits",
                        "minute_reset_at",
                        "daily_reset_at"
                    ],
                    "properties": {
                        "remaining": {
                            "type": "object",
                            "required": [
                                "minute",
                                "day"
                            ],
                            "properties": {
                                "minute": {
                                    "type": "integer",
                                    "minimum": 0
                                },
                                "day": {
                                    "type": "integer",
                                    "minimum": 0
                                }
                            },
                            "additionalProperties": true
                        },
                        "limits": {
                            "type": "object",
                            "required": [
                                "minute",
                                "day"
                            ],
                            "properties": {
                                "minute": {
                                    "type": "integer",
                                    "minimum": 0
                                },
                                "day": {
                                    "type": "integer",
                                    "minimum": 0
                                }
                            },
                            "additionalProperties": true
                        },
                        "minute_reset_at": {
                            "type": "string",
                            "format": "date-time"
                        },
                        "daily_reset_at": {
                            "type": "string",
                            "format": "date-time"
                        }
                    },
                    "additionalProperties": true
                },
                "cache_hit": {
                    "type": "boolean"
                },
                "stale": {
                    "type": "boolean"
                }
            },
            "additionalProperties": true
        }
    }
}
```


## GET /api/v1/podcasts/{id}/rankings/latest

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`.

| Parameter | Location | Required | Definition |
|---|---|---|---|
| `id` | path | Yes | `{"type":"integer","minimum":1,"description":"Podstatus ID from GET /podcasts; this is not the Apple Podcasts ID."}` |
| `platform` | query | Yes | `{"type":"string","description":"Chart provider. Use ranking-dimensions to find supported combinations.","enum":["apple","spotify","ivoox"]}` |
| `country` | query | Yes | `{"type":"string","description":"Two-letter country code, case-insensitive. Chart support varies by platform.","maxLength":2,"minLength":2}` |
| `category` | query | No | `{"type":"integer","description":"Category ID from ranking-dimensions. Latest rankings include all supported categories when omitted.","minimum":0}` |

Example request (fictitious IDs)

```sh
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 (HTTP 200): Default response (fictitious values)

```json
{
    "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"
        }
    }
}
```

Example response (HTTP 200): iVoox daily chart shared with Spain (fictitious values)

```json
{
    "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"
        }
    }
}
```

Successful response schema

```json
{
    "type": "object",
    "required": [
        "data",
        "meta"
    ],
    "properties": {
        "data": {
            "type": "array",
            "items": {
                "type": "object",
                "required": [
                    "podcast_id",
                    "platform",
                    "country",
                    "source_country",
                    "category",
                    "position",
                    "status"
                ],
                "properties": {
                    "podcast_id": {
                        "type": "integer"
                    },
                    "platform": {
                        "type": "string",
                        "enum": [
                            "apple",
                            "spotify",
                            "ivoox"
                        ]
                    },
                    "country": {
                        "type": "string"
                    },
                    "source_country": {
                        "type": "string",
                        "description": "Country of the stored chart. iVoox cl and pe share es; these are not independent chart observations."
                    },
                    "category": {
                        "type": "integer"
                    },
                    "position": {
                        "type": "integer",
                        "nullable": true
                    },
                    "status": {
                        "type": "string",
                        "enum": [
                            "ranked",
                            "not_observed"
                        ]
                    }
                },
                "additionalProperties": true
            }
        },
        "meta": {
            "type": "object",
            "required": [
                "timezone"
            ],
            "properties": {
                "timezone": {
                    "type": "string"
                },
                "snapshot_at": {
                    "type": "string",
                    "nullable": true,
                    "format": "date-time"
                },
                "snapshot_date": {
                    "type": "string",
                    "nullable": true,
                    "format": "date"
                },
                "request_id": {
                    "type": "string",
                    "description": "REST diagnostic ID; MCP exposes this only in the X-Request-Id HTTP header."
                },
                "usage": {
                    "type": "object",
                    "required": [
                        "remaining",
                        "limits",
                        "minute_reset_at",
                        "daily_reset_at"
                    ],
                    "properties": {
                        "remaining": {
                            "type": "object",
                            "required": [
                                "minute",
                                "day"
                            ],
                            "properties": {
                                "minute": {
                                    "type": "integer",
                                    "minimum": 0
                                },
                                "day": {
                                    "type": "integer",
                                    "minimum": 0
                                }
                            },
                            "additionalProperties": true
                        },
                        "limits": {
                            "type": "object",
                            "required": [
                                "minute",
                                "day"
                            ],
                            "properties": {
                                "minute": {
                                    "type": "integer",
                                    "minimum": 0
                                },
                                "day": {
                                    "type": "integer",
                                    "minimum": 0
                                }
                            },
                            "additionalProperties": true
                        },
                        "minute_reset_at": {
                            "type": "string",
                            "format": "date-time"
                        },
                        "daily_reset_at": {
                            "type": "string",
                            "format": "date-time"
                        }
                    },
                    "additionalProperties": true
                },
                "cache_hit": {
                    "type": "boolean"
                },
                "stale": {
                    "type": "boolean"
                }
            },
            "additionalProperties": true,
            "oneOf": [
                {
                    "required": [
                        "snapshot_at"
                    ]
                },
                {
                    "required": [
                        "snapshot_date"
                    ]
                }
            ]
        }
    }
}
```


## GET /api/v1/podcasts/{id}/rankings/overview

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`.

| Parameter | Location | Required | Definition |
|---|---|---|---|
| `id` | path | Yes | `{"type":"integer","minimum":1,"description":"Podstatus ID from GET /podcasts; this is not the Apple Podcasts ID."}` |
| `platform` | query | No | `{"type":"string","description":"Chart provider. Use ranking-dimensions to find supported combinations.","enum":["apple","spotify","ivoox"]}` |

Example request (fictitious IDs)

```sh
curl --fail-with-body \
  -H "Authorization: Bearer $PODSTATUS_API_KEY" \
  -H 'Accept: application/json' \
  'https://podstatus.com/api/v1/podcasts/123/rankings/overview'
```

Example response (HTTP 200): Default response (fictitious values)

```json
{
    "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"
        }
    }
}
```

Example response (HTTP 200): iVoox daily chart shared with Spain (fictitious values)

```json
{
    "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"
        }
    }
}
```

Successful response schema

```json
{
    "type": "object",
    "required": [
        "data",
        "meta"
    ],
    "properties": {
        "data": {
            "type": "array",
            "items": {
                "type": "object",
                "required": [
                    "podcast_id",
                    "platform",
                    "country",
                    "source_country",
                    "category",
                    "position",
                    "status"
                ],
                "properties": {
                    "podcast_id": {
                        "type": "integer"
                    },
                    "platform": {
                        "type": "string",
                        "enum": [
                            "apple",
                            "spotify",
                            "ivoox"
                        ]
                    },
                    "country": {
                        "type": "string"
                    },
                    "source_country": {
                        "type": "string",
                        "description": "Country of the stored chart. iVoox cl and pe share es; these are not independent chart observations."
                    },
                    "category": {
                        "type": "integer"
                    },
                    "position": {
                        "type": "integer",
                        "nullable": true
                    },
                    "status": {
                        "type": "string",
                        "enum": [
                            "ranked",
                            "not_observed"
                        ]
                    }
                },
                "additionalProperties": true
            }
        },
        "meta": {
            "type": "object",
            "required": [
                "timezone",
                "coverage",
                "snapshots"
            ],
            "properties": {
                "timezone": {
                    "type": "string"
                },
                "coverage": {
                    "type": "string",
                    "enum": [
                        "observed_rankings_only"
                    ]
                },
                "snapshots": {
                    "type": "array",
                    "items": {
                        "type": "object",
                        "required": [
                            "timezone",
                            "platform"
                        ],
                        "properties": {
                            "timezone": {
                                "type": "string"
                            },
                            "snapshot_at": {
                                "type": "string",
                                "nullable": true,
                                "format": "date-time"
                            },
                            "snapshot_date": {
                                "type": "string",
                                "nullable": true,
                                "format": "date"
                            },
                            "platform": {
                                "type": "string",
                                "enum": [
                                    "apple",
                                    "spotify",
                                    "ivoox"
                                ]
                            }
                        },
                        "additionalProperties": true,
                        "oneOf": [
                            {
                                "required": [
                                    "snapshot_at"
                                ]
                            },
                            {
                                "required": [
                                    "snapshot_date"
                                ]
                            }
                        ]
                    }
                },
                "request_id": {
                    "type": "string",
                    "description": "REST diagnostic ID; MCP exposes this only in the X-Request-Id HTTP header."
                },
                "usage": {
                    "type": "object",
                    "required": [
                        "remaining",
                        "limits",
                        "minute_reset_at",
                        "daily_reset_at"
                    ],
                    "properties": {
                        "remaining": {
                            "type": "object",
                            "required": [
                                "minute",
                                "day"
                            ],
                            "properties": {
                                "minute": {
                                    "type": "integer",
                                    "minimum": 0
                                },
                                "day": {
                                    "type": "integer",
                                    "minimum": 0
                                }
                            },
                            "additionalProperties": true
                        },
                        "limits": {
                            "type": "object",
                            "required": [
                                "minute",
                                "day"
                            ],
                            "properties": {
                                "minute": {
                                    "type": "integer",
                                    "minimum": 0
                                },
                                "day": {
                                    "type": "integer",
                                    "minimum": 0
                                }
                            },
                            "additionalProperties": true
                        },
                        "minute_reset_at": {
                            "type": "string",
                            "format": "date-time"
                        },
                        "daily_reset_at": {
                            "type": "string",
                            "format": "date-time"
                        }
                    },
                    "additionalProperties": true
                },
                "cache_hit": {
                    "type": "boolean"
                },
                "stale": {
                    "type": "boolean"
                }
            },
            "additionalProperties": true
        }
    }
}
```


## GET /api/v1/podcasts/{id}/rankings/history

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`.

| Parameter | Location | Required | Definition |
|---|---|---|---|
| `id` | path | Yes | `{"type":"integer","minimum":1,"description":"Podstatus ID from GET /podcasts; this is not the Apple Podcasts ID."}` |
| `platform` | query | Yes | `{"type":"string","description":"Chart provider. Use ranking-dimensions to find supported combinations.","enum":["apple","spotify","ivoox"]}` |
| `country` | query | Yes | `{"type":"string","description":"Two-letter country code, case-insensitive. Chart support varies by platform.","maxLength":2,"minLength":2}` |
| `category` | query | Yes | `{"type":"integer","description":"Category ID from ranking-dimensions. Latest rankings include all supported categories when omitted.","minimum":0}` |
| `from` | query | No | `{"type":"string","description":"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."}` |
| `to` | query | No | `{"type":"string","description":"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."}` |
| `resolution` | query | No | `{"type":"string","description":"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.","enum":["day","hour"]}` |

Example request (fictitious IDs)

```sh
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'
```

Example response (HTTP 200): Default response (fictitious values)

```json
{
    "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"
        }
    }
}
```

Example response (HTTP 200): iVoox daily chart shared with Spain (fictitious values)

```json
{
    "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"
        }
    }
}
```

Example response (HTTP 200): Hourly data with an unobserved boundary (fictitious values)

```json
{
    "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"
        }
    }
}
```

Successful response schema

```json
{
    "type": "object",
    "required": [
        "data",
        "meta"
    ],
    "properties": {
        "data": {
            "type": "array",
            "items": {
                "oneOf": [
                    {
                        "type": "object",
                        "required": [
                            "date",
                            "position",
                            "status"
                        ],
                        "properties": {
                            "date": {
                                "type": "string",
                                "format": "date"
                            },
                            "position": {
                                "type": "integer",
                                "nullable": true
                            },
                            "status": {
                                "type": "string",
                                "enum": [
                                    "ranked",
                                    "not_observed"
                                ]
                            }
                        },
                        "additionalProperties": true
                    },
                    {
                        "type": "object",
                        "required": [
                            "timestamp",
                            "position",
                            "status"
                        ],
                        "properties": {
                            "timestamp": {
                                "type": "string",
                                "format": "date-time"
                            },
                            "position": {
                                "type": "integer",
                                "nullable": true
                            },
                            "status": {
                                "type": "string",
                                "enum": [
                                    "ranked",
                                    "not_observed"
                                ]
                            }
                        },
                        "additionalProperties": true
                    }
                ]
            }
        },
        "meta": {
            "type": "object",
            "required": [
                "platform",
                "country",
                "source_country",
                "category",
                "from",
                "to",
                "resolution",
                "aggregation",
                "timezone"
            ],
            "properties": {
                "platform": {
                    "type": "string",
                    "enum": [
                        "apple",
                        "spotify",
                        "ivoox"
                    ]
                },
                "country": {
                    "type": "string"
                },
                "source_country": {
                    "type": "string"
                },
                "category": {
                    "type": "integer"
                },
                "from": {
                    "type": "string"
                },
                "to": {
                    "type": "string"
                },
                "resolution": {
                    "type": "string",
                    "enum": [
                        "day",
                        "hour"
                    ]
                },
                "aggregation": {
                    "type": "string",
                    "enum": [
                        "best_observed_rank",
                        "stored_snapshot"
                    ]
                },
                "timezone": {
                    "type": "string"
                },
                "request_id": {
                    "type": "string",
                    "description": "REST diagnostic ID; MCP exposes this only in the X-Request-Id HTTP header."
                },
                "usage": {
                    "type": "object",
                    "required": [
                        "remaining",
                        "limits",
                        "minute_reset_at",
                        "daily_reset_at"
                    ],
                    "properties": {
                        "remaining": {
                            "type": "object",
                            "required": [
                                "minute",
                                "day"
                            ],
                            "properties": {
                                "minute": {
                                    "type": "integer",
                                    "minimum": 0
                                },
                                "day": {
                                    "type": "integer",
                                    "minimum": 0
                                }
                            },
                            "additionalProperties": true
                        },
                        "limits": {
                            "type": "object",
                            "required": [
                                "minute",
                                "day"
                            ],
                            "properties": {
                                "minute": {
                                    "type": "integer",
                                    "minimum": 0
                                },
                                "day": {
                                    "type": "integer",
                                    "minimum": 0
                                }
                            },
                            "additionalProperties": true
                        },
                        "minute_reset_at": {
                            "type": "string",
                            "format": "date-time"
                        },
                        "daily_reset_at": {
                            "type": "string",
                            "format": "date-time"
                        }
                    },
                    "additionalProperties": true
                },
                "cache_hit": {
                    "type": "boolean"
                },
                "stale": {
                    "type": "boolean"
                }
            },
            "additionalProperties": true
        }
    }
}
```


## GET /api/v1/podcasts/{id}/rankings/summary

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`.

| Parameter | Location | Required | Definition |
|---|---|---|---|
| `id` | path | Yes | `{"type":"integer","minimum":1,"description":"Podstatus ID from GET /podcasts; this is not the Apple Podcasts ID."}` |
| `platform` | query | Yes | `{"type":"string","description":"Chart provider. Use ranking-dimensions to find supported combinations.","enum":["apple","spotify","ivoox"]}` |
| `country` | query | Yes | `{"type":"string","description":"Two-letter country code, case-insensitive. Chart support varies by platform.","maxLength":2,"minLength":2}` |
| `category` | query | Yes | `{"type":"integer","description":"Category ID from ranking-dimensions. Latest rankings include all supported categories when omitted.","minimum":0}` |
| `from` | query | No | `{"type":"string","description":"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."}` |
| `to` | query | No | `{"type":"string","description":"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."}` |
| `resolution` | query | No | `{"type":"string","description":"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.","enum":["day","hour"]}` |
| `group_by` | query | No | `{"type":"string","description":"Aggregation of the requested interval; monthly groups may cover partial months.","enum":["period","month"]}` |

Example request (fictitious IDs)

```sh
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'
```

Example response (HTTP 200): Default response (fictitious values)

```json
{
    "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"
        }
    }
}
```

Example response (HTTP 200): iVoox daily chart shared with Spain (fictitious values)

```json
{
    "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"
        }
    }
}
```

Example response (HTTP 200): Hourly data with an unobserved boundary (fictitious values)

```json
{
    "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"
        }
    }
}
```

Successful response schema

```json
{
    "type": "object",
    "required": [
        "data",
        "meta"
    ],
    "properties": {
        "data": {
            "type": "array",
            "items": {
                "type": "object",
                "required": [
                    "period",
                    "best_observed_rank",
                    "best_date",
                    "start_position",
                    "end_position",
                    "change",
                    "observations",
                    "first_available",
                    "last_available"
                ],
                "properties": {
                    "period": {
                        "type": "string"
                    },
                    "best_observed_rank": {
                        "type": "integer",
                        "nullable": true
                    },
                    "best_date": {
                        "type": "string",
                        "nullable": true
                    },
                    "start_position": {
                        "type": "integer",
                        "nullable": true
                    },
                    "end_position": {
                        "type": "integer",
                        "nullable": true
                    },
                    "change": {
                        "type": "integer",
                        "nullable": true
                    },
                    "observations": {
                        "type": "integer"
                    },
                    "first_available": {
                        "type": "string",
                        "nullable": true
                    },
                    "last_available": {
                        "type": "string",
                        "nullable": true
                    }
                },
                "additionalProperties": true
            }
        },
        "meta": {
            "type": "object",
            "required": [
                "platform",
                "country",
                "source_country",
                "category",
                "from",
                "to",
                "resolution",
                "aggregation",
                "timezone",
                "change_definition"
            ],
            "properties": {
                "platform": {
                    "type": "string",
                    "enum": [
                        "apple",
                        "spotify",
                        "ivoox"
                    ]
                },
                "country": {
                    "type": "string"
                },
                "source_country": {
                    "type": "string"
                },
                "category": {
                    "type": "integer"
                },
                "from": {
                    "type": "string"
                },
                "to": {
                    "type": "string"
                },
                "resolution": {
                    "type": "string",
                    "enum": [
                        "day",
                        "hour"
                    ]
                },
                "aggregation": {
                    "type": "string",
                    "enum": [
                        "best_observed_rank",
                        "stored_snapshot"
                    ]
                },
                "timezone": {
                    "type": "string"
                },
                "change_definition": {
                    "type": "string"
                },
                "request_id": {
                    "type": "string",
                    "description": "REST diagnostic ID; MCP exposes this only in the X-Request-Id HTTP header."
                },
                "usage": {
                    "type": "object",
                    "required": [
                        "remaining",
                        "limits",
                        "minute_reset_at",
                        "daily_reset_at"
                    ],
                    "properties": {
                        "remaining": {
                            "type": "object",
                            "required": [
                                "minute",
                                "day"
                            ],
                            "properties": {
                                "minute": {
                                    "type": "integer",
                                    "minimum": 0
                                },
                                "day": {
                                    "type": "integer",
                                    "minimum": 0
                                }
                            },
                            "additionalProperties": true
                        },
                        "limits": {
                            "type": "object",
                            "required": [
                                "minute",
                                "day"
                            ],
                            "properties": {
                                "minute": {
                                    "type": "integer",
                                    "minimum": 0
                                },
                                "day": {
                                    "type": "integer",
                                    "minimum": 0
                                }
                            },
                            "additionalProperties": true
                        },
                        "minute_reset_at": {
                            "type": "string",
                            "format": "date-time"
                        },
                        "daily_reset_at": {
                            "type": "string",
                            "format": "date-time"
                        }
                    },
                    "additionalProperties": true
                },
                "cache_hit": {
                    "type": "boolean"
                },
                "stale": {
                    "type": "boolean"
                }
            },
            "additionalProperties": true
        }
    }
}
```


## GET /api/v1/rankings/latest

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

Required permission `rankings:read`.

| Parameter | Location | Required | Definition |
|---|---|---|---|
| `platform` | query | Yes | `{"type":"string","description":"Chart provider. Use ranking-dimensions to find supported combinations.","enum":["apple","spotify","ivoox"]}` |
| `country` | query | Yes | `{"type":"string","description":"Two-letter country code, case-insensitive. Chart support varies by platform.","maxLength":2,"minLength":2}` |
| `category` | query | No | `{"type":"integer","description":"Category ID from ranking-dimensions. Latest rankings include all supported categories when omitted.","minimum":0}` |
| `podcast_ids` | query | Yes | `{"type":"array","minItems":1,"maxItems":5,"uniqueItems":true,"items":{"type":"integer","minimum":1}}` |

Example request (fictitious IDs)

```sh
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'
```

Example response (HTTP 200): Default response (fictitious values)

```json
{
    "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"
        }
    }
}
```

Example response (HTTP 200): iVoox daily chart shared with Spain (fictitious values)

```json
{
    "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"
        }
    }
}
```

Successful response schema

```json
{
    "type": "object",
    "required": [
        "data",
        "meta"
    ],
    "properties": {
        "data": {
            "type": "array",
            "items": {
                "type": "object",
                "required": [
                    "podcast_id",
                    "data",
                    "meta"
                ],
                "properties": {
                    "podcast_id": {
                        "type": "integer"
                    },
                    "data": {
                        "type": "array",
                        "items": {
                            "type": "object",
                            "required": [
                                "podcast_id",
                                "platform",
                                "country",
                                "source_country",
                                "category",
                                "position",
                                "status"
                            ],
                            "properties": {
                                "podcast_id": {
                                    "type": "integer"
                                },
                                "platform": {
                                    "type": "string",
                                    "enum": [
                                        "apple",
                                        "spotify",
                                        "ivoox"
                                    ]
                                },
                                "country": {
                                    "type": "string"
                                },
                                "source_country": {
                                    "type": "string",
                                    "description": "Country of the stored chart. iVoox cl and pe share es; these are not independent chart observations."
                                },
                                "category": {
                                    "type": "integer"
                                },
                                "position": {
                                    "type": "integer",
                                    "nullable": true
                                },
                                "status": {
                                    "type": "string",
                                    "enum": [
                                        "ranked",
                                        "not_observed"
                                    ]
                                }
                            },
                            "additionalProperties": true
                        }
                    },
                    "meta": {
                        "type": "object",
                        "required": [
                            "timezone"
                        ],
                        "properties": {
                            "timezone": {
                                "type": "string"
                            },
                            "snapshot_at": {
                                "type": "string",
                                "nullable": true,
                                "format": "date-time"
                            },
                            "snapshot_date": {
                                "type": "string",
                                "nullable": true,
                                "format": "date"
                            }
                        },
                        "additionalProperties": true,
                        "oneOf": [
                            {
                                "required": [
                                    "snapshot_at"
                                ]
                            },
                            {
                                "required": [
                                    "snapshot_date"
                                ]
                            }
                        ]
                    }
                },
                "additionalProperties": true
            }
        },
        "meta": {
            "type": "object",
            "required": [],
            "properties": {
                "request_id": {
                    "type": "string",
                    "description": "REST diagnostic ID; MCP exposes this only in the X-Request-Id HTTP header."
                },
                "usage": {
                    "type": "object",
                    "required": [
                        "remaining",
                        "limits",
                        "minute_reset_at",
                        "daily_reset_at"
                    ],
                    "properties": {
                        "remaining": {
                            "type": "object",
                            "required": [
                                "minute",
                                "day"
                            ],
                            "properties": {
                                "minute": {
                                    "type": "integer",
                                    "minimum": 0
                                },
                                "day": {
                                    "type": "integer",
                                    "minimum": 0
                                }
                            },
                            "additionalProperties": true
                        },
                        "limits": {
                            "type": "object",
                            "required": [
                                "minute",
                                "day"
                            ],
                            "properties": {
                                "minute": {
                                    "type": "integer",
                                    "minimum": 0
                                },
                                "day": {
                                    "type": "integer",
                                    "minimum": 0
                                }
                            },
                            "additionalProperties": true
                        },
                        "minute_reset_at": {
                            "type": "string",
                            "format": "date-time"
                        },
                        "daily_reset_at": {
                            "type": "string",
                            "format": "date-time"
                        }
                    },
                    "additionalProperties": true
                },
                "cache_hit": {
                    "type": "boolean"
                },
                "stale": {
                    "type": "boolean"
                }
            },
            "additionalProperties": true
        }
    }
}
```


## GET /api/v1/podcasts/{id}/downloads

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

Required permission `analytics:read`.

| Parameter | Location | Required | Definition |
|---|---|---|---|
| `id` | path | Yes | `{"type":"integer","minimum":1,"description":"Podstatus ID from GET /podcasts; this is not the Apple Podcasts ID."}` |
| `from` | query | No | `{"type":"string","description":"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."}` |
| `to` | query | No | `{"type":"string","description":"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_by` | query | No | `{"type":"string","description":"Aggregation of the requested interval; monthly groups may cover partial months.","enum":["day","month"]}` |

Example request (fictitious IDs)

```sh
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'
```

Example response (HTTP 200): Default response (fictitious values)

```json
{
    "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"
        }
    }
}
```

Example response (HTTP 200): Monthly group: available (fictitious values)

```json
{
    "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"
        }
    }
}
```

Example response (HTTP 200): Monthly group: partial (fictitious values)

```json
{
    "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"
        }
    }
}
```

Example response (HTTP 200): Monthly group: not_observed (fictitious values)

```json
{
    "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"
        }
    }
}
```

Successful response schema

```json
{
    "type": "object",
    "required": [
        "data",
        "meta"
    ],
    "properties": {
        "data": {
            "type": "array",
            "items": {
                "oneOf": [
                    {
                        "type": "object",
                        "required": [
                            "date",
                            "value",
                            "status"
                        ],
                        "properties": {
                            "date": {
                                "type": "string",
                                "format": "date"
                            },
                            "value": {
                                "type": "integer",
                                "nullable": true
                            },
                            "status": {
                                "type": "string",
                                "enum": [
                                    "available",
                                    "not_observed"
                                ]
                            }
                        },
                        "additionalProperties": true
                    },
                    {
                        "type": "object",
                        "required": [
                            "date",
                            "value",
                            "status",
                            "from",
                            "to",
                            "requested_days",
                            "available_days"
                        ],
                        "properties": {
                            "date": {
                                "type": "string",
                                "pattern": "^\\d{4}-\\d{2}$"
                            },
                            "value": {
                                "type": "integer",
                                "nullable": true
                            },
                            "status": {
                                "type": "string",
                                "enum": [
                                    "available",
                                    "partial",
                                    "not_observed"
                                ]
                            },
                            "from": {
                                "type": "string",
                                "format": "date"
                            },
                            "to": {
                                "type": "string",
                                "format": "date"
                            },
                            "requested_days": {
                                "type": "integer"
                            },
                            "available_days": {
                                "type": "integer"
                            }
                        },
                        "additionalProperties": true
                    }
                ]
            }
        },
        "meta": {
            "type": "object",
            "required": [
                "from",
                "to",
                "source",
                "metric",
                "latest_available_date",
                "observed_total",
                "available_days",
                "timezone"
            ],
            "properties": {
                "from": {
                    "type": "string"
                },
                "to": {
                    "type": "string"
                },
                "source": {
                    "type": "string",
                    "enum": [
                        "op3"
                    ]
                },
                "metric": {
                    "type": "string",
                    "enum": [
                        "downloads"
                    ]
                },
                "latest_available_date": {
                    "type": "string",
                    "nullable": true,
                    "format": "date"
                },
                "observed_total": {
                    "type": "integer",
                    "nullable": true
                },
                "available_days": {
                    "type": "integer"
                },
                "timezone": {
                    "type": "string"
                },
                "request_id": {
                    "type": "string",
                    "description": "REST diagnostic ID; MCP exposes this only in the X-Request-Id HTTP header."
                },
                "usage": {
                    "type": "object",
                    "required": [
                        "remaining",
                        "limits",
                        "minute_reset_at",
                        "daily_reset_at"
                    ],
                    "properties": {
                        "remaining": {
                            "type": "object",
                            "required": [
                                "minute",
                                "day"
                            ],
                            "properties": {
                                "minute": {
                                    "type": "integer",
                                    "minimum": 0
                                },
                                "day": {
                                    "type": "integer",
                                    "minimum": 0
                                }
                            },
                            "additionalProperties": true
                        },
                        "limits": {
                            "type": "object",
                            "required": [
                                "minute",
                                "day"
                            ],
                            "properties": {
                                "minute": {
                                    "type": "integer",
                                    "minimum": 0
                                },
                                "day": {
                                    "type": "integer",
                                    "minimum": 0
                                }
                            },
                            "additionalProperties": true
                        },
                        "minute_reset_at": {
                            "type": "string",
                            "format": "date-time"
                        },
                        "daily_reset_at": {
                            "type": "string",
                            "format": "date-time"
                        }
                    },
                    "additionalProperties": true
                },
                "cache_hit": {
                    "type": "boolean"
                },
                "stale": {
                    "type": "boolean"
                }
            },
            "additionalProperties": true
        }
    }
}
```


## GET /api/v1/podcasts/{id}/audience

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

Required permission `analytics:read`.

| Parameter | Location | Required | Definition |
|---|---|---|---|
| `id` | path | Yes | `{"type":"integer","minimum":1,"description":"Podstatus ID from GET /podcasts; this is not the Apple Podcasts ID."}` |
| `from` | query | No | `{"type":"string","description":"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."}` |
| `to` | query | No | `{"type":"string","description":"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."}` |
| `dimension` | query | Yes | `{"type":"string","description":"Group recorded downloads by this audience dimension.","enum":["country","region","device","app"]}` |

Example request (fictitious IDs)

```sh
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'
```

Example response (HTTP 200): Default response (fictitious values)

```json
{
    "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"
        }
    }
}
```

Example response (HTTP 200): Distribution by region (fictitious values)

```json
{
    "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"
        }
    }
}
```

Example response (HTTP 200): Distribution by device (fictitious values)

```json
{
    "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"
        }
    }
}
```

Example response (HTTP 200): Distribution by app (fictitious values)

```json
{
    "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"
        }
    }
}
```

Successful response schema

```json
{
    "type": "object",
    "required": [
        "data",
        "meta"
    ],
    "properties": {
        "data": {
            "type": "array",
            "items": {
                "oneOf": [
                    {
                        "type": "object",
                        "required": [
                            "country_code",
                            "downloads",
                            "percentage"
                        ],
                        "properties": {
                            "country_code": {
                                "type": "string"
                            },
                            "downloads": {
                                "type": "integer"
                            },
                            "percentage": {
                                "type": "number",
                                "nullable": true
                            }
                        },
                        "additionalProperties": true,
                        "not": {
                            "required": [
                                "region_name"
                            ]
                        }
                    },
                    {
                        "type": "object",
                        "required": [
                            "country_code",
                            "region_name",
                            "downloads",
                            "percentage"
                        ],
                        "properties": {
                            "country_code": {
                                "type": "string"
                            },
                            "region_name": {
                                "type": "string"
                            },
                            "downloads": {
                                "type": "integer"
                            },
                            "percentage": {
                                "type": "number",
                                "nullable": true
                            }
                        },
                        "additionalProperties": true
                    },
                    {
                        "type": "object",
                        "required": [
                            "device_type",
                            "downloads",
                            "percentage"
                        ],
                        "properties": {
                            "device_type": {
                                "type": "string"
                            },
                            "downloads": {
                                "type": "integer"
                            },
                            "percentage": {
                                "type": "number",
                                "nullable": true
                            }
                        },
                        "additionalProperties": true
                    },
                    {
                        "type": "object",
                        "required": [
                            "agent_name",
                            "downloads",
                            "percentage"
                        ],
                        "properties": {
                            "agent_name": {
                                "type": "string"
                            },
                            "downloads": {
                                "type": "integer"
                            },
                            "percentage": {
                                "type": "number",
                                "nullable": true
                            }
                        },
                        "additionalProperties": true
                    }
                ]
            }
        },
        "meta": {
            "type": "object",
            "required": [
                "from",
                "to",
                "source",
                "metric",
                "denominator",
                "denominator_definition",
                "dimension",
                "truncated",
                "timezone"
            ],
            "properties": {
                "from": {
                    "type": "string"
                },
                "to": {
                    "type": "string"
                },
                "source": {
                    "type": "string",
                    "enum": [
                        "op3"
                    ]
                },
                "metric": {
                    "type": "string",
                    "enum": [
                        "downloads"
                    ]
                },
                "denominator": {
                    "type": "integer"
                },
                "denominator_definition": {
                    "type": "string"
                },
                "dimension": {
                    "type": "string",
                    "enum": [
                        "country",
                        "region",
                        "device",
                        "app"
                    ]
                },
                "truncated": {
                    "type": "boolean",
                    "enum": [
                        false
                    ],
                    "description": "Oversized results fail with query_too_large instead of returning truncated aggregates."
                },
                "timezone": {
                    "type": "string"
                },
                "request_id": {
                    "type": "string",
                    "description": "REST diagnostic ID; MCP exposes this only in the X-Request-Id HTTP header."
                },
                "usage": {
                    "type": "object",
                    "required": [
                        "remaining",
                        "limits",
                        "minute_reset_at",
                        "daily_reset_at"
                    ],
                    "properties": {
                        "remaining": {
                            "type": "object",
                            "required": [
                                "minute",
                                "day"
                            ],
                            "properties": {
                                "minute": {
                                    "type": "integer",
                                    "minimum": 0
                                },
                                "day": {
                                    "type": "integer",
                                    "minimum": 0
                                }
                            },
                            "additionalProperties": true
                        },
                        "limits": {
                            "type": "object",
                            "required": [
                                "minute",
                                "day"
                            ],
                            "properties": {
                                "minute": {
                                    "type": "integer",
                                    "minimum": 0
                                },
                                "day": {
                                    "type": "integer",
                                    "minimum": 0
                                }
                            },
                            "additionalProperties": true
                        },
                        "minute_reset_at": {
                            "type": "string",
                            "format": "date-time"
                        },
                        "daily_reset_at": {
                            "type": "string",
                            "format": "date-time"
                        }
                    },
                    "additionalProperties": true
                },
                "cache_hit": {
                    "type": "boolean"
                },
                "stale": {
                    "type": "boolean"
                }
            },
            "additionalProperties": true
        }
    }
}
```


## GET /api/v1/podcasts/{id}/episodes

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`.

| Parameter | Location | Required | Definition |
|---|---|---|---|
| `id` | path | Yes | `{"type":"integer","minimum":1,"description":"Podstatus ID from GET /podcasts; this is not the Apple Podcasts ID."}` |
| `from` | query | No | `{"type":"string","description":"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."}` |
| `to` | query | No | `{"type":"string","description":"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."}` |
| `page` | query | No | `{"type":"integer","description":"Page number. Default: 1. The offset (page - 1) \u00d7 per_page must not exceed 10000.","minimum":1,"maximum":10000}` |
| `per_page` | query | No | `{"type":"integer","description":"Items per page. Default: 25.","minimum":1,"maximum":50}` |
| `sort` | query | No | `{"type":"string","description":"Default: published (newest first). downloads sorts by descending downloads for the requested period.","enum":["published","downloads"]}` |
| `published_from` | query | No | `{"type":"string","description":"Inclusive episode publication date in YYYY-MM-DD format."}` |
| `published_to` | query | No | `{"type":"string","description":"Inclusive episode publication date in YYYY-MM-DD format."}` |

Example request (fictitious IDs)

```sh
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'
```

Example response (HTTP 200): Default response (fictitious values)

```json
{
    "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"
        }
    }
}
```

Successful response schema

```json
{
    "type": "object",
    "required": [
        "data",
        "meta"
    ],
    "properties": {
        "data": {
            "type": "array",
            "items": {
                "type": "object",
                "required": [
                    "id",
                    "title",
                    "published_at",
                    "audio_url",
                    "downloads"
                ],
                "properties": {
                    "id": {
                        "type": "integer"
                    },
                    "title": {
                        "type": "string"
                    },
                    "published_at": {
                        "type": "string",
                        "format": "date-time"
                    },
                    "audio_url": {
                        "type": "string"
                    },
                    "downloads": {
                        "type": "integer",
                        "nullable": true
                    }
                },
                "additionalProperties": true
            }
        },
        "meta": {
            "type": "object",
            "required": [
                "page",
                "per_page",
                "has_more",
                "from",
                "to",
                "downloads_available",
                "episode_matching"
            ],
            "properties": {
                "page": {
                    "type": "integer"
                },
                "per_page": {
                    "type": "integer"
                },
                "has_more": {
                    "type": "boolean"
                },
                "from": {
                    "type": "string"
                },
                "to": {
                    "type": "string"
                },
                "downloads_available": {
                    "type": "boolean"
                },
                "episode_matching": {
                    "type": "string"
                },
                "request_id": {
                    "type": "string",
                    "description": "REST diagnostic ID; MCP exposes this only in the X-Request-Id HTTP header."
                },
                "usage": {
                    "type": "object",
                    "required": [
                        "remaining",
                        "limits",
                        "minute_reset_at",
                        "daily_reset_at"
                    ],
                    "properties": {
                        "remaining": {
                            "type": "object",
                            "required": [
                                "minute",
                                "day"
                            ],
                            "properties": {
                                "minute": {
                                    "type": "integer",
                                    "minimum": 0
                                },
                                "day": {
                                    "type": "integer",
                                    "minimum": 0
                                }
                            },
                            "additionalProperties": true
                        },
                        "limits": {
                            "type": "object",
                            "required": [
                                "minute",
                                "day"
                            ],
                            "properties": {
                                "minute": {
                                    "type": "integer",
                                    "minimum": 0
                                },
                                "day": {
                                    "type": "integer",
                                    "minimum": 0
                                }
                            },
                            "additionalProperties": true
                        },
                        "minute_reset_at": {
                            "type": "string",
                            "format": "date-time"
                        },
                        "daily_reset_at": {
                            "type": "string",
                            "format": "date-time"
                        }
                    },
                    "additionalProperties": true
                },
                "cache_hit": {
                    "type": "boolean"
                },
                "stale": {
                    "type": "boolean"
                }
            },
            "additionalProperties": true
        }
    }
}
```


## GET /api/v1/podcasts/{id}/reviews

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`.

| Parameter | Location | Required | Definition |
|---|---|---|---|
| `id` | path | Yes | `{"type":"integer","minimum":1,"description":"Podstatus ID from GET /podcasts; this is not the Apple Podcasts ID."}` |
| `from` | query | No | `{"type":"string","description":"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."}` |
| `to` | query | No | `{"type":"string","description":"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."}` |
| `page` | query | No | `{"type":"integer","description":"Page number. Default: 1. The offset (page - 1) \u00d7 per_page must not exceed 10000.","minimum":1,"maximum":10000}` |
| `per_page` | query | No | `{"type":"integer","description":"Items per page. Default: 25.","minimum":1,"maximum":50}` |
| `country` | query | No | `{"type":"string","description":"Two-letter country code, case-insensitive. Chart support varies by platform.","maxLength":2,"minLength":2}` |
| `rating` | query | No | `{"type":"integer","description":"Exact review rating, from 1 to 5.","minimum":1,"maximum":5}` |
| `provider` | query | No | `{"type":"string","description":"Review source: apple, stitcher or podchaser.","enum":["apple","stitcher","podchaser"]}` |

Example request (fictitious IDs)

```sh
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'
```

Example response (HTTP 200): Default response (fictitious values)

```json
{
    "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"
        }
    }
}
```

Successful response schema

```json
{
    "type": "object",
    "required": [
        "data",
        "meta"
    ],
    "properties": {
        "data": {
            "type": "array",
            "items": {
                "type": "object",
                "required": [
                    "id",
                    "provider",
                    "provider_id",
                    "country_code",
                    "rating",
                    "title",
                    "content",
                    "author",
                    "date"
                ],
                "properties": {
                    "id": {
                        "type": "integer"
                    },
                    "provider": {
                        "type": "string",
                        "enum": [
                            "apple",
                            "stitcher",
                            "podchaser",
                            "unknown"
                        ]
                    },
                    "provider_id": {
                        "type": "integer"
                    },
                    "country_code": {
                        "type": "string"
                    },
                    "rating": {
                        "type": "integer",
                        "nullable": true
                    },
                    "title": {
                        "type": "string",
                        "nullable": true
                    },
                    "content": {
                        "type": "string",
                        "nullable": true
                    },
                    "author": {
                        "type": "string",
                        "nullable": true
                    },
                    "date": {
                        "type": "string",
                        "format": "date-time"
                    }
                },
                "additionalProperties": true
            }
        },
        "meta": {
            "type": "object",
            "required": [
                "page",
                "per_page",
                "has_more"
            ],
            "properties": {
                "page": {
                    "type": "integer"
                },
                "per_page": {
                    "type": "integer"
                },
                "has_more": {
                    "type": "boolean"
                },
                "request_id": {
                    "type": "string",
                    "description": "REST diagnostic ID; MCP exposes this only in the X-Request-Id HTTP header."
                },
                "usage": {
                    "type": "object",
                    "required": [
                        "remaining",
                        "limits",
                        "minute_reset_at",
                        "daily_reset_at"
                    ],
                    "properties": {
                        "remaining": {
                            "type": "object",
                            "required": [
                                "minute",
                                "day"
                            ],
                            "properties": {
                                "minute": {
                                    "type": "integer",
                                    "minimum": 0
                                },
                                "day": {
                                    "type": "integer",
                                    "minimum": 0
                                }
                            },
                            "additionalProperties": true
                        },
                        "limits": {
                            "type": "object",
                            "required": [
                                "minute",
                                "day"
                            ],
                            "properties": {
                                "minute": {
                                    "type": "integer",
                                    "minimum": 0
                                },
                                "day": {
                                    "type": "integer",
                                    "minimum": 0
                                }
                            },
                            "additionalProperties": true
                        },
                        "minute_reset_at": {
                            "type": "string",
                            "format": "date-time"
                        },
                        "daily_reset_at": {
                            "type": "string",
                            "format": "date-time"
                        }
                    },
                    "additionalProperties": true
                },
                "cache_hit": {
                    "type": "boolean"
                },
                "stale": {
                    "type": "boolean"
                }
            },
            "additionalProperties": true
        }
    }
}
```


## GET /api/v1/podcasts/{id}/reviews/summary

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`.

| Parameter | Location | Required | Definition |
|---|---|---|---|
| `id` | path | Yes | `{"type":"integer","minimum":1,"description":"Podstatus ID from GET /podcasts; this is not the Apple Podcasts ID."}` |
| `from` | query | No | `{"type":"string","description":"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."}` |
| `to` | query | No | `{"type":"string","description":"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."}` |
| `country` | query | No | `{"type":"string","description":"Two-letter country code, case-insensitive. Chart support varies by platform.","maxLength":2,"minLength":2}` |
| `provider` | query | No | `{"type":"string","description":"Review source: apple, stitcher or podchaser.","enum":["apple","stitcher","podchaser"]}` |

Example request (fictitious IDs)

```sh
curl --fail-with-body \
  -H "Authorization: Bearer $PODSTATUS_API_KEY" \
  -H 'Accept: application/json' \
  'https://podstatus.com/api/v1/podcasts/123/reviews/summary'
```

Example response (HTTP 200): Default response (fictitious values)

```json
{
    "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"
        }
    }
}
```

Successful response schema

```json
{
    "type": "object",
    "required": [
        "data",
        "meta"
    ],
    "properties": {
        "data": {
            "type": "object",
            "required": [
                "total_reviews",
                "rated_reviews",
                "average_rating",
                "rating_counts",
                "by_provider"
            ],
            "properties": {
                "total_reviews": {
                    "type": "integer"
                },
                "rated_reviews": {
                    "type": "integer"
                },
                "average_rating": {
                    "type": "number",
                    "nullable": true
                },
                "rating_counts": {
                    "type": "object",
                    "required": [
                        "1",
                        "2",
                        "3",
                        "4",
                        "5"
                    ],
                    "properties": {
                        "1": {
                            "type": "integer"
                        },
                        "2": {
                            "type": "integer"
                        },
                        "3": {
                            "type": "integer"
                        },
                        "4": {
                            "type": "integer"
                        },
                        "5": {
                            "type": "integer"
                        }
                    }
                },
                "by_provider": {
                    "type": "array",
                    "items": {
                        "type": "object",
                        "required": [
                            "provider",
                            "provider_id",
                            "total_reviews",
                            "rated_reviews",
                            "average_rating",
                            "rating_counts"
                        ],
                        "properties": {
                            "provider": {
                                "type": "string",
                                "enum": [
                                    "apple",
                                    "stitcher",
                                    "podchaser",
                                    "unknown"
                                ]
                            },
                            "provider_id": {
                                "type": "integer"
                            },
                            "total_reviews": {
                                "type": "integer"
                            },
                            "rated_reviews": {
                                "type": "integer"
                            },
                            "average_rating": {
                                "type": "number",
                                "nullable": true
                            },
                            "rating_counts": {
                                "type": "object",
                                "required": [
                                    "1",
                                    "2",
                                    "3",
                                    "4",
                                    "5"
                                ],
                                "properties": {
                                    "1": {
                                        "type": "integer"
                                    },
                                    "2": {
                                        "type": "integer"
                                    },
                                    "3": {
                                        "type": "integer"
                                    },
                                    "4": {
                                        "type": "integer"
                                    },
                                    "5": {
                                        "type": "integer"
                                    }
                                }
                            }
                        },
                        "additionalProperties": true
                    }
                }
            },
            "additionalProperties": true
        },
        "meta": {
            "type": "object",
            "required": [
                "from",
                "to",
                "coverage",
                "timezone"
            ],
            "properties": {
                "from": {
                    "type": "string",
                    "nullable": true,
                    "format": "date"
                },
                "to": {
                    "type": "string",
                    "nullable": true,
                    "format": "date"
                },
                "coverage": {
                    "type": "string",
                    "enum": [
                        "stored_reviews"
                    ]
                },
                "timezone": {
                    "type": "string"
                },
                "request_id": {
                    "type": "string",
                    "description": "REST diagnostic ID; MCP exposes this only in the X-Request-Id HTTP header."
                },
                "usage": {
                    "type": "object",
                    "required": [
                        "remaining",
                        "limits",
                        "minute_reset_at",
                        "daily_reset_at"
                    ],
                    "properties": {
                        "remaining": {
                            "type": "object",
                            "required": [
                                "minute",
                                "day"
                            ],
                            "properties": {
                                "minute": {
                                    "type": "integer",
                                    "minimum": 0
                                },
                                "day": {
                                    "type": "integer",
                                    "minimum": 0
                                }
                            },
                            "additionalProperties": true
                        },
                        "limits": {
                            "type": "object",
                            "required": [
                                "minute",
                                "day"
                            ],
                            "properties": {
                                "minute": {
                                    "type": "integer",
                                    "minimum": 0
                                },
                                "day": {
                                    "type": "integer",
                                    "minimum": 0
                                }
                            },
                            "additionalProperties": true
                        },
                        "minute_reset_at": {
                            "type": "string",
                            "format": "date-time"
                        },
                        "daily_reset_at": {
                            "type": "string",
                            "format": "date-time"
                        }
                    },
                    "additionalProperties": true
                },
                "cache_hit": {
                    "type": "boolean"
                },
                "stale": {
                    "type": "boolean"
                }
            },
            "additionalProperties": true
        }
    }
}
```


## GET /api/v1/podcasts/{id}/keywords

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

Required permission `keywords:read`.

| Parameter | Location | Required | Definition |
|---|---|---|---|
| `id` | path | Yes | `{"type":"integer","minimum":1,"description":"Podstatus ID from GET /podcasts; this is not the Apple Podcasts ID."}` |
| `page` | query | No | `{"type":"integer","description":"Page number. Default: 1. The offset (page - 1) \u00d7 per_page must not exceed 10000.","minimum":1,"maximum":10000}` |
| `per_page` | query | No | `{"type":"integer","description":"Items per page. Default: 25.","minimum":1,"maximum":50}` |

Example request (fictitious IDs)

```sh
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'
```

Example response (HTTP 200): Default response (fictitious values)

```json
{
    "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"
        }
    }
}
```

Successful response schema

```json
{
    "type": "object",
    "required": [
        "data",
        "meta"
    ],
    "properties": {
        "data": {
            "type": "array",
            "items": {
                "type": "object",
                "required": [
                    "keyword_id",
                    "keyword"
                ],
                "properties": {
                    "keyword_id": {
                        "type": "integer"
                    },
                    "keyword": {
                        "type": "string",
                        "nullable": true
                    }
                },
                "additionalProperties": true
            }
        },
        "meta": {
            "type": "object",
            "required": [
                "page",
                "per_page",
                "has_more"
            ],
            "properties": {
                "page": {
                    "type": "integer"
                },
                "per_page": {
                    "type": "integer"
                },
                "has_more": {
                    "type": "boolean"
                },
                "request_id": {
                    "type": "string",
                    "description": "REST diagnostic ID; MCP exposes this only in the X-Request-Id HTTP header."
                },
                "usage": {
                    "type": "object",
                    "required": [
                        "remaining",
                        "limits",
                        "minute_reset_at",
                        "daily_reset_at"
                    ],
                    "properties": {
                        "remaining": {
                            "type": "object",
                            "required": [
                                "minute",
                                "day"
                            ],
                            "properties": {
                                "minute": {
                                    "type": "integer",
                                    "minimum": 0
                                },
                                "day": {
                                    "type": "integer",
                                    "minimum": 0
                                }
                            },
                            "additionalProperties": true
                        },
                        "limits": {
                            "type": "object",
                            "required": [
                                "minute",
                                "day"
                            ],
                            "properties": {
                                "minute": {
                                    "type": "integer",
                                    "minimum": 0
                                },
                                "day": {
                                    "type": "integer",
                                    "minimum": 0
                                }
                            },
                            "additionalProperties": true
                        },
                        "minute_reset_at": {
                            "type": "string",
                            "format": "date-time"
                        },
                        "daily_reset_at": {
                            "type": "string",
                            "format": "date-time"
                        }
                    },
                    "additionalProperties": true
                },
                "cache_hit": {
                    "type": "boolean"
                },
                "stale": {
                    "type": "boolean"
                }
            },
            "additionalProperties": true
        }
    }
}
```


## GET /api/v1/podcasts/{id}/keyword-rankings

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

Required permission `keywords:read`.

| Parameter | Location | Required | Definition |
|---|---|---|---|
| `id` | path | Yes | `{"type":"integer","minimum":1,"description":"Podstatus ID from GET /podcasts; this is not the Apple Podcasts ID."}` |
| `from` | query | No | `{"type":"string","description":"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."}` |
| `to` | query | No | `{"type":"string","description":"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."}` |
| `page` | query | No | `{"type":"integer","description":"Page number. Default: 1. The offset (page - 1) \u00d7 per_page must not exceed 10000.","minimum":1,"maximum":10000}` |
| `per_page` | query | No | `{"type":"integer","description":"Items per page. Default: 25.","minimum":1,"maximum":50}` |
| `platform` | query | No | `{"type":"string","description":"Chart provider. Use ranking-dimensions to find supported combinations.","enum":["apple","spotify"]}` |
| `country` | query | No | `{"type":"string","description":"Two-letter country code, case-insensitive. Chart support varies by platform.","maxLength":2,"minLength":2}` |

Example request (fictitious IDs)

```sh
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'
```

Example response (HTTP 200): Default response (fictitious values)

```json
{
    "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"
        }
    }
}
```

Successful response schema

```json
{
    "type": "object",
    "required": [
        "data",
        "meta"
    ],
    "properties": {
        "data": {
            "type": "array",
            "items": {
                "type": "object",
                "required": [
                    "keyword",
                    "keyword_id",
                    "platform",
                    "country",
                    "date",
                    "position"
                ],
                "properties": {
                    "keyword": {
                        "type": "string",
                        "nullable": true
                    },
                    "keyword_id": {
                        "type": "integer"
                    },
                    "platform": {
                        "type": "string",
                        "enum": [
                            "apple",
                            "spotify"
                        ]
                    },
                    "country": {
                        "type": "string"
                    },
                    "date": {
                        "type": "string",
                        "format": "date"
                    },
                    "position": {
                        "type": "integer"
                    }
                },
                "additionalProperties": true
            }
        },
        "meta": {
            "type": "object",
            "required": [
                "page",
                "per_page",
                "has_more"
            ],
            "properties": {
                "page": {
                    "type": "integer"
                },
                "per_page": {
                    "type": "integer"
                },
                "has_more": {
                    "type": "boolean"
                },
                "request_id": {
                    "type": "string",
                    "description": "REST diagnostic ID; MCP exposes this only in the X-Request-Id HTTP header."
                },
                "usage": {
                    "type": "object",
                    "required": [
                        "remaining",
                        "limits",
                        "minute_reset_at",
                        "daily_reset_at"
                    ],
                    "properties": {
                        "remaining": {
                            "type": "object",
                            "required": [
                                "minute",
                                "day"
                            ],
                            "properties": {
                                "minute": {
                                    "type": "integer",
                                    "minimum": 0
                                },
                                "day": {
                                    "type": "integer",
                                    "minimum": 0
                                }
                            },
                            "additionalProperties": true
                        },
                        "limits": {
                            "type": "object",
                            "required": [
                                "minute",
                                "day"
                            ],
                            "properties": {
                                "minute": {
                                    "type": "integer",
                                    "minimum": 0
                                },
                                "day": {
                                    "type": "integer",
                                    "minimum": 0
                                }
                            },
                            "additionalProperties": true
                        },
                        "minute_reset_at": {
                            "type": "string",
                            "format": "date-time"
                        },
                        "daily_reset_at": {
                            "type": "string",
                            "format": "date-time"
                        }
                    },
                    "additionalProperties": true
                },
                "cache_hit": {
                    "type": "boolean"
                },
                "stale": {
                    "type": "boolean"
                }
            },
            "additionalProperties": true
        }
    }
}
```


## GET /api/v1/podcasts/{id}/reports

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

Required permission `reports:read`.

| Parameter | Location | Required | Definition |
|---|---|---|---|
| `id` | path | Yes | `{"type":"integer","minimum":1,"description":"Podstatus ID from GET /podcasts; this is not the Apple Podcasts ID."}` |
| `page` | query | No | `{"type":"integer","description":"Page number. Default: 1. The offset (page - 1) \u00d7 per_page must not exceed 10000.","minimum":1,"maximum":10000}` |
| `per_page` | query | No | `{"type":"integer","description":"Items per page. Default: 25.","minimum":1,"maximum":50}` |
| `report_id` | query | No | `{"type":"integer","description":"Only return this report, if it belongs to the selected podcast.","minimum":1}` |

Example request (fictitious IDs)

```sh
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'
```

Example response (HTTP 200): Default response (fictitious values)

```json
{
    "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"
        }
    }
}
```

Example response (HTTP 200): Report processing (fictitious download link) (fictitious values)

```json
{
    "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"
        }
    }
}
```

Example response (HTTP 200): Report completed (fictitious download link) (fictitious values)

```json
{
    "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"
        }
    }
}
```

Example response (HTTP 200): Report failed (fictitious download link) (fictitious values)

```json
{
    "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"
        }
    }
}
```

Example response (HTTP 200): Report expired (fictitious download link) (fictitious values)

```json
{
    "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"
        }
    }
}
```

Successful response schema

```json
{
    "type": "object",
    "required": [
        "data",
        "meta"
    ],
    "properties": {
        "data": {
            "type": "array",
            "items": {
                "type": "object",
                "required": [
                    "id",
                    "podcast_id",
                    "status",
                    "period_type",
                    "period",
                    "partial_period",
                    "failure_code",
                    "credit_refunded",
                    "expires_at",
                    "expired",
                    "download_url"
                ],
                "properties": {
                    "id": {
                        "type": "integer"
                    },
                    "podcast_id": {
                        "type": "integer"
                    },
                    "status": {
                        "type": "string",
                        "enum": [
                            "queued",
                            "processing",
                            "completed",
                            "failed"
                        ]
                    },
                    "period_type": {
                        "type": "string",
                        "enum": [
                            "monthly",
                            "yearly"
                        ]
                    },
                    "period": {
                        "type": "string"
                    },
                    "partial_period": {
                        "type": "boolean",
                        "description": "The calendar period was unfinished when this report was requested; does not guarantee source completeness."
                    },
                    "failure_code": {
                        "type": "string",
                        "nullable": true,
                        "enum": [
                            null,
                            "report_generation_failed"
                        ]
                    },
                    "credit_refunded": {
                        "type": "boolean",
                        "nullable": true,
                        "description": "API refund recorded; null for reports without an API request receipt."
                    },
                    "expires_at": {
                        "type": "string",
                        "nullable": true,
                        "format": "date-time"
                    },
                    "expired": {
                        "type": "boolean"
                    },
                    "download_url": {
                        "type": "string",
                        "nullable": true
                    }
                },
                "additionalProperties": true
            }
        },
        "meta": {
            "type": "object",
            "required": [
                "page",
                "per_page",
                "has_more"
            ],
            "properties": {
                "page": {
                    "type": "integer"
                },
                "per_page": {
                    "type": "integer"
                },
                "has_more": {
                    "type": "boolean"
                },
                "request_id": {
                    "type": "string",
                    "description": "REST diagnostic ID; MCP exposes this only in the X-Request-Id HTTP header."
                },
                "usage": {
                    "type": "object",
                    "required": [
                        "remaining",
                        "limits",
                        "minute_reset_at",
                        "daily_reset_at"
                    ],
                    "properties": {
                        "remaining": {
                            "type": "object",
                            "required": [
                                "minute",
                                "day"
                            ],
                            "properties": {
                                "minute": {
                                    "type": "integer",
                                    "minimum": 0
                                },
                                "day": {
                                    "type": "integer",
                                    "minimum": 0
                                }
                            },
                            "additionalProperties": true
                        },
                        "limits": {
                            "type": "object",
                            "required": [
                                "minute",
                                "day"
                            ],
                            "properties": {
                                "minute": {
                                    "type": "integer",
                                    "minimum": 0
                                },
                                "day": {
                                    "type": "integer",
                                    "minimum": 0
                                }
                            },
                            "additionalProperties": true
                        },
                        "minute_reset_at": {
                            "type": "string",
                            "format": "date-time"
                        },
                        "daily_reset_at": {
                            "type": "string",
                            "format": "date-time"
                        }
                    },
                    "additionalProperties": true
                },
                "cache_hit": {
                    "type": "boolean"
                },
                "stale": {
                    "type": "boolean"
                }
            },
            "additionalProperties": true
        }
    }
}
```


## POST /api/v1/podcasts/{id}/reports

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`.

| Parameter | Location | Required | Definition |
|---|---|---|---|
| `id` | path | Yes | `{"type":"integer","minimum":1,"description":"Podstatus ID from GET /podcasts; this is not the Apple Podcasts ID."}` |
| `Idempotency-Key` | header | Yes | `{"type":"string","minLength":1,"maxLength":128,"pattern":"^[a-zA-Z0-9._-]+$"}` |

Request body schema

```json
{
    "type": "object",
    "required": [
        "period_type",
        "period"
    ],
    "properties": {
        "period_type": {
            "type": "string",
            "description": "Calendar period covered by the report.",
            "enum": [
                "monthly",
                "yearly"
            ]
        },
        "period": {
            "type": "string",
            "description": "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."
        },
        "options": {
            "type": "object",
            "additionalProperties": false,
            "properties": {
                "show_reviews": {
                    "type": "boolean",
                    "default": true
                },
                "show_apple_rankings": {
                    "type": "boolean",
                    "default": true
                },
                "show_spotify_rankings": {
                    "type": "boolean",
                    "default": true
                },
                "show_ivoox_rankings": {
                    "type": "boolean",
                    "default": true
                },
                "show_podcast_activity": {
                    "type": "boolean",
                    "default": true
                },
                "show_competitive_analysis": {
                    "type": "boolean",
                    "default": true
                },
                "show_seasonality_analysis": {
                    "type": "boolean",
                    "default": true
                },
                "show_sentiment_analysis": {
                    "type": "boolean",
                    "default": true
                },
                "show_geographic_opportunities": {
                    "type": "boolean",
                    "default": false
                }
            }
        }
    },
    "additionalProperties": false
}
```

Example request (fictitious IDs)

```sh
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'
```

Example response (HTTP 202): Default response (fictitious values)

```json
{
    "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"
        }
    }
}
```

Example response (HTTP 202): Report processing (fictitious download link) (fictitious values)

```json
{
    "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"
        }
    }
}
```

Example response (HTTP 200): Report completed (fictitious download link) (fictitious values)

```json
{
    "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"
        }
    }
}
```

Example response (HTTP 200): Report failed (fictitious download link) (fictitious values)

```json
{
    "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"
        }
    }
}
```

Example response (HTTP 200): Report expired (fictitious download link) (fictitious values)

```json
{
    "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"
        }
    }
}
```

Successful response schema

```json
{
    "type": "object",
    "required": [
        "data",
        "meta"
    ],
    "properties": {
        "data": {
            "type": "object",
            "required": [
                "id",
                "podcast_id",
                "status",
                "period_type",
                "period",
                "partial_period",
                "failure_code",
                "credit_refunded",
                "expires_at",
                "expired",
                "download_url"
            ],
            "properties": {
                "id": {
                    "type": "integer"
                },
                "podcast_id": {
                    "type": "integer"
                },
                "status": {
                    "type": "string",
                    "enum": [
                        "queued",
                        "processing",
                        "completed",
                        "failed"
                    ]
                },
                "period_type": {
                    "type": "string",
                    "enum": [
                        "monthly",
                        "yearly"
                    ]
                },
                "period": {
                    "type": "string"
                },
                "partial_period": {
                    "type": "boolean",
                    "description": "The calendar period was unfinished when this report was requested; does not guarantee source completeness."
                },
                "failure_code": {
                    "type": "string",
                    "nullable": true,
                    "enum": [
                        null,
                        "report_generation_failed"
                    ]
                },
                "credit_refunded": {
                    "type": "boolean",
                    "nullable": true,
                    "description": "API refund recorded; null for reports without an API request receipt."
                },
                "expires_at": {
                    "type": "string",
                    "nullable": true,
                    "format": "date-time"
                },
                "expired": {
                    "type": "boolean"
                },
                "download_url": {
                    "type": "string",
                    "nullable": true
                }
            },
            "additionalProperties": true
        },
        "meta": {
            "type": "object",
            "required": [
                "replayed"
            ],
            "properties": {
                "replayed": {
                    "type": "boolean"
                },
                "request_id": {
                    "type": "string",
                    "description": "REST diagnostic ID; MCP exposes this only in the X-Request-Id HTTP header."
                },
                "usage": {
                    "type": "object",
                    "required": [
                        "remaining",
                        "limits",
                        "minute_reset_at",
                        "daily_reset_at"
                    ],
                    "properties": {
                        "remaining": {
                            "type": "object",
                            "required": [
                                "minute",
                                "day"
                            ],
                            "properties": {
                                "minute": {
                                    "type": "integer",
                                    "minimum": 0
                                },
                                "day": {
                                    "type": "integer",
                                    "minimum": 0
                                }
                            },
                            "additionalProperties": true
                        },
                        "limits": {
                            "type": "object",
                            "required": [
                                "minute",
                                "day"
                            ],
                            "properties": {
                                "minute": {
                                    "type": "integer",
                                    "minimum": 0
                                },
                                "day": {
                                    "type": "integer",
                                    "minimum": 0
                                }
                            },
                            "additionalProperties": true
                        },
                        "minute_reset_at": {
                            "type": "string",
                            "format": "date-time"
                        },
                        "daily_reset_at": {
                            "type": "string",
                            "format": "date-time"
                        }
                    },
                    "additionalProperties": true
                },
                "cache_hit": {
                    "type": "boolean"
                },
                "stale": {
                    "type": "boolean"
                }
            },
            "additionalProperties": true
        }
    }
}
```
