# Shopify Products API & Scraper (`sourcing-data-studio/shopify-products-api`) Actor

Shopify products scraper and API: enter store addresses and get one row per product with prices, compare-at prices, variants, images and availability from each store's public catalogue. No API key; pay only for saved products. Unofficial: not affiliated with or endorsed by Shopify.

- **URL**: https://apify.com/sourcing-data-studio/shopify-products-api.md
- **Developed by:** [Sourcing Data Studio](https://apify.com/sourcing-data-studio) (community)
- **Categories:** E-commerce, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.80 / 1,000 product results

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

Watch competitor Shopify stores: prices, new products and availability. Shopify Products API & Scraper reads the public product catalogue of a store and returns one clean row per product: lowest and highest price, compare-at price, currency, variants, availability and images. Schedule it daily or weekly and compare runs to see what changed, or call it from an AI agent. You pay only for products saved: $0.001 per product on the Free tier, so a typical query (one store with 250 products) costs $0.25.

Unofficial: not affiliated with or endorsed by Shopify or by any store whose feed is read.

Source: the Shopify products.json catalogue that a store serves as JSON at `/collections/all/products.json`. Shopify stores publish instructions for software agents at `/agents.md`; under "Read-Only Browsing (No Authentication Required)" these list `GET /collections/{handle}/products.json`, and the Actor reads that route for the collection `all`. That is a description of a route, not a licence: every store has its own terms.

Sellers and importers use this Shopify products scraper for Shopify price monitoring: follow the catalogues and prices of competitors, and compare the rows of two runs to see new products, price changes and stock changes. It is a Shopify scraper for whole catalogues: one request per 250 products, no browser and no API key. It works as a Shopify competitor price tracker and a Shopify catalogue export in one, and gives you clean Shopify product data for a spreadsheet, a database or an AI agent.

### What does Shopify Products API & Scraper do?

- Reads the product catalogue of each store you list, page by page, up to 250 products per page. The stores take turns: the first page of every store, then the next page of every store.
- Turns every product into one row with a price range, a currency, variants, images and a plain-text description.
- Stops a store at your **Maximum products per store** limit and the whole run at your **Maximum products in total** limit. When that total is lower than the stores together would give, they share it page by page: the first store does not fill it alone.
- Removes duplicates and rejects incomplete rows before they are saved. Two addresses of the same shop are read once.
- Charges one event per saved product. Stores that refuse the feed, duplicates and invalid rows are never charged.
- Never calls a store empty that it did not read. A store that was not read at all is marked `not-read` in the run summary and named in the status message; a run that saved nothing and left a store unread ends as failed, free of charge, and says that running again usually works.
- Gives a store that limits requests the pause it asks for, reads the other stores meanwhile, and asks it again, from the same address, at most twice.
- Writes a run summary with totals, the state of each store and failures to the key-value store.
- Works from Apify Console, the Apify API, n8n, Make and AI agents through the Apify MCP server.

### Output example

One product from a real run, shortened: tags, images and variants are cut down here (this product has 12 variants). The table below lists every field.

```json
{
  "productId": "7204718084176",
  "title": "Allbirds Wool Slipper - Light Tan",
  "handle": "allbirds-slipper",
  "vendor": "Allbirds",
  "productType": "Shoes",
  "tags": ["allbirds::gender => unisex", "allbirds::material => wool"],
  "price": 52,
  "priceMax": 52,
  "compareAtPrice": 75,
  "currency": "USD",
  "available": true,
  "variantCount": 12,
  "variants": [
    { "variantId": "41268090306640", "title": "W4.5/M3", "sku": "A12022U030", "price": 52, "compareAtPrice": 75, "available": true, "grams": 381 },
    { "variantId": "41268090339408", "title": "W5.5/M4", "sku": "A12022U040", "price": 52, "compareAtPrice": 75, "available": true, "grams": 396 }
  ],
  "imageUrl": "https://cdn.shopify.com/s/files/1/1104/4168/files/A12022_25Q4_Wool-Slipper-Light-Tan-Light-Tan-Sole_PDP_LEFT.png?v=1759335798",
  "imageUrls": ["https://cdn.shopify.com/s/files/1/1104/4168/files/A12022_25Q4_Wool-Slipper-Light-Tan-Light-Tan-Sole_PDP_LEFT.png?v=1759335798"],
  "productUrl": "https://www.allbirds.com/products/allbirds-slipper",
  "descriptionText": "We set out to create slippers no one would want to take off. With cloudlike comfort in every step, and three styles to choose from, consider your feet spoken for.",
  "createdAt": "2025-08-05T18:53:12.000Z",
  "updatedAt": "2026-10-06T03:59:33.000Z",
  "publishedAt": "2026-09-25T22:24:31.000Z",
  "store": "www.allbirds.com",
  "position": 2,
  "source": "shopify-feed",
  "retrievedAt": "2026-10-06T03:59:33.627Z"
}
```

### What data can you get with Shopify Products API & Scraper?

| Field | Type | Description |
|---|---|---|
| `productId` | string | Identifier of the product in the store (the id in the public product feed). |
| `title` | string | Product title as the store shows it, with e-mail addresses and phone numbers removed. |
| `handle` | string | URL handle of the product in the store, the last part of the product link. |
| `vendor` | string, optional | Vendor or brand name the store gives for the product, with e-mail addresses and phone numbers removed. |
| `productType` | string, optional | Product type as the store sets it, for example Shoes. |
| `tags` | array | Tags the store attaches to the product. Tags that contain an e-mail address or a phone number are dropped. |
| `price` | number | Lowest variant price in major currency units of the store currency. |
| `priceMax` | number | Highest variant price in major currency units of the store currency. |
| `compareAtPrice` | number, optional | Lowest compare-at (before discount) price among the variants that have one; null when no variant has one. |
| `currency` | string | ISO 4217 currency code of the store, read from the store's meta.json; null when the store does not tell. |
| `available` | boolean | True when at least one variant is available to buy. |
| `variantCount` | integer | Number of variants of the product. |
| `variants` | array, optional | All variants of the product. Left out when the input option Include variants is off. |
| `imageUrl` | string, optional | URL of the first product image. |
| `imageUrls` | array | URLs of all product images in the store's order. |
| `productUrl` | string | Link to the product page in the store. |
| `descriptionText` | string, optional | Plain text of the product description, at most 500 characters, with e-mail addresses and phone numbers removed. |
| `createdAt` | string, optional | ISO 8601 time the store created the product. |
| `updatedAt` | string, optional | ISO 8601 time the store last updated the product. |
| `publishedAt` | string, optional | ISO 8601 time the store published the product. |
| `store` | string | Host name of the store, for example www.allbirds.com. |
| `position` | integer | 1-based position of the product in the store's feed. |
| `source` | string | Where the row comes from: always shopify-feed, the store's public product feed. |
| `retrievedAt` | string | ISO 8601 time when the product was collected. |

Prices are numbers in the currency of the store. The currency comes from the store's own `meta.json`; if the store does not give one, `currency` is null. A compare-at price of zero in the feed means "none" and is saved as null.

### How to use Shopify Products API & Scraper

1. Click **Try for free** on this page.
2. Enter one or more **Store URLs**, for example `https://www.allbirds.com` or just `www.allbirds.com`. Only the host name is used.
3. Set **Maximum products per store** and **Maximum products in total**. The run stops as soon as the limit is reached.
4. Turn **Include variants** off if you only need the price range and a smaller row.
5. Click **Start**. Results appear in the **Output** tab while the run is going.
6. Download the results as JSON, CSV or Excel, or read them through the API.

### Input examples

#### Quick test

```json
{
  "storeUrls": [
    "https://www.allbirds.com"
  ],
  "maxItemsPerStore": 5,
  "maxItems": 5
}
```

#### Price monitoring two stores

```json
{
  "storeUrls": [
    "https://www.allbirds.com",
    "https://www.colourpop.com"
  ],
  "maxItemsPerStore": 250,
  "includeVariants": false,
  "maxItems": 500
}
```

#### Full catalogue with variants

```json
{
  "storeUrls": [
    "www.allbirds.com"
  ],
  "maxItemsPerStore": 1000,
  "includeVariants": true,
  "maxItems": 1000,
  "feedback": "Example: please add a filter for products updated since a date."
}
```

### How much does Shopify Products API & Scraper cost?

| Event | You pay for | Free | Bronze | Silver | Gold |
|---|---|---|---|---|---|
| Product result | One product from a store's public feed saved to the dataset. Stores that refuse the feed, duplicates and invalid rows are never charged. | $0.001 | $0.001 | $0.0009 | $0.0008 |

Prices are per event. Your tier follows your Apify subscription plan.

On the Free tier, $0.001 per product means:

- 100 products cost $0.10.
- 1,000 products cost $1.00.
- A typical query (one store with 250 products) is 250 products and costs $0.25.

Apify also charges a tiny start fee per run (apify-actor-start, $0.00005 per GB of memory). Stores that refuse the feed, duplicates and invalid rows are never charged. To cap your spending, set **Maximum cost per run** in the run options; the run stops cleanly when that budget is used.

### Use Shopify Products API & Scraper from the API, n8n, Make or an AI agent

#### Apify API

Run the Actor and get the products in one call:

**curl**

```bash
curl -X POST "https://api.apify.com/v2/acts/sourcing-data-studio~shopify-products-api/run-sync-get-dataset-items" \
  -H "Authorization: Bearer YOUR_APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"storeUrls":["https://www.allbirds.com"],"maxItemsPerStore":5,"maxItems":5}'
```

**JavaScript** (Node.js, `npm install apify-client`)

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

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });

