Eng brief · Growth ↔ Engineering

Meta DPA & the catalog crawler

Why app.tryhoma.com/property/* is getting hammered (RapidAPI + OpenAI spend, UX latency), what Meta's own docs say about it, and the clean fix. It's not a rogue campaign — it's Meta's dynamic-ads catalog crawler doing exactly what it's designed to do, pointed at our most expensive route.

Prepared for Federico / Evan / Valeriia · sources linked at the bottom · companion: the Commerce catalog & its crawlers →

TL;DR — the fix

  1. The traffic is Meta's meta-externalads/1.1 crawler validating our catalog's property URLs. It's standard DPA behavior, not a misconfigured ad — it re-checks each listing's landing page for availability / price / link health.
  2. It hurts us specifically because every /property/{id} render fires a live RapidAPI/AnyProp lookup + OpenAI call. For most advertisers a catalog crawl hits cheap static pages; for us each hit burns real quota + CPU.
  3. Fix: detect meta-externalads/1.1 on the property routes and serve it a cached / lightweight response — skip the live RapidAPI + OpenAI calls. Validation still succeeds, ads stay healthy, cost → ~0. Cache-by-user-agent; do not block (blocking breaks DPA delivery).
  4. Stopgap already in place (Growth side): catalog feed auto-refresh is paused, which removed the big feed-driven bursts. The residual autonomous crawl remains — that's what the cache is for.

1What's actually hitting us

Meta re-fetches the landing URL of every catalog item to keep dynamic ads accurate — real-estate listings sell fast, so it re-validates constantly (availability, price, broken-link/policy checks, Open-Graph/microdata, image resolution, and pixel-ID↔catalog matching). Normal, documented behavior for any Advantage+ catalog.

The volume, measured from our own analytics (Meta-datacenter IPs = clean crawler proxy):

~15/day
steady baseline crawler users (autonomous validation)
3,577
Jul 29 — a catalog rebuild triggered a full re-crawl (~150k property fetches)
1,055
Jul 27 — a feed refresh event

So there are two triggers: (a) feed re-ingestion — every scheduled refresh / manual rebuild makes Meta re-crawl the ~16k product URLs in a burst (the spikes above); and (b) autonomous validation — Meta re-checks active items on its own cadence regardless of the feed (the ~15/day trickle). (a) is now paused. (b) can't be switched off while the catalog + ads exist — hence the cache.

2Meta's crawlers (from their docs)

Meta runs five distinct crawlers, each with its own user-agent. Identify ours by UA:

User-agentPurpose (per Meta)
meta-externalads/1.1 OURSEnhances advertising & business products — this is the DPA / catalog validation crawler hitting the property routes
facebookexternalhit/1.1Link previews / shared-content metadata
meta-webindexer/1.1AI search result quality
meta-externalagent/1.1Training foundation AI models
meta-externalfetcher/1.1Agentic AI tasks (website navigation)

Full UA string for ours looks like:

meta-externalads/1.1 (+https://developers.facebook.com/docs/sharing/webmasters/crawler)

3How Meta says to control it

Don't block it. A Disallow on the property routes for meta-externalads would stop legitimate catalog validation — Meta would start flagging listings as broken/unavailable and could throttle DPA delivery. We want the crawler to succeed cheaply, not fail.

4Recommended fix

Handle meta-externalads/1.1 explicitly on the property (and any catalog-linked) routes so it validates without touching the expensive read path:

// pseudo — property route handler
const ua = req.headers['user-agent'] || '';
if (ua.includes('meta-externalads') || ua.includes('facebookexternalhit')) {
  // serve cached/lightweight property data ONLY:
  //   - skip live RapidAPI / AnyProp lookup
  //   - skip OpenAI call
  //   - return last-known cached fields + OG/microdata tags
  //     (title, price, availability, image) so Meta can validate
  return renderCachedProperty(id);   // cheap, no third-party calls
}
return renderProperty(id);           // full live path for real users
Why this is the right lever: it keeps dynamic ads accurate (the crawler still gets valid availability/price/image to validate against), it costs ~$0 per crawl, and it works against the autonomous validation crawl that we can't turn off. This is what the Local-Storage / cached-read-path work already targets — this just makes sure the Meta crawler hits the cache, not live reads.

If a cache isn't ready immediately, a per-UA rate limit / 304 Not-Modified on those routes is a reasonable interim (serve Last-Modified so Meta short-circuits re-fetches).

5Current state & what's left

6Sources (Meta developer docs)

Meta web crawlers — user-agents, purposes, robots.txt controldevelopers.facebook.com/docs/sharing/webmasters/web-crawlers Dynamic Ads for Real Estate — catalog setup, partial upload, HOUSING categorydevelopers.facebook.com/docs/marketing-api/dynamic-ads-for-real-estate Product catalog — feeds, data sources, Advantage+ catalog adsdevelopers.facebook.com/docs/marketing-api/catalog