# Shopify App Store Scraper — Apps, Pricing Plans & Reviews (`kilndata/shopify-app-store-scraper`) Actor

Scrape Shopify App Store apps by keyword, category, developer or URL: pricing plans, rating, rating breakdown, review count, developer, launch date, categories and features, languages, integrations, data access. Optional reviews with no reviewer names. No login, no browser.

- **URL**: https://apify.com/kilndata/shopify-app-store-scraper.md
- **Developed by:** [Datakiln](https://apify.com/kilndata) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 app listings

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?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Shopify App Store Scraper: apps, pricing plans and reviews

Give it search words, a category, a developer page or a list of app URLs, and it returns one row per app from the Shopify App Store: name, tagline, rating and star breakdown, review count, every pricing plan with its price and features, developer, launch date, categories, languages, integrations and the store data the app asks to access. If you want them, it also returns the reviews of each app, with date, text, star rating, the reviewing store's country, how long they used the app and the developer's reply.

It reads the public pages at apps.shopify.com over plain HTTP. No browser, no login, no API key. Reviews come **without the name of the store or person who wrote them**. That field is never collected.

This actor is independent. It is not made, endorsed or supported by Shopify. "Shopify" is a trademark of its owner and appears here only to say which site the actor reads.

### Who it is for

| You are | What you do with it |
|---|---|
| App developer | Compare your pricing plans and ratings with the other apps in your category; read what merchants complain about in competitors' reviews |
| Agency or consultant | Build a shortlist of apps for a client: free plan or not, cheapest paid plan, rating, "Built for Shopify" badge, languages |
| Investor or analyst | Track launch dates, review counts and rating changes across a category over time |
| Market researcher | Count how many apps solve one problem, how they price it, and who builds them |
| Agent or workflow builder | Small, flat JSON that an agent can filter ("email apps under 30 USD with a free plan and a 4.8+ rating") |

### How it works

1. **Keyword search.** The store's own search page is closed to robots, so the actor does not use it. It downloads the store's public app sitemap and keeps the apps whose URL name contains every word you typed. `email marketing` finds `klaviyo-email-marketing`. The result is not in Shopify's search ranking order, and an app whose URL does not contain your word will not match.
2. **Categories, developers, collections.** The actor opens the listing page and takes the apps in the store's own order. The position is saved in `sourcePosition`. Category pages are read page by page, 24 apps each.
3. **App pages.** Each app page is read once and turned into one row.
4. **Reviews (optional).** The actor opens the app's review pages, 10 reviews per page, newest first or in the store's default order, until it reaches your limit.

An app found through several routes is scraped, and charged, once.

### Input

Give at least one of `searchTerms`, `categories` or `startUrls`.

Example:

```json
{
    "searchTerms": ["upsell"],
    "categories": ["marketing-and-conversion-marketing-email-marketing"],
    "startUrls": ["https://apps.shopify.com/klaviyo-email-marketing", "judgeme"],
    "maxResults": 50,
    "includeReviews": true,
    "maxReviewsPerApp": 20,
    "reviewsSort": "newest"
}
```

| Field | Type | Default | What it does |
|---|---|---|---|
| `searchTerms` | list of text | empty | Keywords. An app matches when its URL name contains every word |
| `categories` | list of text | empty | Category handles or full category URLs |
| `startUrls` | list of text | empty | Any mix of app URLs, app handles, developer pages (`/partners/...`) and category or collection URLs |
| `maxResults` | integer, 1 to 10,000 | 50 | Apps per search term, category, developer or collection. App URLs you list directly are always scraped |
| `includeReviews` | true/false | false | Also return reviews |
| `maxReviewsPerApp` | integer, 0 to 100,000 | 20 | Reviews per app (10 are read per page) |
| `reviewsSort` | `newest` or `relevance` | `newest` | Order of the reviews |
| `respectRobotsTxt` | true/false | true | Skip pages the store's robots.txt disallows. Skipped pages are not charged |
| `maxConcurrency` | integer, 1 to 20 | 3 | Pages fetched at the same time |
| `maxRequestsPerMinute` | integer, 10 to 600 | 60 | Upper limit on page requests |
| `requestTimeoutSecs` | integer, 5 to 120 | 30 | Wait time per page before a retry |
| `maxRetries` | integer, 0 to 5 | 2 | Retries for a failing page |
| `proxyConfiguration` | proxy object | no proxy | Optional. The store works without one. Try Apify Proxy only if many pages fail |

The Console form pre-fills small values (3 apps, 5 reviews) so a first test run costs a few cents. Raise them for real work.

### Output

The dataset has two kinds of rows, told apart by `type`: `app` and `review`. Both are in the same dataset; the Console has an "Apps" view and a "Reviews" view.

An app row (shortened, from a real run):

```json
{
    "type": "app",
    "status": "ok",
    "handle": "reupsell",
    "url": "https://apps.shopify.com/reupsell",
    "name": "Reup: AI Product Accessories",
    "tagline": "Find matching product accessories with AI, review them, and publish in bulk.",
    "rating": 5,
    "reviewCount": 1,
    "ratingDistribution": { "5": 1 },
    "builtForShopify": false,
    "pricingSummary": "Free plan available",
    "hasFreePlan": true,
    "hasFreeTrial": false,
    "lowestPaidPlanUsd": 9.99,
    "pricingPlans": [
        { "name": "Free", "price": "Free", "priceUsd": 0, "billingPeriod": null, "features": ["Stores up to 25 products", "CSV import and export"] },
        { "name": "Starter", "price": "$9.99/month", "priceUsd": 9.99, "billingPeriod": "month", "features": ["Stores up to 250 products", "Analytics"] }
    ],
    "developerName": "Sky Design Media Srl",
    "developerPageUrl": "https://apps.shopify.com/partners/sky-design-media-srl",
    "launchedAt": "2026-03-10",
    "listingLastModified": "2026-09-19",
    "categoryNames": ["Upsell and bundles - Other"],
    "foundVia": "search:upsell",
    "sourcePosition": 1,
    "scrapedAt": "2026-10-05T04:01:12.244Z"
}
```

A review row:

```json
{
    "type": "review",
    "status": "ok",
    "handle": "upsell",
    "appName": "Avocados upsell with triggers",
    "reviewId": "841229",
    "rating": 5,
    "reviewDate": "2022-01-21",
    "edited": false,
    "text": "Very good purchase app. Stunning customer support as well. No need to look further. I love it. Thanks",
    "reviewerCountry": "France",
    "timeUsingApp": "About 2 hours",
    "developerReply": "Thanks a lot for such a nice & honest review. ...",
    "developerReplyDate": "2022-01-21",
    "reviewUrl": "https://apps.shopify.com/reviews/841229",
    "scrapedAt": "2026-10-05T03:58:53.258Z"
}
```

There is no field for the reviewer's name or store name. `reviewerCountry` is the country of the reviewing store, not of a person.

The app row has more fields than shown: `description`, `highlights`, `reviewsSummary` (the store's own summary, only on apps with many reviews), `languages`, `worksWith`, `dataAccess`, `resources`, `screenshots`, `similarApps` and others. The full list is in the dataset schema.

#### Rows that are not apps

When something cannot be scraped you get a row with a `status` and an `error` that says why, so a run never fails silently:

| `status` | Meaning |
|---|---|
| `ok` | Scraped, and charged |
| `failed` | The page could not be read after the retries, or the app is unlisted or removed |
| `skipped` | robots.txt disallows the page |
| `invalid-input` | A value you gave is not a valid category, URL or search (for example a keyword that matches no app) |

Only `ok` rows are charged. Every other status is free.

### Pricing

You pay per result, plus the usual Apify platform usage.

| Event | What is charged | Free | Starter / Bronze | Scale / Silver | Business / Gold |
|---|---|---|---|---|---|
| `app-listing` | One app row with status `ok` | $3.00 per 1,000 | $2.50 per 1,000 | $2.20 per 1,000 | $2.00 per 1,000 |
| `app-review` | One review row | $1.00 per 1,000 | $0.90 per 1,000 | $0.80 per 1,000 | $0.70 per 1,000 |

There is also a start event of $0.00005 per run (Actor Start).

- Failed, skipped and invalid-input rows are not charged.
- An app that appears through several routes is charged once.
- Review pages that return no reviews cost nothing.

**Your spending limit is respected.** Set "Maximum cost per run" when you start the actor. The actor checks how many results your limit still allows before it saves each batch. When the limit is reached it stops and logs a warning. It never saves more than you agreed to pay for.

#### Cost examples (Free plan prices)

| Run | Calculation | Cost |
|---|---|---|
| 100 apps, no reviews | 100 x $3.00 / 1,000 | $0.30 |
| 500 apps in one category | 500 x $3.00 / 1,000 | $1.50 |
| 50 apps and 20 newest reviews each | 50 x $3.00 / 1,000 + 1,000 x $1.00 / 1,000 | $1.15 |
| One app, all of its 2,900 reviews | $0.003 + 2,900 x $1.00 / 1,000 | about $2.90 |
| The same 50 apps and 1,000 reviews on the Business / Gold plan | 50 x $2.00 / 1,000 + 1,000 x $0.70 / 1,000 | $0.80 |

Reviews are the expensive part when an app has thousands. Keep `maxReviewsPerApp` low unless you need the full history.

### Limits

- **The store can change its HTML.** The actor reads the page structure as it was when it was last tested. If the store changes its layout, some fields can come back empty or wrong until the actor is updated. Report it in Issues.
- **Removed or unlisted apps** appear as `failed` with a message. The sitemap can still list an app for a while after it is gone.
- **Keyword search matches URL names, not descriptions.** An app called `acme-boost` will not show up for "upsell" even if it is an upsell app. Use categories when you want everything in a field.
- **No ranking order for keywords.** Results follow the sitemap, not the store's search ranking. Category and developer pages keep the store's own order.
- **Developer and collection pages are read as one page.** If a developer has more apps than their page shows at once, you get the apps on that page. Category pages are read page by page up to `maxResults`.
- **Reviews are read page by page**, 10 at a time, and the whole run is capped at `maxRequestsPerMinute` (60 by default). A history of thousands of reviews takes a while.
- **Pricing is what the listing shows.** Usage-based fees, volume discounts and prices negotiated outside the store are not in the listing. `lowestPaidPlanUsd` is the cheapest paid plan in USD per billing period, so a yearly plan and a monthly plan are not directly comparable.
- **English listings only.** The actor reads the English version of the store.
- **Rate limiting.** If the store answers HTTP 429, the actor pauses all requests, then retries.

### Responsible use

The actor reads public pages that the store shows to any visitor. It needs no login and does not get around any access control. It checks robots.txt before every request, limits request speed, backs off when asked to, and does not use a proxy unless you turn one on.

Reviews are collected without the author's name or store name. The text of a review is still the work of the person who wrote it and can contain personal details they chose to write. Do not try to identify authors, and check the store's terms and your local law (data protection, copyright, competition) for what you plan to do with the data. If you publish review text, quote it with care and credit the source.

### FAQ

**Does it need a Shopify account or API key?**
No. It reads the public store pages only.

**Can I get the name of the store that left a review?**
No. It is not collected, and there is no setting to change that.

**Why did my keyword return nothing?**
The words must all appear in the app's URL name. Try fewer or shorter words (`review` instead of `product reviews`), or use a category.

**What happens when I hit my spending limit in the middle of a run?**
The actor stops, keeps what it already saved, and logs which limit it hit. Everything saved is charged, nothing beyond it.

**How do I find a category handle?**
Open the category on apps.shopify.com. The handle is the last part of the address after `/categories/`. You can also paste the full URL.

**How do I monitor an app over time?**
Schedule the actor with the app's URL, `includeReviews` on, `reviewsSort` on `newest` and a small `maxReviewsPerApp`. Compare `rating`, `reviewCount` and `pricingPlans` between runs.

**Is the data live?**
It is read at the time of the run. `listingLastModified` comes from the store's sitemap and shows when the listing was last changed.

### Support and feature requests

Use the **Issues** tab of this actor to report a problem or ask for a new field or filter. For a bug, include the run link and the app URL or keyword that caused it. The Apify platform, API, schedules, webhooks and integrations (Make, Zapier, n8n, Google Sheets, MCP) work with this actor like with any other.

# Actor input Schema

## `searchTerms` (type: `array`):

Keywords such as "email marketing", "reviews" or "upsell". An app matches when its store URL name contains every word (e.g. "email marketing" → klaviyo-email-marketing). Uses the store's official sitemap, so results are not in Shopify's search ranking order. Up to "Max apps per search term / category" apps per term.

## `categories` (type: `array`):

Category handles or URLs, e.g. "marketing-and-conversion-marketing-email-marketing" or "https://apps.shopify.com/categories/store-design-images-and-media-image-gallery". Apps come in the store's own category order, with their position saved in "sourcePosition".

## `startUrls` (type: `array`):

Any mix of app URLs (https://apps.shopify.com/klaviyo-email-marketing), app handles (klaviyo-email-marketing), developer pages (https://apps.shopify.com/partners/klaviyo) and category or collection URLs.

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

How many apps to scrape for each search term, category, developer or collection. App URLs given directly are always scraped. An app found in several places is scraped (and charged) once.

## `includeReviews` (type: `boolean`):

Also scrape reviews: rating, date, text, reviewer country, time using the app and the developer's reply. The reviewer's store name is never collected. Each review is a separate item with "type": "review".

## `maxReviewsPerApp` (type: `integer`):

Upper limit of reviews per app (10 per page are read).

## `reviewsSort` (type: `string`):

"newest" = most recent first (best for monitoring); "relevance" = the store's default order.

## `respectRobotsTxt` (type: `boolean`):

Skip pages that the store's robots.txt disallows. Skipped pages are not charged.

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

How many pages are fetched in parallel. Keep it low to be polite to the store.

## `maxRequestsPerMinute` (type: `integer`):

Upper limit of page requests per minute.

## `requestTimeoutSecs` (type: `integer`):

How long to wait for a page before retrying.

## `maxRetries` (type: `integer`):

How many times a failing page is retried. Apps that still fail are listed with status "failed" and are not charged.

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

Optional. The store works without a proxy. Use Apify Proxy only if you see many failed pages.

## Actor input object example

```json
{
  "searchTerms": [],
  "categories": [
    "marketing-and-conversion-marketing-email-marketing"
  ],
  "startUrls": [
    "https://apps.shopify.com/klaviyo-email-marketing",
    "judgeme"
  ],
  "maxResults": 3,
  "includeReviews": true,
  "maxReviewsPerApp": 5,
  "reviewsSort": "newest",
  "respectRobotsTxt": true,
  "maxConcurrency": 3,
  "maxRequestsPerMinute": 60,
  "requestTimeoutSecs": 30,
  "maxRetries": 2,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# 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 = {
    "searchTerms": [],
    "categories": [
        "marketing-and-conversion-marketing-email-marketing"
    ],
    "startUrls": [
        "https://apps.shopify.com/klaviyo-email-marketing",
        "judgeme"
    ],
    "maxResults": 3,
    "includeReviews": true,
    "maxReviewsPerApp": 5
};

// Run the Actor and wait for it to finish
const run = await client.actor("kilndata/shopify-app-store-scraper").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 = {
    "searchTerms": [],
    "categories": ["marketing-and-conversion-marketing-email-marketing"],
    "startUrls": [
        "https://apps.shopify.com/klaviyo-email-marketing",
        "judgeme",
    ],
    "maxResults": 3,
    "includeReviews": True,
    "maxReviewsPerApp": 5,
}

# Run the Actor and wait for it to finish
run = client.actor("kilndata/shopify-app-store-scraper").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 '{
  "searchTerms": [],
  "categories": [
    "marketing-and-conversion-marketing-email-marketing"
  ],
  "startUrls": [
    "https://apps.shopify.com/klaviyo-email-marketing",
    "judgeme"
  ],
  "maxResults": 3,
  "includeReviews": true,
  "maxReviewsPerApp": 5
}' |
apify call kilndata/shopify-app-store-scraper --silent --output-dataset

```

## MCP server setup

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

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/xZOug1L1ivVX942TD/builds/cbcf78oblYIhWTbma/openapi.json
