Inspiration
If you run a brand, an affiliate program or a PPC account, one question keeps coming up: who is bidding on my brand name in Google Ads, and where are they sending that traffic? Google publishes every advertiser's ads in its official public ad archive (the Ads Transparency Center), but the archive is built for looking up one advertiser at a time. You can't start from your own domain and ask "who advertises to this?", and you can't see how long an ad has been running. PPCEye turns that archive around so you can.
What it does
- Domain lookup: enter a domain and see every advertiser whose Google search ads land on it. This is how you spot affiliates, resellers and look-alike sites bidding on a brand.
- Advertiser lookup: see all of an advertiser's ad creatives, every landing domain it sends traffic to, and first-seen / last-seen dates for each ad.
- Run-duration tiers (new, 30+, 90+, 180+, 365+ days): ads that keep running for months are the ones that pay, so you can study the winners first.
- Rankings of the most active advertisers and of the domains with the most advertisers competing on them.
- Keyword metrics: search volume and competition for each domain's brand term.
- AI labels: every landing domain gets an industry and a risk label.
- English and Chinese interface.
How we built it
- A crawler pulls search-network image creatives from the archive and stores them in MariaDB.
- Image ads don't carry a clickable URL, so a vision model (via OpenRouter, with DashScope and Baidu OCR as fallbacks) reads each ad image to extract the landing page it points to.
tldextractthen resolves every creative to exactly one root domain. - A language model visits each landing domain and writes an industry / risk classification.
- Keyword volume comes from the Google Ads Keyword Planner API.
- The web app is Python + Flask behind nginx and Cloudflare, with a Redis Streams priority queue feeding background workers for fetching, OCR and analysis.
Challenges we ran into
- Scale: a handful of giant domains return more than a million creatives per day, so no date window is ever small enough. We stopped crawling those at the domain level and show the stored inventory instead.
- Long jobs vs. queue reclaim: Redis Streams only reset a message's idle time on delivery, so a 10-hour crawl got re-claimed and re-run by every worker in turn. Fixed with a heartbeat that keeps renewing the lease.
- Free vs. paid without cloaking: detail pages are readable by everyone, crawlers included, with identical HTML. Totals and samples are free; full lists and the newest 30 days are paid.
What we learned
Real ads beat modelled keyword guesses for this job. Run duration turned out to be the most useful single signal, and the number of landing domains per advertiser is the quickest way to tell an agency or affiliate network from a brand advertising itself.
Try it at https://ppceye.com: a sampled view needs no account, and signing up gives a 3-hour full-access trial.
Built With
- cloudflare
- flask
- google-ads
- mariadb
- nginx
- openrouter
- python
- redis
Log in or sign up for Devpost to join the conversation.