# Shopify Store Lookup — Product Catalog & Detection API (`accountable_eel/shopify-store-lookup`) Actor

Detect Shopify stores and fetch product catalogs directly from /products.json — titles, prices, vendors, availability. No API key or scraping needed. Charged once per store, never for misses.

- **URL**: https://apify.com/accountable\_eel/shopify-store-lookup.md
- **Developed by:** [Adrian Voss](https://apify.com/accountable_eel) (community)
- **Categories:** Lead generation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 successful lookups

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Shopify Store Lookup

Check whether a domain runs on Shopify, and if it does, pull its live product catalog —
titles, vendors, prices, availability, tags — straight from Shopify's own public
`/products.json` endpoint. Every Shopify storefront exposes this feed by default, so there's
no Admin API key to request and no HTML to scrape: you get the same JSON the store's own
theme reads, for up to 50 products per domain.

### Who it's for

The accountable\_eel catalogue sells company intelligence columns for outbound. Each actor takes a
list of domains or company identifiers and returns one flat, stably-named row per input —
firmographics, registry IDs, tech stack, email route, hiring activity — the shape a Clay table, an
n8n workflow, or an AI agent can consume without post-processing. Pricing is pay-per-event and
per-domain: a few tenths of a cent for a row that was actually found, and nothing for a
miss, so a list that doesn't enrich costs you next to nothing. Where an official source exists —
VIES, GLEIF, SEC EDGAR, Brønnøysund, PRH, RDAP — it is queried directly instead of scraped. No
seat licence, no monthly minimum, no credit system to decode. This one answers a narrower
question than most of the catalogue: does this specific domain run on Shopify, and if so, what's
actually on the shelf right now.

### Why this one

- **Reads Shopify's own data, not a scrape of rendered HTML.** `/products.json` is the same
  JSON feed the storefront's theme pulls from — no headless browser, no CSS selectors that
  break when a theme changes.
- **One call answers two questions.** `isShopify` gives you a clean yes/no signal, and when
  it's yes you already have the catalog — no second lookup needed.
- **You don't pay to find out "no."** A domain that isn't on Shopify, has the endpoint
  disabled, or rate-limits the request comes back `found: false` and is never billed.
- **No Admin API key, no app install.** The endpoint is public and unauthenticated by
  Shopify's own design, so there's no merchant permission to request.
- **Accepts whatever format your list is already in.** Bare domains or full URLs with a
  path both resolve to the same lookup — you don't need to clean the list first.

### What you get

One row per input domain. Every field below is stably named — it won't be renamed between
runs, which matters if you're wiring this into an agent or a Clay column that reads the
schema once and keeps using it.

| Field | Type / format | Description |
|---|---|---|
| `query` | text | The domain exactly as submitted, before normalization. |
| `found` | boolean | Whether `/products.json` returned valid JSON with at least one product. Gates billing — `false` rows are never charged. |
| `status` | text | Short result status (e.g. `OK`). |
| `domain` | text | The normalized store domain (scheme and path stripped) that the catalog was fetched from. |
| `isShopify` | boolean | Whether the domain is confirmed to run Shopify — true only when `found` is true. |
| `productCount` | number | Number of products in this single page of results (capped at 50). Not the store's total catalog size. |
| `products` | array | The full list of product objects returned, each with the fields below. |
| `id` | number | Shopify's internal product ID. |
| `title` | text | Product title. |
| `vendor` | text | The product's vendor/brand field, as set by the merchant. |
| `productType` | text | Shopify's `product_type` field — merchant-defined, not a fixed taxonomy. |
| `tags` | array | Tags attached to the product. |
| `createdAt` | date (ISO 8601) | When the product was created in the store's Shopify admin. |
| `price` | text | The first variant's price, as a string with no currency symbol. |
| `available` | boolean | True if any variant of the product is in stock. |
| `imageUrl` | link | URL of the product's first image. |
| `handle` | text | The URL slug — append it to the store's domain for the product page. |
| `scrapedAt` | date (ISO 8601) | When this actor made the request. |

### Price

$5 per 1,000 domains, plus a $0.005 start fee. Misses (`found:false`) are never charged.

You're billed per domain confirmed to run Shopify with at least one product returned — a
domain that isn't Shopify, blocks the request, or rate-limits the endpoint costs nothing.
Run 1,000 domains through this actor and, at the live FREE-tier per-row price, that's
roughly **$5.29 if every one of them is found** — less as some domains turn out not to be
Shopify stores, and less again on paid Apify plans, where the per-row price drops. The same
1,000 rows through a credit-based enrichment platform typically runs **$80–$400**. There's
no seat, no monthly minimum, and no credits to track — you pay for confirmed stores.

### How to use

1. **In the Apify Console.** Open the actor page and click **Start** — the `domains` field is already pre-filled with a working example. Results land in the run's dataset as soon as each item is found.
2. **Via the API.** Call it directly with a POST request — no Console needed once you have an API token:
   ```bash
   curl "https://api.apify.com/v2/acts/accountable_eel~shopify-store-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \
     -X POST \
     -H "Content-Type: application/json" \
     -d '{"domains":["allbirds.com"]}'
   ```
3. **On a schedule.** Save this actor as an Apify **Task** with the input you want, then add a **Schedule** (hourly, daily, weekly) so it runs on its own — no server of your own required.

1) Paste a list of domains into `domains` — bare hosts or full URLs, one per input, mixed
   formats are fine.
