Skip to content

skymethod/spc

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

37 Commits
 
 
 
 
 
 
 
 

Repository files navigation

SPC

Standard Podcast Consumption (proposal)

Why

Podcasting is built on open technologies: allowing anyone to publish, and anyone to listen - supporting various companies on both sides, agreeing to nothing but the open standard that defines how podcasts are described.

Some podcast player apps keep track of various metrics happening on their app for each podcast, and make podcaster-facing portals available to help the podcaster see how their show is doing among listeners to that app (e.g. Apple Podcasts, Spotify). Many podcasters find this information extremely helpful, but these metrics (like "how many people follow this show", or "how many people actually hit play on this episode") are only known by each app, completely unavailable to the podcaster's usual server-side tools like hosting-company dashboards, which are based on the only thing the server-side can see: downloads.

While one-off app-specific portals are useful, they each define and report information in slightly different and incompatible ways, and require the podcaster to log into each platform or set up an unsupported custom integration based on scraping the portal content.

Since podcasting is based on open standards, there should be a simple and uniform way for podcast apps to securely make a show's aggregate client-side metrics available to the podcaster. After all, it's the podcaster's work that is powering their app's experience.

What

This document describes a simple, standard mechanism that any podcast app can use to securely make these client-side metrics available to each podcaster.

Important

These are aggregate metrics at the show/episode level, not at the listener level. This is not a new RAD, no listener IP addresses or proxies, just sums. In most cases, implementing this standard won't even require a change to an app's published policies whatsoever, since this aggregate information is likely already being collected and disclosed.

It's called Standard Podcast Consumption, or SPC, and can be implemented by podcast apps of any size.

How

The mechanism is simple, inspired by what the popular podcast app Overcast already does today, namely: include podcast-specific information inside the HTTP user-agent request header when the app's backend server fetches the podcast RSS feed. But instead of one metric (followers), a podcast-specific url to call back.

