RORK LABJP
TOOLING — Rork's developer repos keep moving: rork-xcode was updated on July 16, rork-device on July 15, and rork-plist on July 13OPUS46 — Claude Opus 4.6 is live in Rork, and Rork Max is built to assemble apps on top of Claude CodeSIM — A cloud iOS simulator runs in the browser, with one click to install on a device and two clicks to publish to the App StoreMAX — Rork Max emits pure Swift rather than React Native, reaching iPhone, iPad, Apple Watch, Apple TV, Vision Pro, and even iMessageNATIVE — That opens up HealthKit, ARKit and LiDAR, NFC, Dynamic Island, Live Activities, 3D through Metal, and on-device inference with Core MLSEED — Rork raised a $15M seed led by Left Lane Capital, with Peak XV and a16z Speedrun joining the roundTOOLING — Rork's developer repos keep moving: rork-xcode was updated on July 16, rork-device on July 15, and rork-plist on July 13OPUS46 — Claude Opus 4.6 is live in Rork, and Rork Max is built to assemble apps on top of Claude CodeSIM — A cloud iOS simulator runs in the browser, with one click to install on a device and two clicks to publish to the App StoreMAX — Rork Max emits pure Swift rather than React Native, reaching iPhone, iPad, Apple Watch, Apple TV, Vision Pro, and even iMessageNATIVE — That opens up HealthKit, ARKit and LiDAR, NFC, Dynamic Island, Live Activities, 3D through Metal, and on-device inference with Core MLSEED — Rork raised a $15M seed led by Left Lane Capital, with Peak XV and a16z Speedrun joining the round
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 Native209AdMob70Mediation7Revenue AnalyticsArchitecture17

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 $10 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-07-14
Designing Seams That Survive AI Regeneration in Rork
Every follow-up prompt to Rork can quietly wipe out logic you wrote by hand. Protecting it with prompts is a patch, not a fix. Here is how to separate generated code from code you own, and draw a boundary that regeneration cannot reach, with working Zustand and service-layer examples.
Dev Tools2026-07-08
When the Device Runs Out of Space, What Should Your App Protect?
Design Expo/React Native apps that assume writes can fail. Free-space budgets, LRU reclaim, data-protection tiers, and telemetry that surfaces silent failures — drawn from running six wallpaper apps.
📚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 →