Skip to main content
 

With RAD, podcasters can finally learn who's listening

NPR announced Remote Audio Data today: a technology standard for sending podcast audience analytics back to their publishers. Podcasting is one of the few truly decentralized publishing ecosystems left on the web, and it's a relief to see that this is as decentralized as it should be.

Moreover, it's exactly the role public media should be playing: they convened a group of interested parties and created an underlying open source layer that benefits everyone. One of the major issues in the podcast ecosystem is that nobody has good data about who's actually listening; most people use Apple's stats, look at their download numbers, and make inferences. This will change the game - and in a way that directly benefits podcast publishers rather than any single central gatekeeper.

What's not listed in the spec is a standard way to disclose to the listener that their analytics are being shared. This may fall afoul of GDPR and similar legislation if not handled properly; to be honest, I'd hope that any ethical podcast player would ask permission to send this information, giving me the opportunity to tell it not to. Still, at least in the five minutes that everyone isn't sending their listening data to be processed by Google Analytics, this is an order of magnitude better than using Apple as a clearinghouse.

Here's a quick technical overview of how it works:

While MP3 files mostly contain audio, they can also contain something called an ID3 tag for human-readable information like song title, album name, artist, and genre. RAD adds a JSON-encoded remoteAudioData field, which in turn contains two arrays: trackingUrls and events. It can also list a custom podcastId and episodeId. Events have an optional label and mandatory eventTime, expressed as hh:mm:ss.sss, and can have any number of other keys and values.

The example data from the spec looks like this:

{
 "remoteAudioData": {
   "podcastId":"510298",
   "episodeId":"497679856",
   "trackingUrls": [
     "https://tracking.publisher1.org/remote_audio_data",
     "https://tracking.publisher2.org/remote_audio_data",
     "https://tracking.publisherN.org/remote_audio_data",
   ],
   "events": [
     {
       "eventTime":"00:00:00.000",
       "label":"podcastDownload",
       "spId":"0",
       "creativeId":"0",
       "adPosition":"0",
       "eventNum":"0"
     },
     {
       "eventTime":"00:00:05.000",
       "label":"podcastStart",
       "spId":"0",
       "creativeId":"0",
       "adPosition":"0",
       "eventNum":"1"
     },
     {
       "eventTime":"00:05:00.000",
       "label":"breakStart",
       "spId":"123456",
       "creativeId":"1234567",
       "adPosition":"1",
       "eventNum":"2"
     },
     {
       "eventTime":"00:05:15.000",
       "label":"breakEnd",
       "spId":"123456",
       "creativeId":"1234567",
       "adPosition":"1",
       "eventNum":"3"
     }
   ]
 }
}

The podcast player sends a POST request to the URLs listed in trackingURLs, wrapped in a session ID and optionally containing the episodeId and podcastId. By default the player should send this at least once per hour, although the MP3 file can specify a different duration by including a submissionInterval parameter. The intention is that the podcast player stores events and can send them asynchronously, because podcasts are often listened to when there's no available internet connection. After a default of two weeks without sending, events are discarded.

Here's an example JSON string send to a reportingUrl from the spec:

{
 "audioSessions": [
   {
     "podcastId": "510313",
     "episodeId": "525083696",
     "sessionId": "A489C3AD-04AA-4B5F-8289-4D3D2CFE4CFB",
     "events": [
       {
         "sponsorId": "0",
         "creativeId": "0",
         "eventTime": "00:00:00.000",
         "adPosition": "0",
         "label": "podcastDownload",
         "eventNum": "0",
         "timestamp": "2018-10-24T11:23:07+04:00"
       },
       {
         "sponsorId": "0",
         "creativeId": "0",
         "eventTime": "00:00:05.000",
         "adPosition": "0",
         "label": "podcastStart",
         "eventNum": "1",
         "timestamp": "2018-10-24T11:23:08+04:00"
       },
       {
         "sponsorId": "111128",
         "eventTime": "00:00:05.000",
         "adPosition": "1",
         "label": "breakStart",
         "creativeId": "1111132",
         "eventNum": "2",
         "timestamp": "2018-10-24T11:23:09+04:00"
       },
       {
         "label": "breakEnd",
         "sponsorId": "111128",
         "eventTime": "00:00:05.000",
         "adPosition": "1",
         "creativeId": "1111132",
         "eventNum": "3",
         "timestamp": "2018-10-24T11:23:10+04:00"
         }
     ]
   },
   {
     "podcastId": "510314",
     "episodeId": "525083697",
     "sessionId": "778A4569-4B06-469B-8686-519C3B43C31F",
     "events": [
       {
         "sponsorId": "0",
         "eventTime": "00:00:00.000",
         "adPosition": "0",
         "creativeId": "0",
         "eventNum": "0",
         "timestamp": "2018-10-24T11:23:11+04:00"
       }
     ]
    },
   {
     "podcastId": "510315",
     "episodeId": "525083698",
     "sessionId": "F825BE2B-9759-438A-A67E-9C2D54874B4F",
     "events": [
       {
         "sponsorId": "0",
         "eventTime": "00:00:00.000",
         "adPosition": "0",
         "label": "podcastDownload",
         "creativeId": "0",
         "eventNum": "0",
         "timestamp": "2018-10-24T11:23:12+04:00"
       }
     ]
   }
 ]
}

It's a very simple, smart solution. There's more information at the RAD homepage, and mobile developers can grab Android or iOS SDKs on GitHub.