RORK LABJP
PLAY — Google Play's target API level 36 requirement took effect yesterday, August 31. From today, new apps and updates must target Android 16VISIBILITY — Apps still on API 35 stay listed but disappear for users on newer Android versions. No error is raised; new installs simply fade, which makes the change easy to missEXTENSION — If you missed the deadline, an extension through November 1, 2026 can be requested in Play Console — best filed alongside a concrete migration planAPPLE — On the Apple side, the event lands September 9 and iOS 27 is reported to ship September 14. Testing generated apps on iOS 27 hardware before release week is time well spentEXPO — Expo released expo-paste-input on August 28, a native module that brings image, GIF, and sticker paste to React Native TextInputEAS — EAS Observe reached general availability on August 20, putting crash and performance monitoring on the same EAS platform as builds and updatesPLAY — Google Play's target API level 36 requirement took effect yesterday, August 31. From today, new apps and updates must target Android 16VISIBILITY — Apps still on API 35 stay listed but disappear for users on newer Android versions. No error is raised; new installs simply fade, which makes the change easy to missEXTENSION — If you missed the deadline, an extension through November 1, 2026 can be requested in Play Console — best filed alongside a concrete migration planAPPLE — On the Apple side, the event lands September 9 and iOS 27 is reported to ship September 14. Testing generated apps on iOS 27 hardware before release week is time well spentEXPO — Expo released expo-paste-input on August 28, a native module that brings image, GIF, and sticker paste to React Native TextInputEAS — EAS Observe reached general availability on August 20, putting crash and performance monitoring on the same EAS platform as builds and updates
Articles/Dev Tools
Dev Tools/2026-06-02Advanced

Measuring the Real eCPM Yourself: Piping Impression-Level Ad Revenue Into Your Own Analytics

The AdMob dashboard's average eCPM hides which regions and placements actually earn. Here is how I capture per-impression revenue with paidEventListener, normalize it, and pipe it into my own analytics to compute a real eCPM by network, placement, and country.

React Native234AdMob70Mediation7Revenue AnalyticsArchitecture22

Premium Article

This started when I opened the AdMob dashboard to retune the mediation setup for one of the six wallpaper apps I run in parallel — the one whose revenue had plateaued. The per-network average eCPMs were right there. AdMob high, AppLovin middling, Pangle low. So should I trust those numbers and lower Pangle's floor?

I hesitated. What the dashboard shows is "each network's average across every country and every placement, all blended together." My users are about 60% in Japan, with the rest scattered across North America and Southeast Asia. An interstitial and an app-open ad earn wildly different prices. If I look at the blended average, conclude "Pangle is low," and cut it, I might be emptying out exactly the Southeast Asian banner inventory that only Pangle was filling.

Across more than a decade of solo development since 2014, past fifty million cumulative downloads, ad revenue is the one area where I kept "staring at the dashboard and adjusting by gut." This article is the record of throwing that gut feeling away — of pulling per-impression revenue into my own analytics so I could compute a real eCPM by network, placement, and country.

Why the dashboard's average eCPM misleads you

eCPM is "revenue per 1,000 impressions." The dashboard aggregates this per network, per day. That is convenient, but it is far too coarse for deciding mediation priority or floors (minimum accepted prices).

There are three problems. First, regional differences disappear into the average. For the same banner slot, a North American impression often earns several times what a Japanese one does, and several times more than a Southeast Asian one. Second, placement differences disappear. An app-open ad and an article-footer banner command different prices because they capture different amounts of user attention. Third, only the mediation "winner" shows up in dashboard revenue, so the average can't tell you "if I cut this network, who picks up that inventory?"

In other words, I was trying to make a decision that lives in three dimensions — placement x country x network — using the one dimension the dashboard shows me, the per-network average. That mismatch was the root of the bad calls. What I needed was to record how much each individual impression actually earned, inside my own app.

What Impression-Level Ad Revenue returns

AdMob has a mechanism called Impression-Level Ad Revenue (ILRD). Every time an ad is shown, you receive an estimate of that single impression's revenue through an on-device callback. In react-native-google-mobile-ads, the paidEventListener on each ad object handles this.

The callback mainly returns: value (the revenue, but in micros — an integer that is the real amount times one million), a currency code such as "USD" or "JPY", a precision field (ESTIMATED / PUBLISHER_PROVIDED / PRECISE), and the mediation adapter information telling you which network won.

Here is the first trap. Because value is in micros, you only get the amount in currency units after dividing by 1,000,000. And the currency code can come back as USD or as JPY depending on your account settings and the bidding network. Get either of these wrong and your revenue is off by orders of magnitude. I did exactly that, as you'll see below.

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
Why the dashboard's average eCPM misleads mediation decisions, and how to capture per-impression revenue with the paidEventListener in react-native-google-mobile-ads (working code)
The micros-vs-currency mistake that put my revenue off by a factor of 1,000,000, plus the Before/After normalization that fixed it
How to aggregate a true eCPM by network x placement x country and feed it back into bid floors and mediation priority
Secure payment via Stripe · Cancel anytime

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $15 for lifetime access
View Membership →

Related Articles

Dev Tools2026-06-01
Managing Startup Time as a Budget: How I Deferred SDK Init Across Six Apps
Instead of optimizing startup ad hoc, I switched to allocating a per-phase time budget and defending it. Here's how I deferred SDK init across six Rork-built apps and added a CI gate that fails the build when the budget is exceeded.
Dev Tools2026-08-04
Exactly 20 Characters, Still Rejected: Putting Character Counting Behind One Boundary
Emoji and combining marks make your client and your server disagree about length. I measured four counting methods on Node v22 and found 75.6% of naive truncations split a grapheme and 36.1% produced replacement characters over UTF-8. Here is the shared-module design that fixed it.
Dev Tools2026-08-01
Every Experiment Kept Landing on the Same Devices: Hash Choice and Salt Position, Measured Across a Million IDs
On-device experiment assignment looked fine in isolation, then collapsed the moment two experiments ran side by side. Here is what one million IDs revealed about four hash functions, why the culprit was the concatenation order rather than hash quality, and the implementation I settled on, with the measured numbers.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →