Shopify Products API & Scraper
Pricing
from $0.80 / 1,000 product results
Shopify Products API & Scraper
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.
Pricing
from $0.80 / 1,000 product results
Rating
0.0
(0)
Developer
Sourcing Data Studio
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
15 days ago
Last modified
Categories
Share
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-readin 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.
{"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
- Click Try for free on this page.
- Enter one or more Store URLs, for example
https://www.allbirds.comor justwww.allbirds.com. Only the host name is used. - Set Maximum products per store and Maximum products in total. The run stops as soon as the limit is reached.
- Turn Include variants off if you only need the price range and a smaller row.
- Click Start. Results appear in the Output tab while the run is going.
- Download the results as JSON, CSV or Excel, or read them through the API.
Input examples
Quick test
{"storeUrls": ["https://www.allbirds.com"],"maxItemsPerStore": 5,"maxItems": 5}
Price monitoring two stores
{"storeUrls": ["https://www.allbirds.com","https://www.colourpop.com"],"maxItemsPerStore": 250,"includeVariants": false,"maxItems": 500}
Full catalogue with variants
{"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
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)
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)
import osfrom apify_client import ApifyClientclient = 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,availableandproductUrl. - Outcomes: the run status message and the
RUN_SUMMARYrecord in the run's key-value store. Each store underlistingsisok,empty,refused,rate-limited,failed,duplicate,stoppedornot-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-apiand 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_SUMMARYrecord 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
positionof 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, showsstopped. - Stores limit requests per IP address. When a store answers 429 it waits as long as the store asks (the
Retry-Afterheader, 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 asrate-limited. A 429 that comes withx-ratelimit-remaining: 0ends 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.pendingline 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 undertotals(extraPassesandrequestsLeft). A store that is still not read after that is markednot-read. - Server errors (5xx) and connection errors are retried a few times with a growing pause. A server error that comes with a
Retry-Afterheader 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 asfailedwith 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
updatedAtto 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.