← Blog
2026-01-06 · 8 min read

Finding your ICP inside 153,000 stores: the filter stack that beats spray-and-pray

Blueprint diagram of a store catalogue narrowed by platform, country, catalog and traffic filters into a qualified shortlist

Every store database gets used the same wrong way on day one. Someone picks a platform, picks a country, hits export, and walks away with twelve thousand rows. Six weeks later they conclude that outbound is dead because a one percent reply rate turned out to be the ceiling. It was the ceiling — for that list. The list was never a list of prospects. It was a list of stores.

A filter stack is a different object. It is an ordered set of constraints, each one defensible out loud, arranged so that the last constraint you add is the one that makes your offer obvious. Applied properly to a population of 153,515 verified stores, a good stack lands you somewhere between eighty and four hundred rows. That number is not a failure of the tool. That number is the entire point.

Your ICP is a price lever, not a marketing exercise

The most useful thing anyone can tell you about targeting is that it sets your price before you ever open your mouth. The same service, pitched to a badly matched segment, gets negotiated down in three steps — and then the prospect ghosts anyway. The same service, pitched to a segment where the problem is expensive and already visible on their dashboard, gets accepted at the first number you say. Nothing changed in the deck. What changed was who was reading it.

So treat price resistance as a diagnostic signal about fit rather than a copywriting problem. If you keep hearing “that’s a lot for us”, you are not underselling. You are aiming at businesses for whom your outcome is not worth what it costs you to deliver. That is a filter problem, and filters are cheaper to fix than positioning.

This is why the ICP work has to happen before the copy work, not after. Writing brilliant email to a segment that structurally cannot afford you is the most expensive way to learn a lesson you could have learned in an afternoon with a query builder.

Start with the floor, not the filters

Before you touch a single field, do the arithmetic in the other direction. If your engagement is €2,000 a month and you justify it on incremental revenue, the store has to be big enough that a plausible lift covers you several times over. A merchant doing a few thousand euros a month cannot pay you out of a twenty percent improvement, no matter how good you are. Write down the smallest store for whom your offer is arithmetically sane. That is your floor.

There is no revenue field in a store database, and you should be suspicious of anyone who claims otherwise. What you have are proxies, and good proxies are enough: monthlyTraffic from real Semrush worldwide data on 118,324 stores, productCount, organicKeywords on 80,593 stores, the number of detected paymentMethods, whether shippingCarriers look like a real logistics setup or a single default. A store with 40,000 monthly visits, 900 SKUs, three payment methods and two named carriers is running a business. A store with 300 visits and 12 SKUs is running an experiment.

Express the floor as a query, not as a feeling. Something like: monthly traffic above a threshold you chose deliberately, product count above the point where merchandising becomes a job, at least one signal of operational maturity. Every subsequent filter narrows this population. None of them should be allowed to reintroduce stores below the floor.

Blueprint diagram of a stepped funnel whose steps are labelled platform, country, traffic floor and app absent, narrowing to a neck marked 80 to 400 stores above a long line marked the floor.
The width of each step is how much that constraint cuts, not how much it matters.

The eleven columns that replace your gut feeling

Build a table with one row per ICP and these columns: vertical, geography, platform, catalog size band, traffic band, organic dependency, tech stack present, tech stack absent, local footprint, language and currency, buying signal. Fill it in for exactly one offer. If you sell two things, you get two tables — not one table with hedged cells.

A filled-in example, so this stays concrete: skincare and cosmetics, France and Belgium, Shopify, 150 to 1,200 SKUs, 20,000 to 200,000 monthly visits, organic keyword count high relative to traffic (they rank but do not convert the ranking), a review app detected in the apps field, no email or SMS automation detected, matched Google Business profile with a rating below 4.2, French language and EUR currency, review volume growing but unanswered. Every one of those cells maps to a field you can query. None of them is a vibe.

The column people skip is “tech stack absent”, and it is usually the strongest one in the table. Which brings us to the part that separates operators from list-buyers.

Exclusion filters are where the money is

Everyone builds inclusion filters. Platform equals Shopify. Country equals France. So does every other agency emailing that store this week, which is why the merchant has learned to delete on sight. Exclusion is what makes your list uncorrelated with everyone else’s.

Blueprint diagram of a Shopify circle overlapping a France circle, their shared lens marked inclusion filters, with a hatched crescent marked no email automation drawn away to a rectangle marked your segment.
The lens is what every agency exports this week. The crescent is the slice you can price freely.

