I built a workflow that finds small US Shopify stores, checks their site for real problems, and drafts a personalised email to each one about what it found. Here’s how the “finding the right stores” part works, and the things that bit me along the way.
How it finds stores worth contacting
- Search local retail. Each run takes a mix of US cities and store types (boutiques, candles, jewelry, pet stores, home decor…) and pulls businesses with a website from a places API.
- Keep only Shopify stores. For each website it requests
/products.json. Only Shopify stores answer it, so this one request confirms the platform and gives you catalog data for free: how many photos each product has, how long the descriptions are, how much is sold out, and when a product was last added. - Check the homepage for real gaps, like:
- No Meta pixel or Google tag (they can’t measure or retarget any ads)
- No email marketing tool (no Klaviyo, Omnisend, Mailchimp and so on)
- No reviews app, thin product photos, missing descriptions
- An old legacy theme, no mobile viewport tag, a footer still showing 2022
- Score and rank. Each problem has a weight, stores below a threshold are dropped as “too healthy to pitch”, and the worst-off stores come first.
- Pick a real contact. It looks at the homepage and
/pages/contact, and only keeps emails on the store’s own domain (or a Gmail/Yahoo address the store itself shows). It skips noreply, press, careers and app-vendor addresses. - Claude writes the email around the 1–2 biggest problems it found, which get logged to Google Sheets (also used to skip anyone already contacted). Everything is saved as a Gmail draft, and nothing sends without me reviewing it.
What I learned building it
- “No pixel found” can be wrong. Shopify can load the Meta pixel through its Web Pixels system or Google Tag Manager, so it never appears in the page HTML. I had to check for those too, or I’d have been telling stores they had no pixel when they did.
- Don’t trust common words as signals. “popup” shows up in almost every theme, so it says nothing about whether a store does email marketing. Only real tool names count.
- Filter out stores you can’t help. Chains (anything with a store locator) and stores still behind a password page get skipped straight away.
- Hedge the claims. A scanner can be wrong, so the emails say “from what I can see” or “I couldn’t find” rather than stating things as fact. It’s more honest and it reads better.
- Missed schedule triggers can duplicate everything. When n8n restarted, several missed runs fired at the same moment and created duplicate drafts. Setting
N8N_CONCURRENCY_PRODUCTION_LIMIT=1makes them queue up instead. - Rotate your searches. Back-to-back runs were searching the same city and store type and finding nothing new. Picking the searches based on the time fixed it.
- Google credentials expiring every 7 days turned out to be my Google Cloud app still being in “Testing” mode. Publishing it fixed that.
- Self-hosting on a small machine: execution history grew the database to over 800 MB and n8n started running out of memory. Turning on execution pruning and not saving successful runs kept it small.
Happy to answer questions about any part of it. If you want something similar set up for your niche, feel free to DM me.
