# Vee Shop - Export a Shopify store's full catalog (`veelabs/vee-shop`) Actor

Export a Shopify store's full catalog: prices, sale prices, stock, variants with SKUs, images and descriptions. No browser, no proxies. Respects robots.txt and skips stores that block the feed. Built for price and stock monitoring. $0.90 per 1,000 products.

- **URL**: https://apify.com/veelabs/vee-shop.md
- **Developed by:** [Hoxei R\&D](https://apify.com/veelabs) (community)
- **Categories:** E-commerce, Other
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

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/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

## Vee Shop

**The full product catalog of any Shopify store, with prices, sale prices and stock, in one run.**

Give Vee Shop a store URL or a collection URL. It reads the public products feed that Shopify stores publish and returns one clean record per product, with every variant, SKU and image link.

### Who it is for

- Retailers and brands that track competitor prices and stock
- Price-comparison and deal sites
- Analysts studying a category, a brand range or a store's assortment over time
- Developers who need product data for an internal tool

### What you get: Shopify products, prices, variants and stock (CSV, JSON, Excel)

One record per product (or one per variant in flat mode):

```json
{
  "store": "theme-dawn-demo.myshopify.com",
  "productId": 6920737783897,
  "handle": "puff-olive-leaf",
  "title": "Puff",
  "vendor": "Mlouye",
  "productType": null,
  "url": "https://theme-dawn-demo.myshopify.com/products/puff-olive-leaf",
  "currency": "CAD",
  "priceMin": 465,
  "priceMax": 465,
  "compareAtPriceMax": null,
  "onSale": false,
  "available": true,
  "variantCount": 6,
  "optionNames": ["Color"],
  "imageUrls": ["https://cdn.shopify.com/…"],
  "descriptionText": "A streamlined approach to daily essentials…",
  "createdAt": "2021-11-16T17:16:27-07:00",
  "updatedAt": "2026-10-15T11:48:42-06:00",
  "publishedAt": "2021-11-16T17:16:13-07:00",
  "variants": [
    { "variantId": 39577056936025, "title": "Olive Leaf", "sku": "10-039-065", "price": 465, "compareAtPrice": null, "available": false, "option1": "Olive Leaf", "option2": null, "option3": null, "grams": 500 }
  ],
  "scrapedAt": "2026-10-16T…"
}
```

Prices are numbers, exactly as the store publishes them. `currency` is the store's currency (ISO code) read from its public `meta.json`, or `null` when the store does not publish it. `optionNames` names the variant options (for example Color, Size). In `flat` mode each variant row is one result, and one result is one billed item. A `SUMMARY` record in the key-value store lists every store you entered, how many products were returned, and the reason for any store that was skipped.

### How it works

Vee Shop reads each store's public `/products.json` feed, about 250 products per request. It needs no browser and no proxies, so runs are light on compute.

### How it is different

- **robots.txt is always respected, and it cannot be switched off.** Before scraping, Vee Shop reads the store's robots.txt. If the products feed is disallowed for `VeeShop` or for `*`, the store is skipped and the reason is written to `SUMMARY`. `Crawl-delay` is honoured, up to 30 seconds. If robots.txt cannot be fetched because of a server or network error, the store is skipped, because permission cannot be verified. A missing robots.txt (404) counts as allowed.
- **No bypassing.** Stores that block the feed or put it behind a waiting room are skipped and reported. Vee Shop does not work around them.
- **Ready-made monitoring fields.** `onSale` and `available` are computed for you, and `onlyAvailable` returns in-stock products only.
- **Choose your shape.** Variants nested inside the product, one row per variant (`flat`), or left out (`none`).
- **Merchant tags are off by default**, because tags hold internal labels. Turn them on with `includeTags`.
- **Clear errors.** A site that is not a Shopify store, or one with the feed disabled, is reported in `SUMMARY` and the run moves on.
- **Controlled cost.** `maxProductsPerStore` caps each store, and charging stops if you set a maximum cost on the run.

### If a store returns nothing

The `SUMMARY` record and the run's status message give the reason in plain words: the store blocks the request (HTTP 403, bot protection), it is not a Shopify store or has switched off `/products.json` (HTTP 404), the store is password protected, `robots.txt` disallows the feed or cannot be read, or the store is rate-limiting (HTTP 429). The same store listed twice is read once.

### Input examples

Whole catalog of one store:

```json
{ "storeUrls": ["https://theme-dawn-demo.myshopify.com"] }
```

One collection, in stock only, one row per variant:

```json
{
  "storeUrls": ["https://example-store.com/collections/sale"],
  "onlyAvailable": true,
  "variantMode": "flat"
}
```

Several stores, first 200 products each, with tags and a contact in the User-Agent:

```json
{
  "storeUrls": ["store-one.com", "store-two.com"],
  "maxProductsPerStore": 200,
  "includeTags": true,
  "contact": "https://example.com/contact"
}
```

| Field | What it does |
|---|---|
| `storeUrls` | Store URLs or collection URLs (max 100) |
| `maxProductsPerStore` | Cap per store (default 1,000) |
| `variantMode` | `nested` (default), `flat` or `none` |
| `onlyAvailable` | Skip products with no available variant |
| `includeTags` | Include merchant tags (default off) |
| `delayMs` | Delay between pages, minimum 250 ms (default 500) |
| `maxRuntimeSecs` | Stop after this many seconds (default 3,600) |
| `contact` | Optional URL or email added to the User-Agent |

### What it costs

Pay per event. You pay only for results, and the price is the same on every Apify plan.

| Event | Price |
|---|---|
| Product returned | $0.90 per 1,000 products ($0.0009 each) |
| Run start | $0.005 per run |

Platform usage (compute) is included in these prices, so there is no separate bill for it. The pricing table at the top of the Actor page is Apify's standard layout and lists every plan (Free, Starter, Scale, Business). Our price is the same in every column, so you never pay more or less because of your plan.

Examples, each for one run:

| You get | Results | Run start | Total |
|---|---|---|---|
| 100 products | $0.09 | $0.095 |
| 1,000 products | $0.90 | $0.905 |
| 10,000 products | $9.00 | $9.005 |

Filters and caps (limits such as the per-run result caps and your input filters) only reduce the number of results you pay for. Set a maximum cost on the run in Apify if you want a hard spending limit.

### FAQ

**Does it work on every Shopify store?** On stores whose products feed is public and allowed by robots.txt. Others are skipped and reported in `SUMMARY`.

**Can I scrape one collection?** Yes. Use a URL like `https://store.com/collections/sale`.

**Do I need proxies?** No.

**Does it download images?** No. It returns image URLs only.

**Does it return stock counts?** It returns whether each variant is available, not the quantity.

**How do I monitor price changes?** Schedule the Actor and compare runs by `productId` or `variantId`. The `onSale` flag and `compareAtPriceMax` show discounts.

**Why was my store skipped?** Open `SUMMARY`. The reason is one of: not a Shopify store or feed unavailable, disallowed by robots.txt, or robots.txt could not be fetched.

### Limits

- Up to 100 stores per run, up to 100,000 products per store, up to 400 pages per store.
- Up to 250 variants per product are returned.
- Each response is capped at 20 MB.
- Only public http(s) hosts on ports 80 and 443 are fetched. Private, loopback and link-local addresses are blocked, and redirects must stay on the same site.
- Descriptions are plain text. Titles, tags and other fields come from the store, so still escape them before showing them in HTML. Control characters, HTML tags and script/style blocks are removed from them, and text is limited to a sensible length.
- **Spreadsheets:** text fields that a spreadsheet would read as a formula (starting with `=` or `@`, or with `+` / `-` that is not followed by a digit) get a leading apostrophe, so opening the CSV or Excel export cannot run them. Normal values are untouched.
- The same store or collection entered twice (any capitalisation of the collection name, with or without `www.`) is read and billed once.
- With `variantMode` `flat`, each variant row is one billed result, so a product with many variants produces many results. Use `maxProductsPerStore` and a maximum cost per run to bound spend.

### Responsible use, legal and data protection

This Actor retrieves publicly available data only. It is not affiliated with, endorsed by or sponsored by Shopify Inc. or any store you retrieve data from. All trademarks belong to their owners and are used only to describe compatibility.

Vee provides tools only; you are fully responsible for how you use them and the results. The tool is provided as is, without warranty, and Vee gives no legal advice. You are fully responsible for your use of the data: comply with the terms of service, robots.txt, copyright and database rights of the sites you target, and with all applicable law, including data protection laws (GDPR, UK GDPR and similar). Product titles, descriptions and images are copyrighted by the merchant unless the merchant says otherwise. Check each store's terms: many merchants prohibit automated access. Have a lawful basis before you store or reuse any personal data, minimise it and delete it when no longer needed, and do not use it for unsolicited contact. Do not republish copyrighted text or images without permission, and do not use this Actor to copy product copy or images to another shop. Vee does not collect or keep your inputs or results; they stay in your Apify account. Vee reserves the right to change or withdraw the Actor if a site owner objects. Get your own legal review before commercial use. To raise a concern, open an issue on this Actor's Issues tab on Apify.

### Support

Questions, a bug or a feature request? Open an issue on this Actor's Issues tab on Apify and we will reply there.

# Actor input Schema

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

Shopify store URLs, or a collection URL like https://store.com/collections/sale

## `maxProductsPerStore` (type: `integer`):

Cap per store.

## `variantMode` (type: `string`):

How to output variants. Flat mode = one result per variant.

## `onlyAvailable` (type: `boolean`):

Skip products with no available variant.

## `includeTags` (type: `boolean`):

Include the merchant's product tags. Tags can contain merchant-internal labels, so they are off by default.

## `delayMs` (type: `integer`):

Politeness delay between pages (minimum 250 ms). robots.txt Crawl-delay is honoured if larger.

## `maxRuntimeSecs` (type: `integer`):

Stop gracefully after this many seconds.

## `contact` (type: `string`):

Optional. Added to the User-Agent so store owners can reach you.

## Actor input object example

```json
{
  "storeUrls": [
    "https://theme-dawn-demo.myshopify.com"
  ],
  "maxProductsPerStore": 1000,
  "variantMode": "nested",
  "onlyAvailable": false,
  "includeTags": false,
  "delayMs": 500,
  "maxRuntimeSecs": 3600
}
```

# Actor output Schema

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

Shopify products with prices, sale prices, variants, SKUs and stock status in the default dataset, ready to export as CSV, JSON or Excel.

## `summary` (type: `string`):

SUMMARY record with run statistics and any inputs that were skipped.

# 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://theme-dawn-demo.myshopify.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("veelabs/vee-shop").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://theme-dawn-demo.myshopify.com"] }

# Run the Actor and wait for it to finish
run = client.actor("veelabs/vee-shop").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://theme-dawn-demo.myshopify.com"
  ]
}' |
apify call veelabs/vee-shop --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,veelabs/vee-shop"
        }
    }
}
```

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/34NYZLvMpeCTwt4w8/builds/z6fcEvQWZVkbvaLbN/openapi.json