The pattern that works: find stores that have already proved they spend money on one thing, and are visibly missing the adjacent thing. Detected analytics and tag management but no marketing automation. A subscription app but no loyalty app. A high organic keyword count but flat or falling organic traffic in the trend. A matched Google Business profile with hundreds of photos but no claim on the listing. AI mentions in the aiMentions field but zero aiCitedPages, meaning models talk about the brand without ever linking to it. Each of those absences is a sentence you can write in an email that no merge tag can fake.

Add one permanent exclusion to every stack: everyone you have already contacted. Duplicate outreach is worse than no outreach — it is the single clearest proof that you are running a machine and not paying attention. Keep a master list of contacted domains and subtract it before every export.

Segment hard, but stop before absurdity

There is a failure mode on the other side. Start with a broad segment of twenty-two thousand companies, add a role filter, add a country, add a technology exclusion, add a size band, and you can land on a hundred and eighty-six results. That feels like precision. It is usually over-fitting: you have described your last good client rather than a market, and you will exhaust the segment in one campaign.

The practical rule is that segmentation should be tight, but not so tight that the segment cannot sustain repeated campaigns. Rank your filters by how much each one cuts. Apply the cheap, high-cut ones first (platform, country, traffic floor). Apply the expensive, low-cut ones last (a specific app absent, a specific GMB condition). If you overshoot, remove the last constraint you added rather than loosening the floor — the floor is the one that protects your price.

Aim for a target band and check yourself against it. Eighty to four hundred stores per campaign is the range where you can still do real per-store work on every single row. Below eighty, you are one bad week away from having nobody left to email. Above four hundred, you will quietly stop personalizing around row ninety.

Count before you search

Every filter stack should be built with counts, not exports. Ask how many stores match, adjust, ask again. This costs almost nothing and it stops you from burning a search allowance discovering that your five-filter combination returns three rows. It also forces you to feel the shape of the population: you learn that a platform-plus-country cut is enormous, that adding a traffic floor halves it, that adding a missing-app condition halves it again.

Blueprint diagram of a tight three-node loop marked count, adjust and count again, with a single arrow passing through a gate marked commit into a large empty rectangle marked records.
You should go round the loop a dozen times before anything crosses the gate — counts are free, records are not.

This is also where an MCP-connected agent earns its keep. Handing an assistant the tools and telling it to “count French Shopify skincare stores with more than 150 products and no email automation detected, then vary the traffic floor until the count is between 150 and 300” is a two-minute job that would take you twenty in a UI. The agent iterates on counts; you approve the final query; only then do you pull records.

The discipline generalizes. Cheap operation to explore, expensive operation to commit. Count, then search. Search, then enrich. Enrich, then write.

Small lists, named properly, deduped forever

Fifty to three hundred well-chosen stores will out-earn a thousand loosely chosen ones, and it is not close. The reason is mechanical rather than mystical: a tight list lets every send carry something true and specific, and specific mail gets answered. A loose list forces you back onto generic copy, and generic copy is what the merchant has been trained to ignore.

Name every list so that the filter stack is legible six weeks later. A convention like vertical / platform / exclusion / size band / date reads as “skincare / shopify / no-email-automation / 150-1200-sku / 2026-01-06”. When a campaign performs, you can read the name and know what to build next. When it fails, you can read the name and know which constraint to change. Unnamed lists produce unrepeatable results.

Keep the lists in your own store, not only in your sending tool. Domains contacted, date, campaign, outcome. This is the asset that compounds. In year two, the ability to say “we have never emailed this store, and the last time we emailed one like it, forty percent replied” is worth more than any individual campaign.

Turning the stack into a repeatable object

The final step is to stop treating a filter stack as a one-off query and start treating it as a saved definition with an owner. Write it down as a parameterized search: the fixed constraints, the two or three you intend to vary, and the floor that never moves. Run it monthly. The population is not static — stores install apps, traffic trends bend, Google Business profiles get claimed — so the same stack surfaces new rows every cycle.

Then run one variation at a time. Same offer, same sequence, one filter changed: WooCommerce instead of Shopify, or the Netherlands instead of France, or a traffic band one step higher. Changing two things at once produces a result you cannot attribute, which means you learn nothing and have to run it again.

The compounding is in the stack, not in the volume. Most people scale outbound by sending more. The ones who make it work scale by narrowing until the offer is undeniable, then repeating the narrowing across adjacent segments. Sixty distinct platforms and 153,515 stores is a very large number of adjacent segments.

Put this playbook to work

153,000+ verified ecommerce stores, searchable by your AI agent. Plans from €49/mo.

Get your API key