DEV Community

Cover image for How to Find Shopify Stores by Installed App (Klaviyo, Recharge…) — the free way
Pavel Bragin
Pavel Bragin

Posted on

How to Find Shopify Stores by Installed App (Klaviyo, Recharge…) — the free way

If you sell anything to Shopify merchants — an app, an agency service, a 3PL — the hardest part isn't the pitch. It's building a list of the right stores: the ones a certain size, in a certain country, running (or not running) a certain app.

Most "Shopify leads" lists just dump domains on you. Here's how to do it with a scalpel instead — using only public data, no login, no paid database.

The trick: Shopify stores expose themselves

Every Shopify storefront serves two unauthenticated JSON endpoints to the open web:

  • https://<domain>/meta.json — store identity and size signals
  • https://<domain>/products.json — the full catalogue

Let's look at a real store:

curl -s https://kith.com/meta.json | python3 -m json.tool
Enter fullscreen mode Exit fullscreen mode
{
  "name": "Kith",
  "city": "Brooklyn",
  "country": "US",
  "currency": "USD",
  "myshopify_domain": "kithnyc.myshopify.com",
  "published_products_count": 25001,
  "published_collections_count": 411
}
Enter fullscreen mode Exit fullscreen mode

That published_products_count is gold. It's a live size signal — a proxy for how established the store is. Sorting or filtering by it is the difference between "10,000 random domains" and "stores big enough to afford what I sell."

Detecting the tech stack

Apps a store runs leave fingerprints in the storefront HTML. Want every store on Klaviyo? Their script tag is right there:

import httpx, re

APPS = {
    "klaviyo": "Klaviyo", "recharge": "Recharge", "gorgias": "Gorgias",
    "yotpo": "Yotpo", "judge.me": "Judge.me", "loox": "Loox",
    "okendo": "Okendo", "attentive": "Attentive",
}

def profile(domain: str) -> dict | None:
    meta = httpx.get(f"https://{domain}/meta.json", timeout=20)
    if meta.status_code != 200 or "myshopify_domain" not in meta.text:
        return None                      # not a live Shopify store
    m = meta.json()
    html = httpx.get(f"https://{domain}/", timeout=20).text.lower()
    apps = sorted({name for key, name in APPS.items() if key in html})
    return {
        "domain": domain,
        "name": m.get("name"),
        "country": m.get("country"),
        "products": m.get("published_products_count"),
        "apps": apps,
    }

print(profile("kith.com"))
# {'domain': 'kith.com', 'name': 'Kith', 'country': 'US',
#  'products': 25001, 'apps': ['Gorgias', 'Klaviyo']}
Enter fullscreen mode Exit fullscreen mode

Now you can filter: keep only US stores with 50+ products that run Klaviyo. That's a targeted prospect list, not a firehose.

Where do the domains come from?

The one thing Shopify won't give you is a list of stores. Two honest options:

  1. Bring your own — you usually have a seed list (competitors' customers, a niche directory, past exports). Enrich those.
  2. Common Crawl — the public web index has *.myshopify.com subdomains you can harvest for free. Each one serves meta.json directly (and tells you its real custom domain).

Doing it at scale without the plumbing

The code above works, but at volume you'll want concurrency, dedup, retry, dead-store filtering, CSV export — the boring 80%. I packaged all of it into an Apify actor so you can skip straight to the list:

  • Filter by installed app (requireApps / excludeApps)
  • Filter by size (minProducts / maxProducts) and country
  • Discover stores from Common Crawl, or enrich your own domains
  • You pay only for stores that match your filters — dead and non-matching domains are free

👉 Shopify Store Finder on Apify

Either way — hand-rolled or one-click — the principle is the same: Shopify stores tell you who they are, how big they are, and what they run. Point your prospecting at that, and stop paying for domain dumps.

Questions or a store type you can't find? Drop a comment.

Top comments (0)