const input = {
    "storeUrls": [
        "https://www.allbirds.com"
    ],
    "maxItemsPerStore": 5,
    "maxItems": 5
};

const run = await client.actor('sourcing-data-studio/shopify-products-api').call(input);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

**Python** (`pip install "apify-client>=3"`, Python 3.11 or later)

```python
import os

from apify_client import ApifyClient

client = ApifyClient(os.environ["APIFY_TOKEN"])

run_input = {
    "storeUrls": [
        "https://www.allbirds.com"
    ],
    "maxItemsPerStore": 5,
    "maxItems": 5
}

run = client.actor("sourcing-data-studio/shopify-products-api").call(run_input=run_input)

for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)
```

#### n8n and Make

Use an HTTP request module with the same URL, method POST and a JSON body like the input examples above. Store your Apify API token as a credential, never in the workflow itself.

#### AI agents through the Apify MCP server

Connect your agent to the Apify MCP server and call `sourcing-data-studio/shopify-products-api` with the same input. Agents can use the **Feedback for the developer** input field to report missing fields or problems.

### For AI agents

- Minimal input: `{"storeUrls": ["https://www.allbirds.com"], "maxItems": 10}`. 10 products cost $0.01 at the Free price, plus the platform's start fee.
- Read first: `price` (lowest variant price), `compareAtPrice`, `currency`, `available` and `productUrl`.
- Outcomes: the run status message and the `RUN_SUMMARY` record in the run's key-value store. Each store under `listings` is `ok`, `empty`, `refused`, `rate-limited`, `failed`, `duplicate`, `stopped` or `not-read`.
- Do not use it for stores that keep their catalogue private (a password page, or the route switched off) or for anything behind a login, such as orders, customers and stock counts.
- Call it through the Apify MCP server as the tool `sourcing-data-studio/shopify-products-api` and through the Apify API.

