# Shopify Products Scraper (`scraper-engine/shopify-products-scraper`) Actor

Shopify Products Scraper extracts publicly available product data from Shopify stores, including titles, prices, variants, images, descriptions, and availability. Use it for catalog analysis, research, or monitoring with fast runs and structured results.

- **URL**: https://apify.com/scraper-engine/shopify-products-scraper.md
- **Developed by:** [Scraper Engine](https://apify.com/scraper-engine) (community)
- **Categories:** E-commerce, Automation, Lead generation
- **Stats:** 5 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.99 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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 Product Scraper — Variants, Prices, SKUs and Stock

Shopify Products Scraper reads a Shopify store's public product catalogue and returns every product with its complete variant list — SKU, per-variant price, stock status and options — plus price range, currency, vendor, tags and image URLs, as structured JSON. No login or Shopify account is required: it reads the same public JSON feed the storefront itself publishes to browsers. Point it at one or more store URLs and start the run below.

***

### What is Shopify Products Scraper?

Shopify Products Scraper is an Apify Actor that crawls a public Shopify storefront's product catalogue and returns one row per product — with the full variant array nested inside — plus one honest coverage-summary row per store explaining exactly what was and wasn't reachable. It reads the same public `products.json` and `products/<handle>.json` feeds a browser loads, so no Shopify account, API key or store login is required. It's built for e-commerce analysts, dropshippers, competitor-pricing teams and AI engineers who need a store's catalogue as clean, typed JSON rather than scraped HTML.

***

### What Shopify product data is publicly available to scrape?

Any Shopify store that publishes its catalogue feed exposes full product and variant data — title, price, stock and options — to any visitor's browser without a login. Order data, customer data and checkout are never public; they sit behind Shopify's own admin authentication, which this Actor never touches.

| Data category | Publicly available | Restricted |
| --- | --- | --- |
| Product title, price, images, URL | ✅ | — |
| Full variant list (SKU, per-variant price, options, stock) | ✅ | — |
| Vendor, product type, tags | ✅ | — |
| Inventory quantity per variant | ⚠️ Only when the store chooses to publish it | Left `null` on stores that don't |
| Barcode per variant | ⚠️ Only when the store chooses to publish it | Left `null` on stores that don't |
| Product description / full HTML body | ❌ Not returned by this Actor | Present on the storefront, outside this Actor's output |
| Catalogue on a password-protected store | ❌ | Store password |
| Catalogue on a headless/custom-frontend store | ❌ | No public JSON feed exposed |
| Order, customer and checkout data | ❌ | Shopify Admin authentication (store owner only) |

Shopify Products Scraper only returns publicly visible data — what any visitor sees. Nothing behind a login, store password or admin credential.

***

### What data can I extract with Shopify Products Scraper?

Each product row carries identity and descriptive fields, computed pricing and stock metrics, and a nested variant/option/image structure. A second row type — one per store — reports catalogue coverage.

| Field | Description |
| --- | --- |
| `type` | Row type: `"product"` for a product row |
| `store_url` / `store_domain` | The storefront this product came from |
| `store_status` | `"ok"` on every product row |
| `product_url` | Public URL of the product page |
| `handle` | The product's URL slug |
| `product_id` | Shopify's numeric product ID |
| `title` | Product title |
| `vendor` | Vendor/brand as published by the store, or `null` |
| `product_type` | Store-assigned product type, or `null` |
| `tags` | Array of tags |
| `created_at` / `updated_at` / `published_at` | Store-published timestamps |
| `detail_enriched` | `true` if the per-product detail record was also fetched |
| `scraped_at` | UTC timestamp this row was collected |

#### 💰 Pricing, stock and variant metrics

| Field | Description |
| --- | --- |
| `price` | Decimal-string price of the cheapest real variant |
| `compare_at_price` | Compare-at price of that same cheapest variant, or `null` |
| `price_min` / `price_max` | Lowest / highest variant price as numbers |
| `price_varies` | `true` when variants don't all share one price |
| `currency` | ISO currency code, or `null` if the store doesn't publish one |
| `discount_percent` | Percentage discount, only when the compare-at price is genuinely higher than price — see note below |
| `is_on_sale` | `true` when `discount_percent` is not `null` |
| `available` | `true` if at least one variant is in stock |
| `variant_count` | Number of variants on the product |
| `in_stock_variant_count` / `out_of_stock_variant_count` | Variant stock rollup, or `null` when no variant published an `available` flag |
| `availability_ratio` | In-stock variants ÷ total variants with a known flag |
| `sku_count` | How many variants carry a SKU |

> `discount_percent` is genuinely absent, not zero, when there's no real markdown. The source code notes a live measurement where 75% of one store's variants publish a compare-at price *lower* than the selling price — a percentage computed from that would be negative and meaningless, so it comes back `null` instead of a fake number.

#### 🧩 Variants, options and images

| Field | Description |
| --- | --- |
| `variants` | Full array — see the [Output](#-output) example below for every nested key |
| `options` | Array of `{ name, position, values }` — the store's declared option matrix |
| `option_names` | Flat array of option names, e.g. `["Color", "Size"]` |
| `image_count` | Number of image URLs on the product |
| `featured_image` | First image URL, or `null` |
| `image_urls` | Every image URL — URLs only, no files are downloaded |

#### 🤖 Add-on: Need additional Shopify data?

Pair this with **Shopify Merchant Scraper** for a store's contact details, socials and tech stack, or **Shopify Store Scraper** for a store-level profile (theme, installed apps, catalogue size). For product data pulled straight from a store's homepage or collection pages instead of the catalogue feed, see **Shopify Scraper**.

***

### How does Shopify Products Scraper differ from the official Shopify API?

Shopify's own Admin and Storefront APIs only return data for stores that have issued *you* an access token — you install an app on that store, or the store owner authorizes it via OAuth, before a single request works ([shopify.dev, checked 2026-08-15](https://shopify.dev/docs/apps/auth/admin-app-access-tokens)). That makes them unusable for reading a catalogue you don't own or control, which is exactly what most competitor research, sourcing and market-monitoring use cases need.

| Feature | Shopify Admin/Storefront API | Shopify Products Scraper |
| --- | --- | --- |
| Read a store you don't own | ❌ needs an access token that store issues you | ✅ reads the public JSON feed any visitor's browser gets |
| Multiple unrelated stores in one run | ❌ separate token per store | ✅ one run, any number of `startUrls`, independent per store |
| Rate limit (standard plan) | 2 requests/second, leaky bucket, 40-request bucket (REST); cost-based points/second by plan tier — 100 (Standard), 200 (Advanced), 1000 (Plus), 2000 (Enterprise) for GraphQL ([shopify.dev, checked 2026-08-15](https://shopify.dev/docs/api/admin-rest/usage/rate-limits)) | Self-paced with jittered request gaps and automatic connection escalation on refusal — no fixed number to configure |
| Setup | Create/install an app, complete OAuth or generate a custom-app token, per store | Paste a store URL and run |
| Output shape | Raw REST/GraphQL objects you normalize yourself | Pre-computed rollups (price range, stock counts, SKU coverage) on every row |

Use the Admin API when the store is yours and you need write access, orders or checkout data. Use Shopify Products Scraper for read-only catalogue collection across stores you don't control.

***

### How to use Shopify Products Scraper

1. Open Shopify Products Scraper on the Apify Store and click **Try for free** (or **Start**, if you already have it saved) to open the input form in Apify Console.
2. Fill in `startUrls` with at least one Shopify storefront address — a full URL, a bare domain, or a `.myshopify.com` address all work.
3. Optionally raise `maxItems` above the store's catalogue size if you want the whole thing, or leave the default to sample a store first.
4. Leave `proxyConfiguration` on its default unless you specifically need to route through a chosen Apify Proxy selection.
5. Click **Start**, then open the run's dataset and export it as JSON, CSV, Excel or the other formats Apify supports.

#### How to scale to bulk product extraction

`startUrls` is an array — add as many storefront addresses as you need in one run. Each store is crawled independently: one store returning `password_protected` or `unreachable` never stops the others from completing, and every store gets its own product rows plus its own coverage-summary row so you can tell at a glance which ones succeeded.

***

### What can you do with Shopify product data?

- 🏷️ **Competitor price monitoring** — pricing analysts use `price`, `price_min`, `price_max` and `compare_at_price` to track a rival's price bands and discount depth across a catalogue.
- 📦 **Assortment and inventory research** — merchandisers use `variant_count`, `in_stock_variant_count` and `availability_ratio` to see how deep a competitor's size/color matrix runs and how much of it is actually in stock.
- 🔍 **Dropship and supplier sourcing** — resellers use `sku_count`, `vendor` and the per-variant `sku` inside `variants[]` to shortlist suppliers with full SKU coverage before reaching out.
- 📊 **Market and category analysis** — analysts aggregate `product_type`, `tags` and `price` across many stores in one run to map price bands and category mix in a niche.
- 🤖 **AI agent and RAG pipelines** — AI engineers feed `title`, `product_type`, `tags` and `variants[]` into a vector store or agent tool so an LLM can answer product questions or compare catalogues without re-scraping. The typed JSON output is a ready tool response for any agent framework that can call the Apify API.

***

### How does Shopify Products Scraper handle rate limits and blocking?

Requests start on a direct connection with a jittered pacing gap between calls. A request that gets a soft-refusal status (`403`, `406`, `407`, `409`, `429`, `451`, `500`, `502`, `503`, `504`) or no response at all is retried up to 4 attempts total, with capped exponential backoff (`min(2^attempt + jitter, 8s)`), and the connection steps up to Apify Proxy — first a datacenter tier, then residential — the first time a store pushes back. Once it steps up it stays up for the rest of the run; escalation is monotonic and shared across all concurrent requests to that run. A `404` or `401` is treated as deterministic (dead store / password-locked) and is never retried. If you set your own `proxyConfiguration`, that selection is used for every request instead of the automatic ladder.

If a store's catalogue can't be completed, whatever was already collected is still returned and the store's summary row reports `partial_interrupted` coverage rather than silently truncating with no explanation.

***

### ⬇️ Input

| Parameter | Required | Type | Description | Example value |
| --- | --- | --- | --- | --- |
| `startUrls` | Yes | array | One or more Shopify storefront addresses, e.g. `https://tentree.com` or `https://nuts.com`. A bare domain or a `.myshopify.com` address works too. Each store is scraped independently and gets its own coverage summary row. | `["https://tentree.com/"]` |
| `maxItems` | No | integer | Upper limit on how many products to collect from each store. Minimum `1`, maximum `100000`, default `10`. Set it above the store's catalogue size to collect everything. | `500` |
| `proxyConfiguration` | No | object | Optional. Leave the default unless you need the run to go out through a specific Apify Proxy selection. | `{"useApifyProxy": false}` |

⚠️ Shopify's own catalogue-listing endpoint silently clamps any page-size request above 250 items back down to 250 — this Actor walks it page by page rather than requesting more at once, so it is never affected, but it's why `maxItems` values above a store's true size return everything the store has rather than erroring.

#### Example input

```json
{
  "startUrls": [
    "https://tentree.com/",
    "https://nuts.com/"
  ],
  "maxItems": 500,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

***

### ⬆️ Output

Results land in the Actor's dataset as typed JSON, exportable as JSON, CSV, Excel, XML, RSS or HTML directly from Apify Console or via the API. Two row types share the same dataset, told apart by `type`. Only `type: "product"` rows are billed, under the `row_result` event — filter `type == "store_summary"` to see the free coverage rows, or `type == "product"` to see only what you were charged for.

Prices are always major-unit decimal strings (`"45.00"`, not cents) — the Actor reads only the storefront surfaces that publish prices that way.

#### Example output

**Product row** (`type: "product"`):

```json
{
  "type": "product",
  "is_child": false,
  "store_url": "https://tentree.com/",
  "store_domain": "tentree.com",
  "store_status": "ok",
  "product_url": "https://tentree.com/products/treeblend-baker-t-shirt-cottage-red-heather",
  "handle": "treeblend-baker-t-shirt-cottage-red-heather",
  "product_id": 8462674067642,
  "title": "TreeBlend Baker T-Shirt",
  "vendor": "tentree",
  "product_type": "Mens",
  "tags": ["basics", "mens", "organic-cotton", "t-shirts-tanks"],
  "price": "45.00",
  "compare_at_price": null,
  "price_min": 45.0,
  "price_max": 45.0,
  "price_varies": false,
  "currency": "USD",
  "discount_percent": null,
  "is_on_sale": false,
  "available": true,
  "variant_count": 5,
  "in_stock_variant_count": 5,
  "out_of_stock_variant_count": 0,
  "availability_ratio": 1.0,
  "sku_count": 5,
  "variants": [
    {
      "variantId": 46195127550138,
      "title": "COTTAGE RED HEATHER / S",
      "sku": "TCM1869-6298-S",
      "barcode": "661814898315",
      "price": "45.00",
      "priceValue": 45.0,
      "compareAtPrice": null,
      "currency": "USD",
      "available": true,
      "inventoryQuantity": null,
      "inventoryPolicy": null,
      "option1": "COTTAGE RED HEATHER",
      "option2": "S",
      "option3": null,
      "position": 1,
      "grams": 400,
      "weight": 400.0,
      "weightUnit": "g",
      "requiresShipping": true,
      "taxable": true,
      "createdAt": "2026-07-24T16:29:11-07:00",
      "updatedAt": "2026-08-08T02:58:27-07:00"
    }
  ],
  "options": [
    { "name": "Color", "position": 1, "values": ["COTTAGE RED HEATHER"] },
    { "name": "Size", "position": 2, "values": ["S", "M", "L", "XL", "XXL"] }
  ],
  "option_names": ["Color", "Size"],
  "image_count": 3,
  "featured_image": "https://cdn.shopify.com/s/files/1/2341/3995/files/tree_blend_baker_2.jpg",
  "image_urls": [
    "https://cdn.shopify.com/s/files/1/2341/3995/files/tree_blend_baker_2.jpg"
  ],
  "created_at": "2026-07-24T16:29:11-07:00",
  "updated_at": "2026-08-08T02:58:27-07:00",
  "published_at": "2026-08-04T07:00:03-07:00",
  "detail_enriched": true,
  "scraped_at": "2026-08-08T14:21:09Z"
}
```

**Store coverage summary row** (`type: "store_summary"`) — always exactly one per store, including stores that returned nothing:

```json
{
  "type": "store_summary",
  "is_child": false,
  "store_url": "https://tentree.com/",
  "store_domain": "tentree.com",
  "store_status": "ok",
  "status_detail": "Catalogue read successfully.",
  "catalogue_coverage": "complete",
  "catalogue_total": 412,
  "products_returned": 412,
  "variant_total": 1863,
  "detail_enriched_count": 412,
  "currency": "USD",
  "sku_coverage_percent": 97.31,
  "inventory_coverage_percent": 41.06,
  "scraped_at": "2026-08-08T14:22:47Z"
}
```

`store_status` on the summary row is one of `ok`, `password_protected`, `no_published_products`, `catalogue_unavailable`, `not_shopify`, `store_not_found` or `unreachable`. `catalogue_coverage` is one of `complete`, `limited_by_max_items`, `partial_interrupted` or `none`. `catalogue_total` is populated only when `catalogue_coverage` is `complete` — if `maxItems` cut the run short, the store's true size is unknown and is reported as `null` rather than guessed.

***

### How does it work?

Shopify Products Scraper calls the same JSON endpoints a browser loads when it visits a storefront — the catalogue listing (`/products.json`, paginated 250 products at a time, up to 400 pages) and, for every product found, the individual product record (`/products/<handle>.json`) that fills in barcode, inventory quantity and currency. No browser is launched and no JavaScript is rendered — these are direct HTTP calls against Shopify's own public data surfaces. Requests start on a direct connection and only route through Apify Proxy if the store pushes back; only publicly visible data is ever returned, nothing behind a password or admin login. Because every store publishes this feed in the same shape, the output schema stays the same regardless of which storefront theme a store is running.

***

### Integrations

Shopify Products Scraper works with the tools you already use for automation and analysis — no plugin required, since it's just an Apify Actor reachable over the standard Apify API.

#### Calling Shopify Products Scraper programmatically

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_API_TOKEN>")

run = client.actor("scraper-engine/shopify-products-scraper").call(run_input={
    "startUrls": ["https://tentree.com/"],
    "maxItems": 500,
})

for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["title"], item.get("price"))
```

Works in Go, Ruby, Node.js, cURL — any language that can make an HTTP request against the Apify API.

#### No-code tools (n8n, Make, LangChain)

In **n8n**, use the HTTP Request node pointed at the Actor's run-sync-get-dataset-items endpoint with your API token, or the dedicated Apify node if you have it installed. In **Make**, the Apify app's "Run an Actor" module accepts the same `startUrls` / `maxItems` input and returns the dataset items to the next module. In **LangChain**, the `ApifyWrapper` / Apify Actor tool can call this Actor and load its dataset directly as `Document` objects for a retrieval pipeline.

***

### Is it legal to scrape Shopify products?

Scraping publicly available product data is generally lawful — this Actor returns only what any visitor's browser can already load from a storefront, without bypassing a login, password or paywall. Product catalogues, prices and SKUs are business/product data, not personal data, so GDPR and CCPA do not attach to this Actor's output; the relevant frameworks instead are a store's terms of service and database-rights protections that may apply to a compiled catalogue in some jurisdictions. Consult legal counsel if your use case involves bulk storage or redistribution of a store's catalogue data.

***

### Frequently asked questions

#### What Shopify product fields does Shopify Products Scraper return?

The top fields are `title`, `price` (with `price_min`/`price_max`), `variants` (SKU, per-variant price and stock), `tags` and `available`. See [What data can I extract](#what-data-can-i-extract-with-shopify-products-scraper) above for the full field tables.

#### Does Shopify Products Scraper require a Shopify account or login?

No. It reads the storefront's public catalogue feed exactly as a browser would — no Shopify account, API key or store login is needed. A store locked behind a *store password* cannot be read; that's reported as `password_protected` rather than silently returning nothing.

#### How many products can I extract in one run?

Up to `maxItems` per store (max `100000`). Each store is crawled to completion, `maxItems`, or the point where the catalogue feed stops answering — whichever comes first — and the coverage summary row tells you which one happened.

#### What happens if a product's detail record can't be fetched?

The product still comes back from the catalogue listing with `detail_enriched: false`. Fields only available on the detail record — `barcode`, `inventoryQuantity`, `weight`, and the `currency` sourced from that record — are `null` for that product, while everything from the catalogue surface (price, stock, options, images) is still populated.

#### Can I scrape multiple Shopify stores at once?

Yes. `startUrls` accepts an array of storefront addresses; each store is scraped independently in the same run and gets its own summary row, so one problem store never blocks the rest.

#### Does Shopify Products Scraper work with Claude, ChatGPT and other AI agent tools?

Yes, as an HTTP endpoint any agent framework can call through the Apify API — the JSON output is typed and consistent, so it drops straight into a tool call or RAG pipeline without extra parsing.

#### What makes Shopify Products Scraper different from a basic Shopify catalogue scraper?

It returns the full variant array on every product — not just one headline price — with SKU, price and stock per variant, and it reports an honest store status (`password_protected`, `catalogue_unavailable`, `not_shopify`, etc.) instead of returning an empty result when a store can't be read.

#### Does Shopify Products Scraper return data in a format LLMs can use directly?

Yes. Typed, normalized JSON with consistent field names across runs — no HTML parsing, no selectors. Pass it directly to an LLM, index it into a vector store, or feed it to an agent tool.

#### What happens when Shopify changes a store's layout or theme?

The catalogue feed this Actor reads is Shopify's own stable JSON data surface, not the rendered storefront page, so a theme or layout change on the store doesn't change the output schema. No specific update turnaround is published for anti-bot changes on Shopify's side.

#### Can I use Shopify Products Scraper without managing proxies or browser infrastructure?

Yes. There's no browser to configure — requests are direct HTTP calls — and connection escalation through Apify Proxy happens automatically if a store pushes back, with no proxy setup required unless you want to pick a specific configuration yourself.

#### Which fields work best for AI training data and RAG indexing?

For RAG, index `title`, `product_type`, `tags` and `vendor` as descriptive text alongside `variants[]` for structured lookups. For training data, `price`, `variant_count`, `sku_count` and `availability_ratio` are the most consistently structured numeric fields across every row. All fields return as typed primitives — strings, numbers, booleans or arrays — never mixed types.

***

### Related scrapers

| Scraper | What it extracts |
| --- | --- |
| Shopify Merchant Scraper | B2B lead sheet per store — merchant identity, emails, phones, social profiles, product/collection counts and tech stack |
| Shopify Store Scraper | Store-level profile — identity, theme, installed apps, catalogue size, collection count and price range |
| Shopify Scraper | Product data pulled from a store's homepage, collection pages or single product pages |
| Amazon Product Details Scraper | Product details from Amazon listing pages |
| eBay Product Listing Scraper | Product data from eBay search and listing pages |
| Amazon Price Tracker | Amazon prices, ratings, reviews, sellers and stock |

***

### Your feedback

Found a bug, or missing a field you need from a Shopify store's catalogue? Open an issue on this Actor's **Issues** tab in Apify Console — that's the fastest way to reach the maintainer directly.

# Actor input Schema

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

One or more Shopify storefront addresses, for example https://tentree.com or https://nuts.com. A bare domain or a .myshopify.com address works too. Each store is scraped independently and gets its own coverage summary row.

## `maxItems` (type: `integer`):

Upper limit on how many products to collect from each store. Large catalogues take proportionally longer, so raise this only as far as you need. Set it above the store's catalogue size to collect everything.

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

Optional. Leave the default unless you need the run to go out through a specific Apify Proxy selection.

## Actor input object example

```json
{
  "startUrls": [
    "https://tentree.com/"
  ],
  "maxItems": 10,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

All scraped items in the Actor's default dataset: product rows and per-store summary rows together.

## `storeSummary` (type: `string`):

Only the per-store summary rows: catalogue coverage, product/variant totals and SKU/inventory coverage for each scraped store.

## `runSummary` (type: `string`):

The OUTPUT record written at the end of the run: per-store status, catalogue coverage and product counts.

# 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 = {
    "startUrls": [
        "https://tentree.com/"
    ],
    "maxItems": 10,
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("scraper-engine/shopify-products-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 = {
    "startUrls": ["https://tentree.com/"],
    "maxItems": 10,
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("scraper-engine/shopify-products-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 '{
  "startUrls": [
    "https://tentree.com/"
  ],
  "maxItems": 10,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call scraper-engine/shopify-products-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,scraper-engine/shopify-products-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/xVL9CeuMa5Rz4LX3P/builds/xtTXIrdCyR6zSA5zi/openapi.json