2) Run the actor. It fetches `/products.json?limit=50` for each domain, in parallel up to
   `maxConcurrency`.
3) Read the dataset. Each row tells you `isShopify` and, when true, hands you up to 50
   products with pricing and availability already parsed out of Shopify's variant data.
4) Filter to `found: true` if you only want confirmed stores in your export — non-Shopify
   domains are still written to the dataset (unless you drop them downstream) but were
   never charged.

**Common ways teams use this:**

- **Qualify e-commerce leads** by confirming a prospect actually runs Shopify before
  pitching a Shopify-specific tool, app, or service.
- **Pull a competitor's live product list** — names, prices, availability, tags — for
  assortment or pricing research.
- **Build a dataset of Shopify merchants in a niche** by running a list of candidate
  domains and keeping only the `isShopify: true` rows.
- **Monitor a watchlist of stores** for new launches or price changes by re-running the
  lookup on a schedule and diffing `products` between runs.
- **Feed catalog fields into a market-mapping pipeline** — `vendor`, `productType`, and
  `tags` are enough to bucket stores by what they sell without opening a browser.

### Input

```json
{
  "domains": [
    "allbirds.com"
  ]
}
```

One domain per line — bare domain or full URL, with or without https:// and any path (both are stripped down to the bare host). Accepted formats: allbirds.com, kyliecosmetics.com.

```json
{
  "items": ["allbirds.com", "kyliecosmetics.com"],
  "maxConcurrency": 5,
  "proxyConfiguration": { "useApifyProxy": true }
}
```

`items` is a list of domains (with or without `https://` and any path — both are stripped
down to the bare host). One dataset row is returned per item; rows with `"found": false`
are never charged. `maxConcurrency` (default 5) caps parallel requests — kept conservative
since some stores rate-limit this endpoint. `proxyConfiguration` routes requests through
Apify Proxy; residential is recommended for stores that are more aggressive about blocking.

### Sample output

| query | found | status | domain | isShopify | productCount | products | id | title | vendor | productType | tags | createdAt | price | available | imageUrl | handle | scrapedAt |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| allbirds.com | true | OK | allbirds.com | true | 50 | \<all products (full list)> | 7205198004304 | Women's Dasher NZ - Blizzard/Deep Navy (Blizzard Sole) | Allbirds | Shoes | \["allbirds::carbon-score => undefined","allbirds::cfId => color-c09d6e63b4a11feebc68f762220735d5","allbirds::complete => true","allbirds::edition => classic","allbirds::gender => womens","allbirds::hue => blue","allbirds::hue => white","allbirds::master => womens-dasher-nz","allbirds::material => tree","allbirds::price-tier => msrp","allbirds::silhouette => dasher","FLEX3818571 (PL 553696876312)","loop::returnable => true","OOS DNS","WAVE 2 ECDC","WAVE 2 WCDC","YCRF\_womens-perform-shoes","YGroup\_ygroup\_womens-dasher-nz"] | 2025-08-06T20:08:21-07:00 | 140.00 | true | https://cdn.shopify.com/s/files/1/1104/4168/files/A12464\_26Q1\_Dasher-NZ-Blizzard-Deep-Navy-Blizzard\_PDP\_LEFT\_e38509e9-51da-49a3-a794-53de6797a109.png?v=1768948779 | womens-dasher-nz-blizzard-deep-navy | 2026-08-26T06:01:38.607Z |

```json
{
  "query": "allbirds.com",
  "found": true,
  "data": {
    "domain": "allbirds.com",
    "isShopify": true,
    "productCount": 50,
    "products": [
      {
        "id": 4392847259691,
        "title": "Men's Tree Runners",
        "vendor": "Allbirds",
        "productType": "Shoes",
        "tags": ["Men", "Runners", "Everyday"],
        "createdAt": "2019-03-12T10:15:00-04:00",
        "price": "98.00",
        "available": true,
        "imageUrl": "https://cdn.shopify.com/s/files/1/example/tree-runners.jpg",
        "handle": "mens-tree-runners"
      }
    ]
  },
  "scrapedAt": "2026-08-20T14:03:11.000Z"
}
```

