Guide
Shopify products.json: the complete guide
9 min read
Almost every public Shopify store quietly publishes its entire product catalog as JSON - at /products.json. It's the same data the storefront uses to render pages, it needs no API key or login, and anyone can read it in a browser. This guide covers everything: what the endpoint returns field by field, exactly how pagination and the 250-per-page limit work, ready-to-copy code in curl, Python and JavaScript, what you can't get from it, what to do when a store blocks it, whether it's legal, and how to watch it for changes.
What is /products.json?
Shopify exposes a public, read-only JSON feed of a store's published products at the /products.json path of the store's own domain. It is a storefront endpoint - the same data Shopify uses to build product pages - so it is meant to be publicly accessible and requires no authentication.
To see any store's catalog, just add /products.json to its domain and open it in a browser:
https://examplestore.com/products.json
Public products.json vs the Admin API
These two get confused constantly. They are completely different things:
| Public /products.json | Admin API | |
|---|---|---|
| Who can use it | Anyone | Only the store owner / their app |
| Needs an API key | No | Yes (authenticated) |
| What it shows | Published products, variants, prices, availability | Everything incl. drafts, inventory counts, orders, costs |
| Rate limits | Soft - just be polite | Formal API call limits |
| Use case here | Reading a competitor's public catalog | Managing your own store |
What data does products.json return?
The response is a JSON object with a single products array. Each item is a product, and each product contains a list of its variants. At the product level you get:
- id - the product's numeric Shopify ID
- title and handle (the URL slug used at /products/{handle})
- vendor and product_type
- tags - the store's own tags for the product
- published_at, created_at, updated_at - timestamps you can use to spot new and recently-changed products
- images - the product's image URLs
- options - the option names (e.g. Size, Color)
- variants - the array below
The variant fields (where the useful data is)
Each variant is a specific buyable version of the product (a size, a color, a bundle). For competitor research this is the part that matters:
- price - the current selling price
- compare_at_price - the 'was' price shown struck-through during a sale (null when not on sale)
- sku - the store's stock-keeping unit
- title + option1/option2/option3 - e.g. 'Large / Black'
- available - true/false, whether the variant can currently be bought
- requires_shipping, taxable, grams (weight)
- created_at, updated_at - when the variant was added or last changed
A trimmed sample response
Here's roughly what the JSON looks like, shortened to one product with one variant:
{
"products": [
{
"id": 1234567890,
"title": "Wool Runner",
"handle": "wool-runner",
"vendor": "Example",
"product_type": "Shoes",
"created_at": "2026-01-10T08:00:00-05:00",
"updated_at": "2026-10-01T12:30:00-04:00",
"tags": ["bestseller"],
"variants": [
{
"id": 9876543210,
"title": "Grey / 10",
"sku": "WR-GRY-10",
"price": "98.00",
"compare_at_price": "135.00",
"available": true
}
],
"images": [ { "src": "https://cdn.shopify.com/..." } ]
}
]
}Pagination and the 250 limit
By default /products.json returns only the first 30 products. You raise that with the limit parameter, up to a maximum of 250 per page, and walk through the rest with the page parameter. Keep requesting pages until one comes back with an empty products array - that's the end of the catalog.
https://examplestore.com/products.json?limit=250&page=1 https://examplestore.com/products.json?limit=250&page=2 https://examplestore.com/products.json?limit=250&page=3 ...stop when "products": [] is returned
Pull it with code
Reading it in a browser is fine for a quick look. To actually collect the data, here are three copy-paste starting points. Each fetches the first page; wrap them in a loop over the page number to get everything.
# curl
curl -s 'https://examplestore.com/products.json?limit=250&page=1'
# Python
import requests
r = requests.get('https://examplestore.com/products.json',
params={'limit': 250, 'page': 1},
headers={'User-Agent': 'Mozilla/5.0'})
products = r.json()['products']
# JavaScript (Node or browser)
const res = await fetch('https://examplestore.com/products.json?limit=250&page=1');
const { products } = await res.json();Useful endpoints and variations
products.json isn't the only public JSON a Shopify store exposes. These are the most useful relatives:
- /products/{handle}.json - one product by its handle (great for watching a single item)
- /collections/{handle}/products.json - just the products in one collection
- /collections/all/products.json - the automatic 'all products' collection, a handy fallback
- /collections/all?sort_by=best-selling - orders the catalog by best-selling, a rough demand signal
- /collections/all?sort_by=created-descending - newest products first, to spot latest drops
- /sitemap.xml - links to sitemap_products_*.xml files listing every product URL
What you CAN'T get from products.json
It's important to be honest about the limits, because a lot of guides oversell it. The public endpoint only exposes published storefront data. You cannot see:
- Exact inventory numbers - you get the available boolean (in/out of stock per variant), never the actual quantity on hand
- Draft or unpublished products - only products that are live on the storefront appear
- Sales figures or revenue - no order, traffic, or conversion data is ever exposed
- Cost, margin, or supplier info - that lives in the Admin API, which only the owner can access
When products.json doesn't work
A few stores won't return data. The usual reasons and fixes:
- Password-protected store - the storefront is gated, so you get the password page instead of JSON. There's no public data to read until it opens.
- Endpoint disabled - rare, but some stores block /products.json. Try /collections/all/products.json as a fallback; it's a different route and often still works.
- 404 or a redirect - you may be on a non-Shopify domain, or a marketing redirect. Confirm the store is Shopify first (see below).
- 429 Too Many Requests - you're being rate-limited. Slow down and space out requests.
How to confirm a store is actually Shopify
These endpoints only exist on Shopify. The fastest check is simply whether /products.json returns JSON. You can also view the page source and look for a reference to cdn.shopify.com or Shopify.theme, or check for an X-ShopId response header - nearly every Shopify storefront has them.
Etiquette, rate limits, and legality
The data is public, but good manners still apply: send a normal User-Agent, space your requests out, cache responses, and don't hammer a store with hundreds of rapid hits.
On legality: comparing competitors' public prices is a long-established, legitimate practice, and courts have generally held that accessing publicly available data isn't 'unauthorized access.' Where you need care is HOW you collect it (gently, public data only, no bypassing logins) and what you do with it (use the facts, don't republish a competitor's copyrighted images or copy). It's not legal advice, and there's real nuance.
From a one-off peek to continuous monitoring
Reading products.json once tells you what a store sells today. The real value is in the changes over time: a price drop, a sell-out, a new launch, a removal, a sale starting. Catching those by hand means fetching every page of every competitor, storing each snapshot, and diffing it against yesterday's - forever.
That's exactly what Shop Sentry automates. You paste a competitor's Shopify domain, it discovers the full catalog from products.json automatically, and it alerts you by email or Telegram the moment a price, stock level, or product changes - with the before/after value. No scripts, no spreadsheets, no cron jobs.
Frequently asked questions
How many products does products.json return?
Up to 250 per page with ?limit=250 (the default is 30). For stores with more than 250 products, paginate with ?page=2, ?page=3 and so on until a page returns an empty products array.
Do I need an API key or app to use it?
No. products.json is a public storefront endpoint - no API key, no app, no login. That's different from Shopify's Admin API, which requires authentication and is only for the store owner.
Can I see a competitor's exact inventory or sales?
No. You get a per-variant 'available' true/false, but never the exact quantity on hand, and never any sales, revenue, or traffic data. Those require the Admin API that only the owner can access.
Why does a store's products.json return a 404 or the password page?
Either the store is password-protected, the endpoint is disabled (try /collections/all/products.json instead), or it isn't a Shopify store. Confirm the platform first.
How often does products.json update?
It reflects the live storefront, so it updates as soon as the store changes a price, adds a product, or a variant sells out. To catch those changes you have to re-fetch and compare over time - which is what Shop Sentry does for you.
