Podstats
Overview Calendar Audience Account Method Admin

Document of methodology

How Podstats turns CDN access logs into the download and audience figures shown elsewhere in this application, written against the IAB Tech Lab Podcast Measurement Technical Guidelines v2.2.

Revised 3 September 2026 · Podstats 0.4.5

Compliance status

Podstats is not an IAB-certified measurement. Certification requires assessment by IAB Tech Lab and appearance on the compliant-vendor list, and Podstats has not been through it. The accurate description of what is documented here is aligned with IAB v2.2: the guideline is followed where the available data allows it, and every place it does not is named on this page.

Three requirements of the guideline cannot be met at all with the log data available. They are listed under Byte-range data rather than left implicit.

Source data

The only input is the CDN access log for the bucket the audio files are served from — W3C tab-separated files written by DigitalOcean Spaces and read from object storage every thirty minutes. Each file declares its own column order in a #Fields: directive and columns are resolved by name, so a change to the log format does not silently shift a field.

A log line is considered at all only if it is a GET for an .mp3 object that returned HTTP 200 or 206. HEAD requests, 304 responses and every non-audio object are discarded before any measurement takes place.

Episode metadata — title, publication date, duration and enclosure size — comes from each show's RSS feed, refreshed hourly. Podstats fetches the first ten bytes of each episode once to measure its ID3 tag; that request identifies itself as podstats in the user agent and is dropped by this platform's own filters, so it can never be counted as a download.

Measurement window

§5.4.1 permits either a fixed or a rolling 24-hour window and requires the choice be disclosed. Podstats uses a fixed 24-hour window: the calendar day in Australia/Melbourne, including daylight-saving transitions, which makes one day of the year 23 hours long and another 25.

Every date shown anywhere in the application is a Melbourne day. Requests are logged in UTC and converted on the way in. A listener's requests that straddle Melbourne midnight are two sessions and can therefore count as two downloads, which is the accepted consequence of a fixed window.

What counts as a download

Requests are grouped into a session keyed on the day, the episode, the requesting IP address and the user agent, and the bytes delivered across that session are summed. Play, pause and resume therefore produce one download, not three. A session counts as a download once its delivered bytes reach the episode's threshold, and counts once only.

The threshold is the one required by §5.4.3 — the file header plus one minute of content — derived per episode rather than set globally:

header + (size − header) × 60 ÷ duration

header is the episode's measured ID3v2 tag length, read from the tag's own declared synchsafe size. size is the enclosure length published in the feed and duration its published runtime, which together recover the real encoded bitrate, so a spoken-word episode at 64 kb/s and a music episode at 320 kb/s each get their own bar.

Two fallbacks apply, both of which raise the bar rather than lower it in the direction that would inflate figures:

· An episode whose feed publishes no usable duration or size falls back to a flat threshold of 1,048,576 bytes — roughly one minute at 128 kb/s. Today 729 of 4,366 episodes are in this state.

· Only ID3v2 tags are recognised. A file with an ID3v1 trailer, a bare MP3, or one whose header could not be read is recorded as having no measurable header, and its threshold is one minute of content with nothing added.

An episode whose whole file is smaller than one minute's worth of its own bitrate still counts on full delivery. The inputs to this calculation are monitored: each feed sync compares every episode's implied bitrate against the show's median and files a finding in the admin activity log when episodes fall outside half to double it, or when episodes stop publishing usable durations.

Byte-range data — a disclosed substitute approach

§5.4.3 requires that where byte-range data is unavailable, the approach used instead is disclosed. Podstats has no byte-range data. The CDN's log format declares sc-range-start and sc-range-end in its field list but leaves them, and every column after them, empty on every line. This is a property of the log source and cannot be corrected inside Podstats.

Three consequences follow, and none of them is worked around:

· The one-minute rule is applied to session totals, not to the delivered range. A session assembled entirely from trailing-byte requests could in principle reach the threshold without the first minute of audio ever being served.

· Two-byte range probes are not filtered. §5.4.2 Step 1.2(7) requires excluding Range: 0-1 requests; without the range columns they are indistinguishable from any other small 206. Their contribution is bounded and small: the smallest 206 responses observed are around 968 bytes against a threshold of 1–2.4 MB, under a tenth of a percent.

· Partial requests are reassembled by byte total alone. Overlapping ranges — the same audio delivered twice within a session — are counted twice toward the threshold, which can only make a download qualify sooner than the strict reading of the guideline allows.

Invalid traffic filtering

§5.4.2 Step 1.2 sets out eight filters. All eight are applied, with the one exception noted above for two-byte ranges and the one noted below for referrer:

1. Publisher infrastructure. An operator-configured list of addresses and CIDR blocks is dropped at scan time. It is empty by default, so it protects nothing until the publisher's own origin addresses are supplied.

