# Shopify Store Report — Catalog, Pricing, Discounts & Growth (`tidyfeed/shopify-store-report`) Actor

Profile any Shopify store in one row: catalog size, price range, how much of it is discounted right now, stock depth, how many products launched in the last 30 days, assortment mix and theme.

- **URL**: https://apify.com/tidyfeed/shopify-store-report.md
- **Developed by:** [Beta Pos](https://apify.com/tidyfeed) (community)
- **Categories:** E-commerce, Automation, Agents
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $15.00 / 1,000 stores

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#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 Report — Catalog, Pricing, Discounts & Growth

**Paste a store address. Get one row that tells you how that business is actually trading.**

Catalog size, price range, how much of it is discounted right now, how deep those discounts run, how much is still in stock, and how many products they launched in the last 30 days. Paste a list of competitors and profile them all in a single run.

No API key. No login. No proxies. Nothing estimated.

***

### What one row tells you

Two real stores, read on the same day:

| | allbirds.com | deathwishcoffee.com |
|---|---|---|
| Products | 291 | 148 |
| Median price | $91 | $33 |
| **% of catalog on sale** | **53.3%** | 18.2% |
| Average discount | 41.5% | 37.5% |
| **% of variants in stock** | **11.8%** | 88.2% |
| **New products, last 30 days** | **0** | 5 |
| New products, last 90 days | 0 | 20 |
| Catalog age | 7.8 years | 12.8 years |

Read the first column: half the catalog marked down, discounts averaging 41%, nine variants in ten sold out, and nothing new launched in three months. Read the second: light discounting, shelves full, a steady trickle of new products.

**Same category, same country, completely different trading positions** — and you can see it in one line without opening a single product page.

***

### What you get

One row per store.

| Group | Columns |
|---|---|
| **Catalog** | `productCount`, `variantCount`, `avgVariantsPerProduct`, `avgImagesPerProduct` |
| **Pricing** | `priceMin`, `priceMedian`, `priceMax` |
| **Discounting** | `productsOnSale`, `percentOnSale`, `avgDiscountPercent`, `maxDiscountPercent` |
| **Stock** | `variantsInStock`, `percentInStock` |
| **Momentum** | `productsAdded30d`, `productsAdded90d`, `newestProductAt`, `oldestProductAt`, `catalogAgeDays` |
| **Assortment** | `topVendors`, `vendorCount`, `topProductTypes`, `productTypeCount` |
| **Storefront** | `themeName`, `socialInstagram`, `socialFacebook`, `socialTiktok`, `socialYoutube`, `analyticsGa4`, `analyticsGtm`, `detectedTools` |
| **Provenance** | `feedSource`, `usesCustomStorefront`, `catalogTruncated`, `status`, `scannedAt` |

***

### Nothing here is estimated

Every number is **computed from the catalog the store publishes itself**, then shown to you. There is no revenue model, no traffic guess, no risk score invented from a formula.

That is a deliberate limit, and it is the point: you can verify any figure in this report by opening the store yourself. A field this actor cannot read — a theme a headless storefront does not expose, a pixel loaded server-side — comes back **blank rather than filled in with a guess**.

***

### What it costs

**One charge per store profiled.** A store that returns nothing is not billed.

Reading a store means walking its entire catalog, not sampling the front page — a 1,300-product store is 1,300 products of arithmetic behind that single row.

***

### Typical uses

- **Competitor screening** — profile 50 stores, sort by discount depth or launch rate, then look closely at the three that matter.
- **Sale-season watch** — schedule it weekly and watch `percentOnSale` and `avgDiscountPercent` move across a whole category.
- **Sourcing and partnerships** — find stores that are actually launching product rather than clearing stock.
- **Lead qualification** — catalog size, price band and assortment tell you in one row whether a store is worth a pitch.
- **Feeding an AI agent** — callable over the API and through MCP, so an assistant can profile a store mid-task.

***

### Input

| Field | What it does |
|---|---|
| **Shopify store URLs** | One or more addresses. A bare domain, a homepage, a product page or a `.myshopify.com` address all work. |
| **Max products to read per store** | How deep to read each catalog before summarising. Default 3000, which covers almost every store outright. Runs that hit the cap are flagged as `catalogTruncated`. |
| **Include theme, socials and marketing tools** | Reads the storefront page for theme, social profiles, analytics IDs and recognisable marketing apps. On by default. |

```json
{
  "storeUrls": ["allbirds.com", "deathwishcoffee.com", "rothys.com"],
  "maxProductsScanned": 3000
}
```

***

### How it works

Shopify serves a public product feed at `/products.json` on every storefront. This actor walks that feed 250 products at a time, computes the profile from the whole catalog, and reads the storefront page once for theme and marketing signals.

**Headless storefronts are followed to their real shop.** A growing number of brands run a custom front end on their own domain, so `brand.com/products.json` answers with a web page instead of a feed. This actor reads the shop address the site publishes in its own markup and profiles the catalog from there — the row is marked `usesCustomStorefront` so you know.

Because it uses the store's own public feed it needs no proxies, and it does not break when a theme changes.

***

### Honest limits

Worth knowing before you run it:

- **Stock figures are what the feed publishes, from the United States.** Shopify exposes an in-stock/out-of-stock flag per variant, never a quantity — and for stores running separate regional markets, that flag reflects the market the request lands in. Runs here originate from US servers, so stock reads as a US shopper sees it. Measured: one store reported 46.6% of variants in stock from the US and 0.1% from Asia on the same day. Price, discount, catalog and momentum figures are unaffected.
- **Currency is not published in the feed**, so prices are reported as plain numbers in the store's own currency.
- **Theme, analytics IDs and marketing tools are best-effort.** Modern storefronts load a lot of this server-side or through Shopify's pixel API, where it simply is not visible. Blank means "not readable", not "not used".
- **`productsAdded30d` counts catalog records, not restocks.** A product re-created or migrated will look new.
- **Password-protected, pre-launch and feed-disabled stores return nothing**, with a per-store reason in the `status` column.
- **A few brand domains sit behind bot protection.** Those are reported rather than failing the run; the store's `.myshopify.com` address usually works in their place.

***

### Scheduling

Trading positions move. Schedule this weekly against a list of competitors and each run stores a dated snapshot — discount depth and launch rate over time is the shape of a category, and this is all it takes to record it.

***

### Legal

This actor reads only data that a Shopify store publishes publicly, at addresses the store itself serves to anyone. It collects no personal data, requires no login, and bypasses no access control. You remain responsible for how you use the exported data.

***

Questions, or a store that behaves oddly? Open an issue on the actor — issues are read and answered.

# Actor input Schema

## `storeUrls` (type: `array`):

One or more Shopify store addresses. Paste the plain domain — a homepage, a product page or a myshopify.com address all work. Add a list of competitors to profile them all in a single run.

## `maxProductsScanned` (type: `integer`):

How deep to read each catalog before summarising it. The default covers almost every store outright; lower it only if you are profiling very large catalogs and want faster runs. Runs that hit the cap are flagged in the output.

## `includeTechSignals` (type: `boolean`):

Reads the storefront page for the theme name, social profiles, analytics IDs and recognisable marketing apps. These are best-effort — a storefront that hides them leaves the fields blank rather than guessed.

## Actor input object example

```json
{
  "storeUrls": [
    "https://allbirds.com",
    "deathwishcoffee.com"
  ],
  "maxProductsScanned": 3000,
  "includeTechSignals": true
}
```

# Actor output Schema

## `report` (type: `string`):

No description

## `reportCsv` (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 = {
    "storeUrls": [
        "https://allbirds.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("tidyfeed/shopify-store-report").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 = { "storeUrls": ["https://allbirds.com"] }

# Run the Actor and wait for it to finish
run = client.actor("tidyfeed/shopify-store-report").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 '{
  "storeUrls": [
    "https://allbirds.com"
  ]
}' |
apify call tidyfeed/shopify-store-report --silent --output-dataset

```

## MCP server setup

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

```

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/OmBTMydBf9fi8wVMt/builds/RTl2AGgDc3BCd59pm/openapi.json
