App Stack Teardowns · Issue 1 · Android
BuzzFeed App
Google ad server (AdMob or GAM, same SDK), three SDK-level demand adapters all wired for bidding, four declared ad formats, OneTrust consent running TCF v2 and GPP in parallel. Static analysis of the production build plus a runtime capture, cross-checked against BuzzFeed's public 10-K.
The numbers at a glance
Mediation
SDK adapters
3
all bidding
Programmatic share
76%
of ad revenue (10-K)
Sticky refresh
10s
faster than typical
Amazon share
28%
of total revenue (10-K)
Supply chain
75/100
Beamflow health
›Methodology
- Build:
- Publicly distributed production build, captured 2026-05-11
- Static analysis:
- SDK detection + configuration audit of the unpacked build artifact
- Runtime capture:
- Real Android device, US location, screen mirror via scrcpy
- Supply chain audit:
- ads.txt + sellers.json crawl via Beamflow.co (view the live scan)
1. The app at a glance
The BuzzFeed Android app (com.buzzfeed.android) sits on the Play Store with 10m+ installs (play store). The product is a feed-and-article reader with a Shopping tab and a Quizzes tab, classified under news and entertainment content. Revenue comes from two paths: programmatic and direct-sold display on the editorial surfaces, and commerce affiliate links (mostly Amazon) on the Shopping tab. No in-app purchase was detected in the build.
The build runs the Google Mobile Ads SDK (serving AdMob or GAM, same SDK). OneTrust handles consent across IAB TCF v2 and GPP. Branch handles mobile attribution. Firebase covers Analytics, Crashlytics, and Performance, with Google Analytics alongside it. Privacy Sandbox Attribution Reporting is turned on, which most publishers have not done yet. No in-app purchase prompt or paywall appeared in any of the captured surfaces.
Reading the public 10-K against the build
BuzzFeed, Inc. (BZFD) filed its 2025 Form 10-K on 2026-03-16. Four numbers from that filing set the context for the rest of this teardown:
$185.3M
total revenue for fiscal 2025, down 2% year over year. The breakdown below is how that $185M splits across ads, content, and commerce.
76%
of total ad revenue is programmatic ($69.6M of $91.7M). Direct-sold is shrinking 25% YoY while programmatic grew 7%.
28%
of total company revenue comes from Amazon alone (~$52M), primarily affiliate commerce. The buy-button on Shopping pages is the single biggest revenue surface.
30%
of total revenue is commerce affiliate ($56.5M of $185.3M). Bigger than content revenue. Comparable to half of advertising revenue.
One thing to hold from those numbers. A stack that is now 76% programmatic on the ad side is unusually sensitive to format mix and auction tuning, which is why the format-gap finding in Section 8 is the most likely place yield is leaking. The Amazon affiliate share is shown here for context only. The teardown focuses on the ad stack, which is the $91.7M side of the P&L and what static analysis of the build can actually see.
Source: BuzzFeed, Inc. Form 10-K, fiscal year ended December 31, 2025. Filed via SEC EDGAR. All figures in this block are taken directly from the filing.
2. The stack at a glance
One mediation surface (the Google Mobile Ads SDK, which can be wired to either AdMob or GAM, both running the same client library), 3 SDK-level demand adapters (detailed in Section 3), and 4 declared ad format types (banner, native, interstitial, rewarded), of which only banner and native were observed firing during the runtime capture. The next sections walk through adapters, placements, peers, and supply chain in order.
3. SDK adapters detected
Three SDK-level adapters are wired into the build. Each adapter is a gateway into a wider demand pool. The buzzfeed.com app-ads.txt declares hundreds of paths reached through them, so the "3" here counts integration surfaces, not buyers in the auction.
| Adapter | Role in stack | Routes to | Bidding |
|---|---|---|---|
| Google (AdMob / GAM) | Mediation + demand | AdMob mediation networks, GAM Open Bidding, Google AdX | Active |
| Meta Audience Network | In-app demand | Meta's owned demand pool (Facebook + Instagram advertisers) | Active |
| Amazon APS | Header-bidding adapter | Amazon TAM bidders + Amazon's own DSP demand | Active |
4. Ad placements in context
BuzzFeed declares four ad formats in the SDK surface (Banner, Native, Interstitial, Rewarded), all served through the Google ad server. The runtime capture covered home feed, category feeds, articles, listicles, the Shopping tab, and the Quizzes tab. Each surface runs a different refresh timing and creative mix.
Declared in the SDK
| Surface | Top sticky | In-body | Refresh timing |
|---|---|---|---|
| Home feed | None | Display or native (full-card) | No refresh |
| Category feed | None | Display banner | No refresh |
| Article | 320×50 display banner | Display MREC (likely expandable), video, or native | 10s sticky · 30s in-article |
| Listicle | 320×50 display banner | Display MREC or native | 10s sticky · 30s per item |
| Shopping | 320×50 display banner | Display MREC + Amazon affiliate buy-button | 10s sticky · no in-body refresh |
| Quizzes | 320×50 display banner | Display MREC (sometimes native-styled) | 10s sticky · 30s in-quiz |
Density and refresh
Article, Shopping, and Quizzes pages share the same two-slot pattern: a sticky banner anchored at the top of the screen, and a larger in-body unit (MREC, native, or video). The top sticky refreshes every 10 seconds, the in-body unit every 30 seconds. Both refresh on a timer, with or without scroll.
The typical content-app pattern is 30 seconds on the sticky and 30-60 seconds on the in-body unit. Sub-15-second refresh tends to draw advertiser viewability complaints because the next refresh fires before the previous impression has accumulated MRC-viewable time. Google's policy guidance discourages it for display without setting a fixed minimum. BuzzFeed's 10-second sticky refresh runs roughly 3x faster than the content-app norm.
Home and category feeds run no top sticky and the in-feed banners do not refresh during the session. The Shopping article surfaces also expose Amazon affiliate buy-buttons inline with display creative. That is the commerce revenue path running in parallel with the ad path.
Not observedNo interstitials, no rewarded video, no app-open. Each is a format declared in the SDK (rewarded) or available to add (interstitial, app-open) but left unused in the build. See Section 8 Finding #1 for the yield implication.
Home feedIn-feed adIn-feed ad slot
A full-card ad slot between editorial story cards in the Latest feed. The creative captured here happens to be an app install (Dollin), but on a programmatic slot the publisher does not pick the creative. Anything that wins the auction fills it. The slot could be display or native at the format level; from a screenshot alone you can't tell which. The same creative persisted across scroll-and-return sessions, so feed banners are running with refresh disabled.
ShoppingTop stickySticky banner on Shopping
A 320x50-class sticky banner pinned to the top of the Shopping tab landing page. Refreshes every 10 seconds throughout the session. The same slot served Clorox, JCPenney, and an All Day Portable Breastmilk creative across consecutive refreshes.
ShoppingAffiliate + adAffiliate button + display ad on Shopping
Three monetization surfaces visible in one viewport on a Shopping article: a JCPenney sticky banner at the top, an Amazon affiliate buy-button (orange CTA) on the product card, and a Bottle Washer Pro display ad below. The affiliate revenue path runs alongside the display ad path on every Shopping article.
ArticleIn-article MRECTop sticky + expandable in-article ad
The standard article ad pattern at its largest fill: a Sensodyne Pronamel sticky banner anchored at the top (10-second refresh) and a large in-article Homes.com creative that fills most of the viewport. The size and behavior fit an expandable display unit (Google supports these as a standard format) where a base MREC expands to a larger creative on serve. The in-body unit refreshes every 30 seconds even when the reader does not scroll.
ArticleVideo adVideo ad served by Google
A video-in-banner creative served by Google demand into the in-article slot. Autoplay with sound off by default, unmute toggle, and a Learn more CTA pinned below the player. Many content publishers block video creative in display slots. BuzzFeed has allowed it.
ArticleIn-article MRECDifferent article, same pattern
A second article (Anne Hathaway) shows the same two-slot pattern with different fills: All Day Portable Breastmilk on the top sticky and an Original Farmers Market brand creative in the in-article MREC. Refresh timing was confirmed against this surface by watching the slots tick over without scrolling.
QuizzesIn-article MRECSticky + large native on Quizzes
Quizzes surface with the same top-sticky pattern (YouTube TV creative refreshing every 10 seconds) plus a large green-themed Learning Tree native ad embedded between quiz question modules. The in-quiz unit refreshes every 30 seconds.
ArticleIn-article MRECPremium-brand demand on the same slot
Worth noting that the same article ad slots also fill with premium-brand creative. Here a Huawei Watch Fit 5 sticky banner on top and a Boston University LLM Program in-article native unit. The slot is format-agnostic. Demand mix swings with each auction.
5. Peer comparison
How BuzzFeed's stack measures against the typical Tier-1 English-language news app. Direct peer rows will be added as future issues of this series teardown comparable apps.
| App | Mediation | Partners | Formats | CMP | MMP |
|---|---|---|---|---|---|
| BuzzFeed | Google ad server (AdMob or GAM, same SDK) | 3 SDK adapters | 1 of 4 firing | OneTrust + TCF + GPP | Branch |
| Typical Tier-1 EN news app | AdMob or AppLovin MAX | 3-5 SDK adapters | 2-3 of 4 firing | OneTrust or Sourcepoint | AppsFlyer or Branch |
Industry-default row is the typical pattern observed across Tier-1 English-language news apps, not a single source.
6. Supply chain audit
View the live scan →Audit of both the web-side ads.txt and the mobile-side app-ads.txt for buzzfeed.com, cross-checked against each declared SSP's sellers.json file via Beamflow's live scan for buzzfeed.com (full disclosure: Beamflow is my supply-chain monitoring product). Both files share the same root domain authority under the IAB Tech Lab spec, so the entries below cover the full supply path declared for BuzzFeed's web and app inventory. Server response: HTTPS, 406ms response, 2 redirects, text/plain.
909 clean · 413 issues
1,322 total entries
sellers.json Verification50% weight
C 80%
Supply Chain Completeness15% weight
F 10%
Syntax Health10% weight
A 99%
Duplicate Detection10% weight
B 88%
File Health15% weight
A 100%
File-level warnings
- ·Missing recommended "contact" variable
- ·High duplicate rate: 167 duplicate records found
SSP verification · 1,322 ads.txt entries checked
1,037
Verified by SSP sellers.json (78%)
257
Unverified (201 not found, 56 DIRECT/RESELLER mismatches)
Buyers cannot verify 257 of 1,322 entries, so some of that supply is probably being bid lower or skipped entirely.
Top problem SSPs
| SSP | Entries | Issues |
|---|---|---|
| freewheel.tvNow FreeWheel | 22 | 22 |
| google.com | 52 | 17 |
| conversantmedia.com | 26 | 16 |
Operator read
A health score of 75 on a file this size (1,322 entries) is middle-of-the-pack for a Tier-1 publisher. The expensive number is the 257 entries unverified by SSP sellers.json (20% of the file). On a property doing BuzzFeed-scale traffic, even a 2-4% bid skip on those paths is real money. The 167 duplicate records are cosmetic by comparison, but worth deduping on the next release. The Supply Chain Completeness F grade comes from the gap between the ads.txt declarations and what the upstream sellers.json files actually confirm.
7. What this app gets right
Architectural and configuration choices in the BuzzFeed build worth copying or benchmarking against.
- 1.
Ad density is restrained
Across feed, articles, and listicles, the placement rhythm reads as one ad every few editorial cards rather than every card. None of the captured screens felt aggressive or disruptive to read flow. Density is the dial most teams overshoot on a content app. BuzzFeed has resisted the easy yield bump.
- 2.
Single ad-unit architecture
One Ad Unit ID surface in the build, covering all four declared formats. A centralized AdManager-style abstraction is the right pattern for content apps with many surfaces. Most teams ship one ad-unit per screen first, regret it, and refactor to this. BuzzFeed got there.
- 3.
Full TCF v2 + GPP consent stack
OneTrust running both IAB TCF v2 for Europe and IAB GPP for US state-level signals. Few publishers run both. For a property monetizing across the EU/UK and the US state-privacy patchwork, this is what good looks like.
- 4.
Privacy Sandbox Attribution enabled
The team has opted into Privacy Sandbox Attribution Reporting. Most publishers have not. Worth doing now even if the actual attribution data path is still maturing, because the cost of opting in later (after IDFA/cookie deprecation is fully reflected in spend) is higher than the cost of doing it now.
- 5.
All adapters bidding-enabled
No waterfall remnants in the build. Google (via AdMob or GAM), Meta Audience Network, and Amazon APS are all wired for in-app bidding rather than legacy waterfall mediation. For a stack that is 76% programmatic, this is the right architecture: unified auction pressure beats ranked-list waterfalls on every metric except integration complexity.
8. Findings and open questions
Observations and open questions about the stack, ordered by estimated yield impact.
- 1.
Google mediation with three SDK adapters, all wired for bidding
The build runs Google's Mobile Ads SDK as the single mediation surface, with three SDK-level demand adapters underneath: Google (via AdMob or GAM), Meta Audience Network, and Amazon APS. All three are configured for in-app bidding rather than legacy waterfall mediation. The architecture is clean. The count of three describes integration surfaces, not the number of buyers in the auction. The buzzfeed.com app-ads.txt declares hundreds of paths reached through those three adapters. Google's adapter alone fans out via AdMob mediation networks and GAM Open Bidding, and Amazon APS is a header-bidding gateway into Amazon TAM. The question worth asking on a stack like this is whether the demand inside each adapter is fully enabled and tuned: which Open Bidding partners are turned on inside GAM, which TAM partners are turned on inside APS, how floors and timeouts are set per adapter. A static teardown can see the front door of the stack. It cannot see what is happening inside it.
- 2.
Refresh timing runs faster than typical for content apps
10-second refresh on the top sticky and 30-second refresh on the in-article unit, both on a timer regardless of scroll. Typical content-app pattern is 30 seconds on the sticky and 30 to 60 seconds on the in-body unit. Sub-15-second refresh tends to draw advertiser viewability complaints because the next refresh fires before the previous impression has accumulated MRC-viewable time, and Google's policy guidance discourages it for display ads without setting a fixed minimum. The trade-off cuts both ways: more refreshes mean more inventory, but lower viewable rate pulls CPMs down. Worth A/B-testing 15s and 30s against the current 10s.
- 3.
Rewarded video declared but firing path unknown
Rewarded is declared in the SDK surface but was not observed firing during the live session covering home feed, articles, and listicles. Two plausible triggers are quiz-content gates and premium-article paywalls. A targeted capture on the Quizzes tab would settle whether this is a real yield line or an orphan SDK declaration. Interstitial and app-open formats are also unused in the captured surfaces.
- 4.
20% of ads.txt entries are unverified by SSP sellers.json
Beamflow's audit shows 257 entries on buzzfeed.com cannot be verified against the SSP's own sellers.json (201 not found plus 56 DIRECT/RESELLER mismatches). Buyers either skip those paths or bid them lower. On a property at BuzzFeed's traffic, that is meaningful spend leaking. See the full audit in Section 6.
9. What an operator should take from this
- ·A static teardown counts SDK adapters, not auction participants. Three SDK integrations can route to hundreds of demand paths via AdMob mediation, GAM Open Bidding, and Amazon TAM. The question worth asking on any in-app stack is whether the demand inside each adapter is fully enabled and tuned. Answering that takes bid-log access, not a build inspection.
- ·Test your sticky refresh interval against the 30-second norm before assuming 10 seconds is winning. Faster refresh raises inventory but lowers viewable rate and CPMs. You have to test where the break-even sits on your own inventory.
- ·Centralized ad-loading (one Ad Unit ID, many formats) is the right pattern for content apps with many surfaces. Most teams ship one ad-unit per screen first, regret it, and refactor to this. BuzzFeed got there.
- ·If you monetize across Europe and the US state-law patchwork, run TCF v2 and GPP in parallel. OneTrust supports it. Getting consent wrong costs much more than getting it right once.
- ·Audit ads.txt against SSP sellers.json on every release. A 20% unverified rate is common in the wild. On a Tier-1 property, even a low-single-digit bid skip on those paths is real revenue.
- ·Declared SDK formats that do not fire in capture are worth chasing down. They are often orphan integrations left in the build, but they can also fire on a surface you did not record. Confirm either way before drawing conclusions about format mix.
Related from MonetizationGuy
Running a similar stack?
If your app has a comparable mediation setup and you want a full audit (not just static analysis, but a real walk-through of your live waterfall, demand, and supply chain), the free 30-minute call is the starting point.
Book a Free 30-Min Call