This elegant approach neatly solves the problem without needing complicated auth schemes. The information is transferred server-to-server, ensuring the information is seen only by the podcaster (or the podcaster's hosting company), and never includes any given podcast listener in the call flow - avoiding accidental listener IP leakage. It also does not require the app to make thousands of new outgoing fetch calls for every podcast, instead reusing the standard feed-level call that the app already makes.

To implement SPC, a podcast app needs to do three new things:

  • Generate a podcast-specific, short, unique, unguessable keystring (case-sensitive and alpha-numeric only) for each podcast that has reportable client consumption metrics. A random v4 uuid without the dashes is a good choice, perhaps stored as a new column in the backend podcast database table
  • Create and implement a new SPC API endpoint, hosted on a domain associated with the app. This simple HTTPS endpoint must respond to GET requests, with one or more q query params (keystrings), returning a standardized JSON response payload (see below) with metrics for the associated podcasts.
  • Include the podcast-specific endpoint url, prefixed by SPC/, anywhere inside the user-agent header when server-fetching a podcast’s RSS feed. An app called ExampleCast might send the following user-agent when server-fetching a feed for a podcast with associated keystring 5f71780061794eaa9e6c62fc967cb363:

ExampleCast/1.0.1 SPC/https://api.examplecast.com/spc?q=5f71780061794eaa9e6c62fc967cb363

Podcasters (or their hosting companies) can then parse these app-specific endpoints out of their logs and use them to query consumption metrics on an ongoing basis.

Note

This requires no feed-level changes on the server side, so can be incrementally adopted whenever is convenient for the app - no need to wait for podcast publishers to change their feed generation, and no need for podcast publishers to change their feeds at all. No new tags!

spc-diagram

New pieces needed to implement SPC (in red) vs existing infrastructure (gray)

Standard SPC API responses

At a high-level, every SPC API response is a standard UTF-8 encoded JSON object, consisting of a metrics result (or error) for every podcast specified in the query. The standard podcast consumption metrics are defined as:

  • Show-level follower¹ count: "how many people are following this show" ie Apple Podcast followers, Overcast subscribers, etc

  • Show-level all-time total listener² count

  • Episode-level daily listener² count

  • Episode-level all-time total listener² count

  • Episode-level listener² histogram, expressed as a segment resolution (e.g. 1m) and an array of per-resolution percentages of how many listeners listened to that segment

[1] a follower is defined as a single person, across devices, that has indicated interest in receiving special notifications/autodownloads of new episodes for a given show

[2] a listener is defined as a single person (across devices) that plays more than zero seconds of an episode


Example podcaster/hosting company request:

GET https://api.examplecast.com/spc?q=5f71780061794eaa9e6c62fc967cb363

Example podcast app SPC API endpoint response:

// HTTP 200
{
  "results": {
    "5f71780061794eaa9e6c62fc967cb363": {
      "asOf": "2025-04-06T17:46:29.476Z", // utc timestamp at which this data is considered comprehensive
      "followerCount": 1234,
      "totalListeners": 25340,
      "episodes": {
        "episode-guid-10": {
          "totalListeners": 12345,
          "listenerHistogramResolution": "1m", // optional: default is '1m'
          "listenerHistogram": [ 100, 90.5, 90.43, 90.43, /** ... for every minute of the episode */ ],
          "dailyListeners": {
            "2025-04-01": 1002,
            "2025-04-02": 920,
            "2025-04-02": 432,
            "2025-04-03": 200,
            "2025-04-04": 102
          }
        },
        "episode-guid-9": {
          "totalListeners": 18220,
          "listenerHistogram": [ 100, 90.4, 89,5, 72.3, /** ... for every minute of the episode */ ],
          "dailyListeners": {
            // ...
          }
        },
        //...(more episodes)
      }
    }
  }
}

Batch requests are possible by passing multiple keystrings for the same SPC API endpoint:

GET https://api.examplecast.com/spc?q=5f71780061794eaa9e6c62fc967cb363&q=93141cf56f7f4cf8a6eddcd519cd34e3

// HTTP 200
{
  "results": {
    "5f71780061794eaa9e6c62fc967cb363": {
      "asOf": "2025-04-06T17:46:29.476Z",
      // ... rest of result
    },
    "93141cf56f7f4cf8a6eddcd519cd34e3": {
      "asOf": "2025-04-06T20:11:37.743Z",
      // ... rest of result
    }
  }
}

Errors are returned using the 'error' property, and can be defined at the overall response-level for fatal problems...

GET https://api.examplecast.com/spc?q=

// HTTP 400
{
  "error": "Invalid query"
}

...or at the result level for partial successes to batch queries:

GET https://api.examplecast.com/spc?q=5f71780061794eaa9e6c62fc967cb363&q=93141cf56f7f4cf8a6eddcd519cd34eX

// HTTP 200 (or 207 for extra credit)
{
  "results": {
    "5f71780061794eaa9e6c62fc967cb363": {
      "asOf": "2025-04-06T17:46:29.476Z",
      // ... rest of result
    },
    "93141cf56f7f4cf8a6eddcd519cd34eX": {
      "error": "Unknown keystring"
    }
  }
}

All response payloads use the same format for simplicity. The fields are described in the examples above, and also more formally as a JSON Schema and Typescript type definition.

All metrics are highly encouraged, but optional. Leave any unimplemented/uncollected metrics out of the associated show-level or episode-level portions of the response.

FAQ

Do these numbers mean "downloads" are obsolete?

Not at all, these client-side metrics are self-reported by each app and should always be sanity-checked against the corresponding downloads. They are in addition to, not a replacement of the traditional download-based metrics, and should always be displayed to podcasters in separate charts - as to avoid comparing apples and oranges.

Does this mean these numbers will be public?

Not in the sense that anyone can see them by default, that should remain the choice of the podcaster. Although the API endpoint is public in the HTTP sense, keystrings should be unguessable and long enough to combat enumeration attacks. This is similar to how "private feeds" are implemented in podcast-land today. Once the SPC API url is received by the podcaster, they are free to share it with other services used for stats aggregation and display.

Where will this standard live once adopted?

This standard probably makes sense to live under a group or organization, like the OPAWG. I'd be willing to donate it there, this idea is freely available!

Get involved

If you work on a podcast app, and are planning on implementing SPC, leave a note over in the discussion area and I'll keep a list of supporting apps on this page.

About

Standard Podcast Consumption (proposal)

Resources

License

Stars

Watchers

Forks