Home · The 15 signals · Public product feed

Shopify /products.json — the AI bulk-ingest feed

Every Shopify store has a public, paginated, fully-structured feed of its catalog at /products.json. It's the single most-ingested surface on a Shopify store and the path AI shopping agents — ChatGPT Shopping, Perplexity, Google AI Mode, Shopify's Global Catalog — use to discover what you sell at scale. In our 100-store launch scan, 40% of headless DTC brands had accidentally killed it during their Hydrogen or Next.js cutover. Their PDPs still rendered fine; their AI-readable surface had silently disappeared.

Last updated 2026-04-30 · Floor signal · 25 pts · Second-largest signal weight

25 / 170CatalogScan score weight
40%Headless DTC brands fail (100-store report)
250Products per page Shopify returns by default
What this signal scores: a public HTTP GET to https://yourstore.com/products.json returns a JSON body with a products array containing handle, title, body_html, vendor, product_type, variants, options, images, and inventory state. Status 200, valid JSON, non-empty products array. Anything else — 404, HTML login page, raw HTML, redirect loop, empty array — scores zero on this 25-point signal.

What it is

The Shopify /products.json endpoint is part of Shopify's storefront API surface. It's been stable since the early 2010s, requires no authentication on a public storefront, and returns up to 250 products per call with standard pagination via ?page=N&limit=N. The shape:

{
  "products": [
    {
      "id": 4582134823219,
      "title": "Wool Runner",
      "handle": "wool-runner",
      "body_html": "<p>Merino-wool runner with cushioned EVA midsole...</p>",
      "vendor": "Allbirds",
      "product_type": "Footwear",
      "tags": ["sustainable", "running", "merino"],
      "variants": [
        { "id": 31..., "title": "M 9", "sku": "AB-WR-M-09",
          "barcode": "0012345678905", "price": "98.00",
          "available": true, "option1": "9", "option2": "Natural Black" }
      ],
      "options": [...],
      "images": [
        { "src": ".../runner.jpg", "alt": "Wool Runner — Natural Black" }
      ]
    },
    ... up to 249 more
  ]
}

It is not the same as the Shopify Admin API or the Storefront GraphQL API. /products.json is the legacy "open feed" — no auth, no token, public-internet readable.

Why AI shopping agents depend on it

Three reasons this surface gets weighted at 25 points (second only to Product JSON-LD):

How to test it on your store

One curl command:

curl -s https://yourstore.com/products.json?limit=1

Expected response: a JSON body with a products array containing exactly one product (or zero if your store is empty). What a failed feed looks like instead:

To verify pagination works (some headless proxies hardcode the first page only), try ?page=2 and confirm you get the next 250.

How to fix it

Default Shopify storefront5 minfree

If you're on a vanilla Shopify theme, the feed is on by default. Two possible blockers: (a) storefront password is set — Admin → Online Store → Preferences, scroll to "Password page," uncheck "Enable password." (b) products aren't published to Online Store — open one product in Admin, scroll to "Sales channels," confirm "Online Store" is in the list. Both 5-minute fixes.

Hydrogen (Shopify-owned headless)30 minfree

Add a route at app/routes/products[.]json.tsx (or .ts) that proxies the upstream Shopify Storefront API and returns the same shape as the legacy /products.json. Hydrogen ships with the auth token; you read all products via the products connection and serialize to the legacy schema. Keep the response shape exact — agents have learned the field names.

Next.js + Storefront API45 minfree

Add app/products.json/route.ts (App Router) or pages/api/products.ts + a custom rewrite in next.config.js to expose at the canonical /products.json path. Use the Storefront API products query, paginate via cursors, return up to 250 per call. Cache for 5 minutes at the edge to keep it cheap.

Custom stack (Astro, Remix, plain SPA)1 hrfree

Same pattern: a server-side route at /products.json that proxies your Shopify Admin or Storefront API. Don't try to mirror the catalog into a static JSON file at build time — it will go stale, and the inventory available flag is the field agents check most often.

4 mistakes we keep finding

1. /products.json blocked in robots.txt

Some teams add Disallow: /products.json trying to "stop scrapers." This blocks legitimate AI shopping agents — the ones you want to surface in — at the same time. Don't. If you have rate-limit concerns, handle them at the CDN layer, not robots.txt.

2. limit hardcoded to 50 on a custom proxy

Shopify's default page size is 250. Custom proxies that hardcode 50 mean a 5,000-product catalog needs 100 paginated calls instead of 20. Agents notice; some give up at 50 pages of pagination.

3. JSON wrapped in JSONP or surrounded by HTML

An exotic CDN config sometimes wraps the response in a JSONP callback or prepends a comment block. Both break standard JSON parsing. The response body must start with { and parse cleanly without preamble.

4. CORS configured wrong on headless proxy

If your proxy returns the right body but rejects the request with Access-Control-Allow-Origin denial, browser-side AI assistants and the CatalogScan embed widget can't read the feed. Add Access-Control-Allow-Origin: * to the response — public catalog data is meant to be public.

See also

Is your /products.json feed live?

Free 2-minute scan. Paste your store URL — we test the feed plus 14 other AI-readiness signals in one shot.

Scan my store → See all 15 signals