2. Unrealistic volume. An address is capped at 100 counted sessions per day. Bytes still accumulate past the cap so completion remains measurable, but no further download is counted, and the address is added to the permanent exclusion list.

3. Data centres and hosting. Each address is resolved to an autonomous system using DB-IP's free ASN database, refreshed monthly. Requests from AS organisations matching a hosting keyword list, or from AS numbers the operator has listed, are dropped. The free database publishes only the organisation name, so this is a floor on data-centre traffic rather than a complete account of it.

4. Referrer. This filter is deliberately not applied. §5.4.2 Step 1.2(4) is conditional on the platform supplying a referrer; podcast clients fetching an enclosure send none, so nearly every genuine download arrives without one. Filtering on a missing referrer here would remove the audience rather than the invalid traffic. The referrer is retained as an attribute where a client does send one, and is never used to exclude.

5. Malformed user agents. Strings shorter than 3 or longer than 512 bytes, strings carrying a control character, strings with no letter in them, and the placeholders clients leave when substitution fails (null, undefined, unknown, n/a and similar) are dropped. Merely unfamiliar agents are not — they are counted and reported as "Other", so that an unrecognised real client is never silently deleted.

6. Self-identifying bots. Agents declaring themselves as crawlers, spiders, scrapers, monitors or command-line fetchers are dropped. The token bot is matched on a word boundary so that device names containing it are not caught.

7. Two-byte ranges. Not possible; see above.

8. Apple Watch. Dropped per §8.1, in both the atc/ + watchOS and the (null)/(null) + watchOS forms.

Bot list provenance. §5.3 names the IAB/ABC International Spiders and Bots List as the source for general IVT filtering. That list is licensed and is not available to this project, so the bot lists here are hand-maintained and reviewed against observed traffic rather than sourced from it. The date of the last review is shown on the admin page beside the table of unnamed agents, which is also where new bot strings observed here are identified for submission back to IAB Tech Lab.

Aborted transfers. §5.4.2 Step 1.3 asks for an explicit decision on platform quirks such as Akamai's HTTP 000 and nginx's 499, both of which mean the client closed the connection. These do not count. The CDN records no delivered byte total for an aborted transfer, so the one-minute threshold cannot be applied to it. The number discarded is logged per log object rather than dropped in silence.

Inclusion list and revalidation

§5.4.2 permits an inclusion list for legitimately dense addresses — large corporate NATs, carrier CGNAT — so that real listeners are not lost to the volume filter. Podstats keeps one. A listed address is never capped, is never added to the exclusion list, and a listing also overrides an exclusion the volume filter reached earlier, which is how a wrongly excluded network is let back in. The list is maintained by hand by the operator.

§5.4.2 requires any inclusion list be re-validated at least every 90 days with a record retained. A weekly job writes one entry to the admin activity log naming how many listed addresses were checked and which are past 90 days, so the review is on record and stale entries surface for the operator to re-confirm or remove.

Listeners and unique identification

§6.2 defines a Listener as a unique combination of IP address and user agent within a stated time frame. Podstats diverges from that definition and this is the most consequential departure on this page.

A listener is identified by the IP address combined with the classified platform name — "Apple Podcasts", "Spotify", "Android app" — rather than the raw user agent string. The reason is that agent strings carry app and OS version numbers: keyed on the raw string, every iOS release or podcast-app update would retire the entire audience and mint it again as new listeners, making returning-listener, growth, loyalty and retention figures meaningless.

The cost of that choice is an undercount. Two people behind one IP address using the same podcast app are one listener to Podstats and two to the strict definition. Every audience figure in this application — listeners, growth, loyalty, retention — carries this deviation. Download counts do not: downloads are keyed on the full user agent.

IPv6 addresses are truncated to their first 64 bits before any of this, per §5.4.4, since the host portion is frequently randomised per connection. IPv4 addresses are used whole.

Identifiers are hashed with SHA-256 and stored truncated; no raw address is retained in the aggregate tables. The download session fingerprint is truncated to 128 bits. The listener identifier is truncated to 64 bits, and the residual collision risk is disclosed rather than removed: widening it would change every existing key and count the entire returning audience as new, breaking the continuity of the series it exists to measure. At the current scale — tens of thousands of listeners — the expected number of collisions is far below one, but it grows quadratically and is not monitored.

Anomaly detection and change management

§5.4.6 requires anomalous traffic to be investigated as part of ongoing filtering, and §8.2 asks for a defined threshold, a warning mechanism and a stated course of action for mass-market technology change.