### Run summary: the state of each store

Every run writes a `RUN_SUMMARY` record to the key-value store (**Storage** tab). Its `listings` part names each store with one of these states and the number of products saved from it; `failures` gives the address, the HTTP status and the reason.

| State | Meaning |
|---|---|
| `ok` | The store was read to the end of its catalogue or to your limit. |
| `empty` | The store answered, and the first page of its catalogue has no products. Only a store that answered so is said to list no products. |
| `refused` | The store answered 401, 403 or another 4xx status, or showed a password page. It is not asked again, by this or any other means. No product result is charged for it. |
| `rate-limited` | The store kept answering 429, "too many requests from this address", also after the pauses it asked for, or it said in `x-ratelimit-remaining` that no request is left. Products saved before that stay saved. Run again later. |
| `failed` | Another error: 404 (no such route: the catalogue is switched off, or this is not a Shopify store), a server error after the retries, a security certificate that does not verify, a proxy that refuses the connection (407 means wrong proxy credentials and is not tried again), or an answer that is not a product catalogue. |
| `duplicate` | The same shop as another store of this run, listed under a second address. It was read once, under the address named in `sameStoreAs`. |
| `stopped` | The run ended before this store was finished: your product limit, your maximum cost or the time limit of the run was reached, or the run was aborted. A store in the middle of its catalogue at that moment is `stopped`, not `ok`. |
| `not-read` | Nothing was read from this store, although nothing stopped the run: no page of its catalogue was processed, so no product from it was saved. It says nothing about the store itself. Run again: that usually reads it. If every store is `not-read`, the run ends as failed and nothing is charged. |

### Limits