A row is only marked `found: true` — and only then billed — once `/products.json`
returns valid JSON with at least one product. A domain that isn't running Shopify, has
disabled the endpoint, or rate-limits/blocks the request (a real, observed outcome on some
stores) comes back as `found: false` and is never charged. `productCount` reflects the
number of products in this single page of results, capped at 50 — it is not the store's
total catalog size.

### Use it from Clay, n8n, Make, or an AI agent

This actor runs synchronously over plain HTTP — call it directly from a script, a workflow tool, or an AI agent, no Apify Console needed once you have an API token.

```bash
curl "https://api.apify.com/v2/acts/accountable_eel~shopify-store-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{"domains":["allbirds.com"]}'
```

**n8n.** Add an HTTP Request node: Method `POST`, URL `https://api.apify.com/v2/acts/accountable_eel~shopify-store-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>`, Body Content Type `JSON`, JSON Body `{"domains":["allbirds.com"]}` (swap in an expression from an earlier node for a real value).

**Clay.** Add an "HTTP API" column: Method `POST`, URL `https://api.apify.com/v2/acts/accountable_eel~shopify-store-lookup/run-sync-get-dataset-items?token=<YOUR_TOKEN>`, Body `{"domains":["{{domain}}"]}`, mapping the row's domain into the `domains` array.

**MCP.** In Claude, Cursor, or any MCP client with the Apify MCP server, ask for "Shopify Store Lookup | Apify" — the agent will find and run this actor.

### Tips

- **Filter to `found: true` before you export.** Non-Shopify domains still take a dataset
  row so you can see what was checked, but you almost never want them in a lead list.
- **Use `productType` and `tags` for niche mapping, not a fixed taxonomy.** Both fields are
  whatever the merchant typed in Shopify admin — expect inconsistent casing and synonyms
  across stores.
- **`productCount` caps at 50 — don't read it as total catalog size.** A store with 3,000
  SKUs and a store with 12 both cap out differently; only a count below 50 tells you the
  true total.
- **Turn on residential proxies for stores you know block scrapers.** The default Apify
  Proxy setting works for most domains; residential helps on the more defensive ones.
- **Re-run on a schedule and diff `products` to catch new launches or price changes** on a
  watchlist of competitor or partner stores.

### vs. alternatives

| | What it costs | What you get | Trade-off |
|---|---|---|---|
| **This actor** (`shopify-store-lookup`) | $0.00529 per found row (less on paid tiers), $0.00005 actor start, nothing for a domain that isn't Shopify or blocks the request | A yes/no Shopify signal plus up to 50 live products per store — title, vendor, price, availability, tags — read straight from the store's own `/products.json` feed | Capped at 50 products per store (Shopify's own page limit, no pagination), no historical data, and some stores rate-limit or disable the endpoint entirely |
| **BuiltWith** | From $295/month | Historic tech data going back years, plus lead lists filtered by technology | If you need "every site that added Shopify last quarter", that's a database question and BuiltWith answers it. This answers "what is this one domain running, right now". |
| **Clay** | $0.08–$0.40 per enriched row in credits, on top of a seat | A whole enrichment workspace — waterfalls across dozens of providers, plus the table and the sequencing around it | If you want one place that does everything and you're not counting rows, that's Clay. This is one column, priced per column, callable *from* Clay via its HTTP step. |
| **Doing it yourself** | Your time + handling 429s from stores that rate-limit the endpoint, normalizing domain formats, and re-parsing Shopify's variant structure for price/availability | The same data | This actor already absorbs the retry logic, concurrency limits, and the domain-format cleanup — you paste a list and read a flat table. |

Prices for third-party tools are their published list prices as of August 2026 and are not
tracked here — check the vendor before relying on the comparison.

### FAQ

**What happens when a domain isn't running Shopify?** The row comes back `found: false`
and you are not charged for it. The request either 404s, returns non-JSON HTML, or returns
JSON with no `products` array — any of those outcomes counts as not found, and none of them
appear as a line item on your bill. You still get the row in the dataset, with `status`
telling you why, so you can see which domains were checked even though nothing was billed.

**Are there rate limits?** Shopify itself, not Apify, is the limiting factor — some stores
throttle or disable public access to `/products.json`. `maxConcurrency` (default 5) keeps
this actor's own request rate conservative so it doesn't trip a store's own defenses; a
store that returns 429 anyway is recorded as `found: false` rather than retried
indefinitely, since a store that's actively rate-limiting isn't going to resolve on a
second try within the same run.

**How fresh is the data?** Every request hits the store live at run time — there's no cache
or stored snapshot between runs, and nothing is retained after the run finishes. `scrapedAt`
on each row is the exact timestamp of that request, so you always know how current a price
or availability figure is when you read it downstream.

**Do I need to configure a proxy?** No — `proxyConfiguration` defaults to Apify Proxy and
works for most stores out of the box. Switch to residential proxies for domains you already
know are aggressive about blocking non-browser traffic; it costs more per request but
recovers stores that would otherwise come back `found: false` on the default setting.

**Is any personal data collected?** No. The output is a store's public product catalog —
titles, prices, tags, images — the same data any visitor's browser loads when it hits the
storefront. No customer records, order data, or personal data of any kind is read, derived,
or stored.

**Can I run this on a schedule?** Yes — Apify's built-in Scheduler can trigger a run on any
interval you set, from hourly to monthly. Combined with diffing `products` between runs
downstream, that's how the watchlist use case (catching new product launches or price
changes on competitor stores) works in practice — no separate cron infrastructure needed.

