●TOOLING — Rork's developer repos keep moving: rork-xcode was updated on July 16, rork-device on July 15, and rork-plist on July 13●OPUS46 — Claude Opus 4.6 is live in Rork, and Rork Max is built to assemble apps on top of Claude Code●SIM — A cloud iOS simulator runs in the browser, with one click to install on a device and two clicks to publish to the App Store●MAX — Rork Max emits pure Swift rather than React Native, reaching iPhone, iPad, Apple Watch, Apple TV, Vision Pro, and even iMessage●NATIVE — That opens up HealthKit, ARKit and LiDAR, NFC, Dynamic Island, Live Activities, 3D through Metal, and on-device inference with Core ML●SEED — Rork raised a $15M seed led by Left Lane Capital, with Peak XV and a16z Speedrun joining the round●TOOLING — Rork's developer repos keep moving: rork-xcode was updated on July 16, rork-device on July 15, and rork-plist on July 13●OPUS46 — Claude Opus 4.6 is live in Rork, and Rork Max is built to assemble apps on top of Claude Code●SIM — A cloud iOS simulator runs in the browser, with one click to install on a device and two clicks to publish to the App Store●MAX — Rork Max emits pure Swift rather than React Native, reaching iPhone, iPad, Apple Watch, Apple TV, Vision Pro, and even iMessage●NATIVE — That opens up HealthKit, ARKit and LiDAR, NFC, Dynamic Island, Live Activities, 3D through Metal, and on-device inference with Core ML●SEED — Rork raised a $15M seed led by Left Lane Capital, with Peak XV and a16z Speedrun joining the round
Two Weeks After Adding Pangle and Mintegral to My AdMob Waterfall — Notes on eCPM and Fill Rate
A record from a wallpaper app with over 50 million downloads: I added Pangle and Mintegral to my AdMob mediation, then compared two weeks of eCPM and fill-rate data. Bidding-and-waterfall coexistence, the effective-revenue breakdown, region-based groups, and a daily monitoring script — kept in a form you can actually operate.
One morning I opened my AdMob mediation report and noticed that one ad network's fill rate had slipped compared to the previous week. The eCPM itself was fine, but the share of requests that went unfilled had grown — meaning there were windows where I simply wasn't selling my inventory. So I finally tackled a task I'd been putting off: adding two new networks to the waterfall.
I'm Masaki Hirokawa, an artist and solo developer. I've been building wallpaper and relaxation apps on my own since 2014, and the cumulative downloads have passed 50 million. Ad monetization is the area I've touched most in recent years, so here I want to share the actual data from adding Pangle and Mintegral to my AdMob mediation and running them for about two weeks. There's no dramatic conclusion. But if you're weighing the same move, I hope these honest numbers help you guess how it might play out for your own app.
Why I Added Two Networks
My apps swing a lot in display value depending on region. During hours when Japanese users dominate, I can hold a high eCPM, but when the overseas share rises, my existing networks alone start dropping fill rate, and there are moments when no ad comes back for a request. To fill those gaps I picked Pangle, known for strong overseas inventory, and Mintegral, which carries heavy demand from game advertisers.
The reasoning was simple: both support AdMob's open bidding and the waterfall, so the cost of adding them is relatively low. Ever since 1997, when I first touched the internet at sixteen through self-taught programming, I've preferred the approach of "just connect it once and look at the real data." With ad networks too, I'd rather judge by my own app's numbers than by reputation.
What I Did in the Setup
The addition itself isn't hard, but there were three spots where it's easy to stumble. Here they are in order.
First, in the AdMob console you add each network to a mediation group. Bidding-capable networks go into the bidding slot, while waterfall-served ones get registered as manual eCPM rows. At first I pushed both into bidding and ended up losing Mintegral's waterfall inventory.
Next, you issue app IDs and ad unit IDs on each network's own console and paste them into the AdMob mapping. This is copy-and-paste work, but it's easy to mix up test and production, so I leave a one-line note before I start:
# mediation_mapping_memo.txt
app: WallpaperApp-iOS unit: interstitial_main
pangle: placement=YOUR_PANGLE_PLACEMENT_ID mode=bidding
mintegral: unit=YOUR_MINTEGRAL_UNIT_ID mode=waterfall floor=manual
# Check: grep for test IDs (t_xxx) leaking into prod before push
Finally, you add the SDKs. In a React Native–based app, you install each mediation adapter and then verify the native dependency resolution. On iOS you also need to add the SKAdNetwork identifiers to Info.plist; forget this and attribution gets dropped, which makes eCPM look lower than it really is. Reflect every identifier each network publishes, without omissions.
✦
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
✦How to design manual-eCPM floors when bidding and the waterfall coexist, and where to place each network
✦A breakdown formula and worked example for how a five-point fill-rate gain flows into effective revenue
✦A step-by-step for splitting mediation groups by region, plus code to watch daily eCPM and fill rate yourself
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.
The most common misunderstanding here is that bidding and the waterfall are not "one or the other" — they coexist inside the same group. AdMob mediation first runs a real-time auction across bidding-capable networks (and AdMob's own network), then compares the winning bid against each manual-eCPM row lined up in the waterfall. Whichever wins gets served.
That means the "manual eCPM" you set on each waterfall row isn't just an expected value — it acts as a floor that competes against the bidding result. Set it too high and that row is rarely called, contributing nothing to fill. Set it too low and you hand off inventory that could have sold higher. I start each floor around the median of that network's historical eCPM, then nudge it up or down weekly to watch how it responds.
The placement I eventually settled on looks roughly like this. Here are each network's supported modes and the role they play in my wallpaper app:
Network
Supported mode
My placement
How I set the manual-eCPM floor
AdMob Network
Bidding-equivalent
Always in the auction
No setting needed (automatic)
Pangle
Bidding + waterfall
Bidding-led
Waterfall row uses a low floor as backup
Mintegral
Bidding + waterfall
Waterfall-led, share dialed back
Slightly above the historical median; only picks up wins
The key point is that you may register the same network in both bidding and the waterfall. Even if it loses the bidding auction, it gets a second chance on its waterfall row. After putting Pangle into this two-tier setup, my overseas-hours fill grew even steadier.
A Formula for Deciding Between Fill Rate and eCPM
"If fill rate rose but eCPM stayed flat, did revenue actually grow?" is a fair question. Rather than answer by feel, breaking it into a simple formula removes a lot of doubt. I think of interstitial effective revenue like this:
effective revenue ≈ ad requests × fill rate × (show rate) × eCPM ÷ 1000
If you treat show rate (the share of filled ads that were actually displayed) as roughly constant, revenue scales with the product of fill rate and eCPM. That's precisely why revenue can grow even with flat eCPM, as long as fill rate rises. Here's my case, laid out in rounded relative values:
Metric
Two weeks before
Two weeks after
Delta
Fill rate
~92%
~97%
+5 points
Average eCPM (relative)
1.00
1.01
Nearly flat
Filled impressions (relative)
0.92
0.97
+~5.4%
Effective revenue (relative)
0.92
0.98
+~6.5%
The larger your impression base, the more those five points matter. In my case, total monthly revenue rose by a few percent. The judgment rule this yields is clear. If your fill rate is below 95%, adding a network to plug the fill hole has the better payoff first. If fill is already capped above 98%, shift your weight to eCPM levers instead (floor tuning, ad-format review, improving the ATT opt-in rate). Don't argue fill and eCPM on the same field — decide first which bottleneck you're standing on. That's the trick to not taking the long way around.
The Two-Week Data
Here's the heart of it. For interstitial ads, I'll share the rough trend comparing the two weeks before the addition with the two weeks after. The figures are relative to my own app, and they shift a lot with genre and user region, so please read them with that caveat.
The changes I observed were roughly as follows:
Overall fill rate: about 92% to about 97%. The previously missed time windows were reliably filled
Overall average eCPM: nearly flat (a slight plus). Unit value did not jump dramatically
Pangle: contributed to fill during hours with a high overseas share. Mid-level eCPM, with a steady impression of returning inventory
Mintegral: solid on fill, but for my wallpaper app its eCPM came in slightly below the existing networks during many hours
In other words, revenue didn't soar — the accurate way to put it is that "gaps where no ad had been showing got filled." A five-point fill-rate gain matters more the larger your impression base is. In my case, looking at total monthly revenue, it added up to a few percent of lift. Not dramatic, but for an improvement you get from a config change alone, it's meaningful enough.
Where I Hit Walls
One wall: for the first few days after adding a network, don't trust the data. New networks have a learning period, and for the first two or three days eCPM can read lower than its true potential. I almost judged on day-one numbers in my impatience, but once I averaged over a week, Pangle's standing rose. Cutting a network on short-term numbers means throwing away its upside.
The other wall is the effect of the ATT (App Tracking Transparency) opt-in rate. In environments where many users haven't granted consent, no network can serve personalized ads, and eCPM drops across the board. That's not about the merits of the added networks — it's about the opt-in rate as a precondition. If you're going to compare networks, it felt fairer to evaluate after the ATT opt-in rate has stabilized somewhat.
Splitting Mediation Groups by Region and Time
After evaluating everything in a single group, I re-cut the groups by region: deliver Pangle heavily during hours when the overseas share rises, and leave the hours with more Japanese users to my existing networks and AdMob bidding. Here's the procedure I used.
From AdMob's "Mediation," create a new mediation group and assign the target ad unit. Duplicating an existing group is fast because it carries over the mapping.
In the group's "Targeting," specify the region (countries/areas). I split into "overseas-facing" and "domestic-facing" groups. The domestic one puts high-eCPM networks up top; the overseas one lowers Pangle's floor to prioritize fill.
When both groups reference the same ad unit, AdMob automatically decides which group to use based on the device's region. There's no duplicate delivery.
After changing anything, leave it untouched for at least one week, ideally two. Poking floors repeatedly during the learning period makes it impossible to tell which change did what.
Weekly, compare eCPM, fill rate, and impressions per group, and fine-tune each region's network ordering and floors.
With this split, the overseas-hours fill hole shifted from "gets filled" to "gets claimed first." What I find interesting is that the same network changes role just by changing where you place it.
Watching Daily eCPM and Fill Rate Yourself
Opening the console every morning doesn't last. I run a setup that pulls the prior day's per-network eCPM and fill rate through AdMob's Reporting API and pings Slack when a threshold is breached. Here's the skeleton, assuming you've already authenticated with a service account.
// admob_daily_watch.mjs — pull yesterday's per-network eCPM / fill rate and watch thresholdsimport { google } from 'googleapis';const auth = new google.auth.GoogleAuth({ keyFile: 'YOUR_SERVICE_ACCOUNT_JSON', scopes: ['https://www.googleapis.com/auth/admob.report'],});const admob = google.admob({ version: 'v1', auth });const PUBLISHER_ID = 'pub-YOUR_PUBLISHER_ID';const FILL_ALERT = 0.95; // notify if fill rate drops below thisfunction yesterday() { const d = new Date(Date.now() - 24 * 60 * 60 * 1000); return { year: d.getFullYear(), month: d.getMonth() + 1, day: d.getDate() };}const res = await admob.accounts.mediationReport.generate({ parent: `accounts/${PUBLISHER_ID}`, requestBody: { reportSpec: { dateRange: { startDate: yesterday(), endDate: yesterday() }, dimensions: ['AD_SOURCE'], metrics: ['ESTIMATED_EARNINGS', 'IMPRESSIONS', 'MATCH_RATE', 'IMPRESSION_RPM'], }, },});for (const row of res.data) { const r = row.row; if (!r) continue; const source = r.dimensionValues.AD_SOURCE?.displayLabel ?? 'unknown'; const matchRate = Number(r.metricValues.MATCH_RATE?.doubleValue ?? 0); const rpm = Number(r.metricValues.IMPRESSION_RPM?.microsValue ?? 0) / 1_000_000; const line = `${source}: fill=${(matchRate * 100).toFixed(1)}% eCPM≈${rpm.toFixed(2)}`; if (matchRate > 0 && matchRate < FILL_ALERT) { await notifySlack(`⚠️ fill-rate drop ${line}`); // wire up your own webhook } console.log(line);}
MATCH_RATE is the fill rate and IMPRESSION_RPM corresponds to effective eCPM. Lining up yesterday's per-network values lets you spot which network suddenly sank first thing in the morning. It's the plainest, most effective move for preventing the "turned out I'd been missing fill for a week" opening of this article.
Pitfalls to Clear Before You Add
So I don't repeat the same stumbles, here are the points I check every time.
Did I add the SKAdNetwork identifiers to Info.plist? Forget them and attribution drops, making eCPM read low and leading to bad judgment.
Are test IDs (prefixes like t_) leaking into the production mapping? I confirm mechanically with grep before push.
Is the waterfall's manual eCPM floor too high? If a row has never won once, lower the floor or remove the row.
Am I concluding from the first two or three days? Factor in the learning period and evaluate over at least a week.
Has the ATT opt-in rate moved sharply lately? A shifting precondition makes network comparison unfair.
My Current Judgment
After two weeks, the view that settled in me is this: "filling fill-rate holes" and "lifting eCPM" are separate goals worth treating separately. Pangle clearly helped with the former. Mintegral contributed little to the latter in my wallpaper app's genre, so I run it with its delivery share dialed back. With the same two networks, the result could easily flip for game or utility apps.
Network reputation is a useful reference, but in the end your numbers are decided by your own app's regional mix, genre, and display frequency. That's exactly why I keep to the practice of not leaving it on the shelf — connect it once and collect two weeks of data.
What I'll Work on Next
Next, I plan to split mediation groups even more finely by time and region and test whether I can weight Pangle's delivery share toward the hours when the overseas ratio climbs. Splitting groups by region should make each network's strong moments far more visible.
If you're similarly torn over whether to add a new network, start with just one and connect it for two weeks to an app with a hole in its fill rate. The material for your decision is best drawn from the numbers in your own hands, not from reputation. Thank you for reading.
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.