- Only stores that serve this catalogue route can be read. Many stores switch it off or are not Shopify stores.
- A store that answers 401, 403 or 404, or shows a password page, is recorded as refused or failed in the `RUN_SUMMARY` record and is never retried by other means. The Actor does not log in, does not use cookies and does not work around a refusal.
- The Actor sends one request at a time, about one per second, and the stores take turns: the first page of every store, then the next page of every store, and so on. The rows of several stores are therefore saved mixed, page by page (a page is up to 250 products), and not one store after the other. The `position` of a product is its place in the catalogue of its own store. If you set **Maximum products in total** lower than the stores together would give, they share it page by page; the first store does not fill it alone. A store that is cut by that limit, or by the time limit, shows `stopped`.
- Stores limit requests per IP address. When a store answers 429 it waits as long as the store asks (the `Retry-After` header, at most 60 seconds; without the header 15 and then 30 seconds), reads the other stores meanwhile, and asks that store again, from the same address, at most twice in a run. After that, or when the store asks for more than 60 seconds, the store is recorded as `rate-limited`. A 429 that comes with `x-ratelimit-remaining: 0` ends the store at once: it says that no request is left, so it is not asked again in this run.
- The default is to run without a proxy. Shared proxy addresses are used by many people and are usually over a store's limit already, so a store then answers 429 at once. If you do set a proxy, the whole run uses one address (one proxy session) and every retry leaves from that same address. It changes only when the connection through the proxy fails, never because of what a store answered. The Actor never changes address to get around a limit or a refusal.
- Sometimes the crawler ends while a request is still waiting in its queue, and the store behind that request is not read. Without a proxy, the Actor then starts the crawler again for the waiting requests, at most three times, and writes a `run.pending` line to the log for each start. With a proxy it does not: a new start would use a new proxy session, so a new address, and the run keeps one address from start to end. The run summary counts the starts and the requests that were still left under `totals` (`extraPasses` and `requestsLeft`). A store that is still not read after that is marked `not-read`.
- Server errors (5xx) and connection errors are retried a few times with a growing pause. A server error that comes with a `Retry-After` header is waited out as the store asks, at most 60 seconds, and never cut short. If the store asks for more than that, or for a pause that would end after the time limit of the run, it is not asked again in this run and is recorded as `failed` with that reason.
- A run ends by itself shortly before its time limit, so the run summary is always written.
- At most 50 stores per run and 50,000 products per run. The default is 1,000 products. At the Free price of $0.001 per product, a run of 50,000 products costs at most $50, plus the platform's start fee.
- Each store has its own terms, and none of them gives you a licence by serving this catalogue. You are responsible for using the data in line with the terms of the store and the law.
- Products are de-duplicated within a run, not across runs.
- Some stores set `updatedAt` to the time of the request, so it can change on every run even when nothing changed. Compare prices and availability, not only that field.
- To keep contact details out, long runs of digits are removed from titles, vendor, product type, variant titles and descriptions unless they are clearly a date, a year range, a list of sizes, a measurement with its unit or a code with a label such as EAN. A range like "1000-1500" without a unit, or an unlabelled 13-digit code, can therefore disappear. Tags that contain such a run are dropped.

### FAQ

#### Is this the official Shopify API?

No. Shopify Products API & Scraper is unofficial: it is an API for public Shopify store data, not the Shopify Admin API that store owners use with an access token. It reads what any visitor of a store can open, so you do not need access to the store, and it cannot read orders, customers or anything that is not public.

#### Which data does Shopify Products API & Scraper collect?

Only the public product catalogue that anyone can open without logging in: titles, prices, variants, images, tags, a short plain-text description and links. It also reads the store's `meta.json` for the currency and to recognise the same shop under two addresses. It does not log in, does not use cookies and does not read the cart, checkout, orders or customer accounts.

#### Which data is not collected?

No personal data. The Actor does not collect names of customers, contact persons, e-mail addresses, phone numbers or reviews. E-mail addresses and phone numbers are removed from the title, vendor, product type, variant titles and description of a product, also when they are disguised ("name \[at] shop \[dot] com") or written in other digits. Tags that contain one are dropped, and a SKU that contains an e-mail address or an international phone number is left empty. Descriptions are cut to 500 characters. Images are saved as links only.

#### Why did my run return 0 products?

Either the store does not serve the catalogue route, or it refused the request (401, 403 or a password page), or it limited requests from this address (429), or the address is not a Shopify store (404), or the store has no products (state `empty`: it answered, and its first page was empty), or the Actor did not read the store at all (state `not-read`, see below). The run summary in the **Storage** tab lists each store with its state and each failure. No product results are charged in this case; only the start fee of the run applies.

#### What does "not-read" mean?

`not-read` means that nothing was read from the store: its catalogue was never requested or never processed, although nothing stopped the run. It is not a statement about the store, and the store is not called empty. If no product was saved and a store is `not-read`, the run ends as failed, the status message names the store, and nothing is charged. If products were saved from other stores, the run still ends as succeeded and the message names the store that was not read. Run again: that usually reads it.