**Can an AI agent call this directly?** Yes — through the Apify MCP server, an agent can
find and run this actor by name ("Shopify Store Lookup | Apify") without any custom
integration code.

**Does it need a Shopify Admin API key?** No — this only calls the public, unauthenticated
storefront JSON endpoint that requires no authentication or app installation.

**Can I pass a full product URL instead of a bare domain?** Yes — any `https://` prefix and
path are stripped automatically, so `https://allbirds.com/collections/mens` and
`allbirds.com` resolve to the same lookup.

### Related actors

- [Tech Stack Lookup](https://apify.com/accountable_eel/tech-stack-lookup) — broader
  platform and tooling detection when you need more than a Shopify yes/no.
- [Company Domain Enrichment](https://apify.com/accountable_eel/company-domain-enrichment) —
  firmographics, tech stack, and hiring signal for the same domain in one composite lookup.
- [Company Registry Unified Lookup](https://apify.com/accountable_eel/company-registry-unified-lookup) —
  official registry data (VAT, LEI, EDGAR, and more) for the company behind the domain.

# Actor input Schema

## `domains` (type: `array`):

One domain per line — bare domain or full URL, with or without https:// and any path (both are stripped down to the bare host). Accepted formats: allbirds.com, kyliecosmetics.com. You're only charged for the ones we actually find — a miss costs nothing.

## `testRun` (type: `boolean`):

Turn this on to test your input on a small sample before running the full list. Turn it off to process everything.

## `onlyFound` (type: `boolean`):

Only keep rows where something was actually found. Misses are always free, whether or not you show them here.

## `includeKeywords` (type: `array`):

Optional. Only keep results that mention at least one of these words (e.g. a job title, a city, a product name). Leave empty to keep everything.

## `excludeKeywords` (type: `array`):

Optional. Drop any result that mentions one of these words. Leave empty to skip nothing.

## `maxResults` (type: `integer`):

Optional. Stop the run once this many results have been found — useful for a quick, cheap sample. Leave blank for no limit.

## `columns` (type: `array`):

Choose which pieces of information to include in each result row. All are included by default.

## `expandRows` (type: `boolean`):

When on, each product found gets its own row instead of being grouped under its domain. You're still only charged once per domain, no matter how many rows it produces.

## `maxConcurrency` (type: `integer`):

Parallel requests. Keep conservative — this target has no browser fallback, so getting blocked costs more than slow-and-steady.

## `proxyConfiguration` (type: `object`):

Apify Proxy config. Residential recommended for anti-bot-sensitive targets.

## Actor input object example

```json
{
  "domains": [
    "allbirds.com"
  ],
  "testRun": false,
  "onlyFound": false,
  "includeKeywords": [],
  "excludeKeywords": [],
  "columns": [
    "domain",
    "isShopify",
    "productCount",
    "products",
    "id",
    "title",
    "vendor",
    "productType",
    "tags",
    "createdAt",
    "price",
    "available",
    "imageUrl",
    "handle"
  ],
  "expandRows": true,
  "maxConcurrency": 5,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `results` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "domains": [
        "allbirds.com"
    ],
    "includeKeywords": [],
    "excludeKeywords": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("accountable_eel/shopify-store-lookup").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "domains": ["allbirds.com"],
    "includeKeywords": [],
    "excludeKeywords": [],
}

# Run the Actor and wait for it to finish
run = client.actor("accountable_eel/shopify-store-lookup").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "domains": [
    "allbirds.com"
  ],
  "includeKeywords": [],
  "excludeKeywords": []
}' |
apify call accountable_eel/shopify-store-lookup --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,accountable_eel/shopify-store-lookup"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/HFTbCUFznJi5tkTSL/builds/91yopyQfBPx7YzVYA/openapi.json