Threshold. After every ingest cycle the most recent complete day's downloads are compared against the median of the fourteen days before it. A divergence beyond a factor of two in either direction raises a finding. The median rather than the mean, so that one already-anomalous day cannot pull the baseline toward itself and mask the next.

Warning mechanism. Findings are written to the activity log on the admin page, alongside the threshold-drift findings described above. Nothing is auto-corrected — a finding marks a day as worth investigating, it does not judge validity.

Course of action on a finding, in order: confirm the day against the raw log objects on the admin reconciliation panel; check whether the movement is concentrated in one platform, which is the signature of a client or OS release, or in a small number of addresses, which is the signature of invalid traffic; for invalid traffic, add the addresses to the exclusion list, which also retracts what they have already contributed; for a genuine platform change — a client rewrite that alters range behaviour, an OS release that changes the user agent — add or amend the classifier rule, record the date of the change on this page, and treat the affected days as a series discontinuity rather than restating them.

Unnamed agents are surfaced on the admin page with their hit counts, so a new mass-market client appears as a named row rather than as unexplained growth in "Other".

Retroactive correction

§5.4.6 Step 5 requires invalid traffic found after the fact to be removed from current metrics as well as excluded going forward. When an address is added to the exclusion list, its already counted downloads are subtracted from the daily and hourly totals and its listener rows deleted, and the retraction is recorded in the activity log with the address and the number of downloads removed.

Two limits apply, and both are disclosed rather than fixed:

· Reach is bounded by session retention, currently three days. Older sessions are rolled into aggregate completion data where they can no longer be attributed to an address, so a retraction cannot reach them.

· Hourly shape can be approximate. The hour in which a session crossed the threshold is not stored, so a retraction removes from the busiest hours first. The day total after a retraction is exact; the distribution within the day can be off by an hour.

Metric definitions

Every metric below is reported at publisher level (all shows), show level and episode level, on the same definition at each.

Downloads. Sessions that reached their episode's threshold, after all filtering above. One session counts once regardless of how many requests it took.

Listeners. Unique listener identifiers seen in the period, on the IP + platform keying described above rather than the §6.2 IP + user agent definition. Where a period longer than a day is shown, a listener present on several days counts once.

New and returning listeners, growth, loyalty, retention. All derived from the same listener identifier, and all carrying the same deviation.

Platform and country. Platform is the classified user agent. Country is resolved from the IP address using DB-IP's free city database, refreshed monthly, and is approximate — VPN and carrier routing both misattribute.

Completion — non-standard. This is not an IAB-defined metric and must not be read as one. It buckets sessions by the proportion of the file's bytes they pulled, and is restricted to Android app and Web clients, which stream progressively. Clients that bulk-download the whole file — Apple Podcasts and most iOS apps among them — would register as 100% complete whether the episode was played or not, so including them would make the figure meaningless. It measures bytes delivered, never listening.

Ad metrics are not measured. §6.3 defines Ad Delivered and Client-Confirmed Ad Play. Podstats measures content downloads only; neither figure is produced, estimated or implied anywhere in this application.

Self-audit

§5.4.6 requires a self-audit at least twice a year, reported in this document. The audit is scheduled for March and September, and covers, at minimum: a reconciliation of raw log lines through to counted downloads for a sample period; a review of the hand-maintained bot lists against the unnamed-agent table; a review of the inclusion and exclusion lists; a check that the per-episode thresholds still match the feeds' current encoding; and confirmation that this page still describes what the code does.

Results. September 2026 — first audit; no prior period to compare against. The filtering, threshold, correction and disclosure controls described on this page were implemented and verified during this period, and the figures before their deployment were produced without them: filtering is applied at ingest and is not retroactive, so figures predating deployment are higher than the same traffic would produce today and are not comparable across that boundary.

Summary of divergences

Every departure from IAB v2.2 stated above, in one place:

· Listener is keyed on IP + platform, not IP + user agent (§6.2). Undercounts; affects all audience figures.

· No byte-range data available, so the one-minute rule is applied to session totals, two-byte probes are not filtered, and overlapping ranges are counted twice toward the threshold (§5.4.3, §5.4.2 Step 1.2(7)).

· Referrer filtering is deliberately not applied, because podcast clients do not send one (§5.4.2 Step 1.2(4)).

· Bot lists are hand-maintained, not sourced from the licensed IAB/ABC Spiders and Bots List (§5.3).

· Data-centre detection uses a free ASN database that publishes organisation names only, so it is a floor rather than a complete account (§5.4.2 Step 1.2(3)).

· Retroactive correction reaches three days back, and redistributes within a day approximately (§5.4.6 Step 5).

· The listener identifier is a 64-bit truncated hash, with collision risk unmonitored (§5.4.4).

· Completion is reported and is not an IAB-defined metric; no ad metrics are produced (§6.3).