#### What do "rate-limited" and "refused" mean?

`rate-limited` is temporary: the store answered 429 because too many requests came from the address the run used. The Actor paused as the store asked and tried again from the same address, at most twice. A store that says `x-ratelimit-remaining: 0` is not asked again at all. Run again later, and do not add a shared proxy: it makes this more likely. `refused` is the store's decision: it answered 401, 403 or a password page. The Actor accepts that and does not try another way, and it never asks again from another address.

#### How do I know the currency?

The Actor reads the store's `meta.json`. If that file is missing, `currency` is null and the prices are in the store's own currency.

#### Can I schedule Shopify Products API & Scraper?

Yes. Save your input as a task and add a schedule in Apify Console, for example a daily run to track competitor prices.

### Feedback

Tell us what to add next in the **Feedback for the developer** input field, or open an issue in the **Issues** tab. Feedback is saved with the run summary.

# Changelog

This Actor's version history is a separate document: https://apify.com/sourcing-data-studio/shopify-products-api/changelog.md

# Actor input Schema

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

The Shopify stores to read, one address per line, for example https://www.allbirds.com or just www.allbirds.com. Only the host name is used: a path, a query or a port after it is ignored. Between 1 and 50 stores. A store that refuses (answers 401, 403 or shows a password page), has no public catalogue (404) or keeps limiting requests (429) is listed with its state in the RUN_SUMMARY record and no product result is charged for it. Two addresses of the same shop are read once.

## `maxItemsPerStore` (type: `integer`):

Maximum number of products to save from each store, in the order of the store's catalogue. A store is read page by page (up to 250 products per page) until this many products are saved or the catalogue ends.

## `includeVariants` (type: `boolean`):

When on, every row carries the list of variants (size, colour and so on) with price, compare-at price, SKU, availability and weight in grams. Turn it off for smaller rows; the product-level price range, variant count and availability stay.

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

Maximum number of product results to save and pay for in this run, across all stores. The run stops as soon as this many products are saved.

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

Proxy servers used for the requests. The default is no proxy, and that is the right choice for most runs: stores limit requests per IP address, and shared proxy addresses are usually over the limit already, so a store then answers HTTP 429 at once. With a proxy, the whole run uses one address (one proxy session), and a store that refuses or limits requests is never asked again from another address. The address changes only when the connection through the proxy fails.

## `maxRequestRetries` (type: `integer`):

How many times a page that failed with a server error (HTTP 5xx) or a connection error is tried again, with a growing pause. A store that limits requests (HTTP 429) gets the pause it asks for (at most 60 seconds) and is asked again, from the same address, at most twice per run, or fewer times if this number is lower; after that, or when it says x-ratelimit-remaining: 0, it is recorded as rate-limited. Refusals (401, 403, 404) are never retried. Failed pages are never charged.

## `debugLog` (type: `boolean`):

Print detailed debug messages to the run log.

## `feedback` (type: `string`):

Optional. Tell us which field or store type you need next, or what went wrong. It is saved with the run summary. Do not include personal data. AI agents may also use this field to report problems.

## Actor input object example

```json
{
  "storeUrls": [
    "https://www.allbirds.com",
    "https://www.colourpop.com",
    "https://www.deathwishcoffee.com"
  ],
  "maxItemsPerStore": 4,
  "includeVariants": true,
  "maxItems": 10,
  "proxyConfiguration": {
    "useApifyProxy": false
  },
  "maxRequestRetries": 3,
  "debugLog": false
}
```

# Actor output Schema

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

No description

## `summary` (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://www.allbirds.com",
        "https://www.colourpop.com",
        "https://www.deathwishcoffee.com"
    ],
    "maxItemsPerStore": 4,
    "maxItems": 10,
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("sourcing-data-studio/shopify-products-api").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://www.allbirds.com",
        "https://www.colourpop.com",
        "https://www.deathwishcoffee.com",
    ],
    "maxItemsPerStore": 4,
    "maxItems": 10,
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("sourcing-data-studio/shopify-products-api").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://www.allbirds.com",
    "https://www.colourpop.com",
    "https://www.deathwishcoffee.com"
  ],
  "maxItemsPerStore": 4,
  "maxItems": 10,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call sourcing-data-studio/shopify-products-api --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,sourcing-data-studio/shopify-products-api"
        }
    }
}
```

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/iY3dtbGaJMWJaqOyb/builds/ii4QM5f8hXgSxWaqO/openapi.json
