# Product JSON-LD for AI shopping agents

> Product JSON-LD is a schema.org Product block with an Offer (price, currency, availability) in a script tag of the product page's server HTML. AI shopping agents and Google Shopping read it as the machine-readable version of the page; it must include Google's required merchant-listing fields and match the price shoppers see.

- Source: https://is-agentic-storefront.com/checks/product-json-ld
- Updated: 2026-10-15
- Category: Product data (25% of score)
- Impact: High
- Spec: [Google merchant listing](https://developers.google.com/search/docs/appearance/structured-data/merchant-listing)
- Scanner check: Product structured data meets Google merchant listing requirements

## Why AI agents need Product JSON-LD

Visible HTML tells an agent _roughly_ what a page is about. JSON-LD tells it exactly: this is a Product, this is its GTIN, the price is 89.00, the currency is USD, it is in stock. There is no guessing which number on the page is the sale price and which is the "compare at" price.

The evidence that it's used:

- OpenAI says [shopping in ChatGPT search](https://help.openai.com/en/articles/11128490-shopping-with-chatgpt-search) uses "structured metadata from first-party and third-party providers".
- Google Merchant Center can [auto-update product data](https://support.google.com/merchants/answer/7052112) from on-page structured data, and Google's [merchant listing](https://developers.google.com/search/docs/appearance/structured-data/merchant-listing) guide warns that JavaScript-generated markup makes Shopping crawls less frequent and less reliable.
- Agents that don't run JavaScript (see [server-rendered product facts](https://is-agentic-storefront.com/checks/ssr-product-facts)) can only read JSON-LD that's in the initial HTML.

Google also notes structured data isn't strictly required for its AI features ([AI optimization guide](https://developers.google.com/search/docs/fundamentals/ai-optimization-guide)), but for commerce it's the cleanest way to hand an agent an unambiguous price and stock status.

## What the scanner checks

The scanner parses every `<script type="application/ld+json">` block (including `@graph` arrays) on up to three sampled product pages. It uses a `ProductGroup` if present, otherwise the first named `Product`, and reads `Offer` or `AggregateOffer` nodes.

| Field                  | Level       | Accepted as                                                                           |
| ---------------------- | ----------- | ------------------------------------------------------------------------------------- |
| Product JSON-LD        | Required    | `Product` or `ProductGroup` node                                                      |
| `name`, `image`        | Required    | Non-empty                                                                             |
| `offers`               | Required    | At least one `Offer` / `AggregateOffer`                                               |
| `offers.price`         | Required    | `price`, `lowPrice` or `priceSpecification.price`, greater than 0                     |
| `offers.priceCurrency` | Required    | On the offer or its `priceSpecification`                                              |
| `offers.availability`  | Recommended | Any value                                                                             |
| Identifier             | Recommended | `gtin`, `gtin8`, `gtin12`, `gtin13`, `gtin14`, `mpn` or `sku` on the product or offer |
| `brand`, `description` | Recommended | Non-empty                                                                             |

It also runs a **price match**: when a page has a single offer, the JSON-LD price must appear in the visible text (formats like `1,299.50`, `1.299,50` and `1299.50` are accepted).

| Result       | Condition                                                                         |
| ------------ | --------------------------------------------------------------------------------- |
| Pass         | Every sampled page has all required and recommended fields and no price mismatch. |
| Partial      | Some fields are missing or a minority of pages have a mismatch.                   |
| Fail         | No sampled page has Product JSON-LD, or more than half have a price mismatch.     |
| Inconclusive | No product page could be fetched.                                                 |

## How to add complete Product JSON-LD

Render this in the server HTML of every product page, generated from the same data that renders the visible price:

```html
<script type="application/ld+json">
  {
    "@context": "https://schema.org",
    "@type": "Product",
    "name": "Merino Crew Sweater",
    "description": "Midweight merino wool crew neck sweater.",
    "image": ["https://your-store.com/images/merino-crew.jpg"],
    "sku": "MCS-NAVY-M",
    "gtin13": "0123456789012",
    "brand": { "@type": "Brand", "name": "Your Brand" },
    "offers": {
      "@type": "Offer",
      "url": "https://your-store.com/products/merino-crew",
      "price": "89.00",
      "priceCurrency": "USD",
      "availability": "https://schema.org/InStock",
      "itemCondition": "https://schema.org/NewCondition"
    }
  }
</script>
```

Rules that matter:

1. **Price uses a dot decimal and no currency symbol** (`"89.00"`), and `priceCurrency` is an ISO 4217 code.
2. **Availability uses the schema.org enum** (`https://schema.org/InStock`, `OutOfStock`, `PreOrder`, `BackOrder`).
3. **Never hardcode or cache the price separately** from the visible price. A mismatched price is worse than no markup.
4. **Products with options** should use `ProductGroup` with one `Product` per variant; see [product variants](https://is-agentic-storefront.com/checks/product-variants).
5. **Add return and shipping policy** to complete the picture; see [return and shipping data](https://is-agentic-storefront.com/checks/return-shipping-data).

Verify with Google's [Rich Results Test](https://search.google.com/test/rich-results). A product URL should show a valid merchant listing with no errors.

## How to fix it on Medusa

- **Not shipped by default.** Medusa is headless, so Product JSON-LD is the storefront's job, and the [DTC starter](https://github.com/medusajs/dtc-starter) storefront doesn't output it.
- **Where to add it**: the product page server component at `apps/storefront/src/app/[countryCode]/(main)/products/[handle]/page.tsx`. It already fetches the product with calculated variant prices, so build `offers.price` and `priceCurrency` from `variant.calculated_price` and render a `<script type="application/ld+json">` tag as the [Next.js JSON-LD guide](https://nextjs.org/docs/app/guides/json-ld) recommends.
- **Code example**: [Is Medusa agent-ready?](https://is-agentic-storefront.com/guides/medusa-agent-readiness) has a full snippet, including availability from inventory fields.

## Common mistakes

- Injecting JSON-LD with a tag manager or client-side script, so it's absent from the server HTML.
- `"price": "$89.00"` or `"price": "89,00"` instead of `"89.00"`.
- Marking up the "compare at" price instead of the selling price.
- A price of `0` on products that are "price on request".

## Sources

- [Google: merchant listing structured data](https://developers.google.com/search/docs/appearance/structured-data/merchant-listing)
- [Google: product snippet structured data](https://developers.google.com/search/docs/appearance/structured-data/product-snippet)
- [Google Merchant Center product data specification](https://support.google.com/merchants/answer/7052112)
- [OpenAI: shopping with ChatGPT search](https://help.openai.com/en/articles/11128490-shopping-with-chatgpt-search)
- [schema.org Product](https://schema.org/Product) and [Offer](https://schema.org/Offer)
- [Next.js: JSON-LD guide](https://nextjs.org/docs/app/guides/json-ld)

## Frequently asked questions

### Do AI agents use structured data?

OpenAI says ChatGPT shopping results use structured metadata from first- and third-party sources, and Google Merchant Center can update products from on-page structured data. Agents that read raw HTML can only use JSON-LD that is in the server response.

### Which Product JSON-LD fields are required?

For Google merchant listings, name, image and offers with a price above zero and a priceCurrency are required. Availability, a GTIN/MPN/SKU, brand and description are recommended, and this check expects them too.

### Is microdata enough instead of JSON-LD?

Google accepts microdata and RDFa, but this scanner reads only JSON-LD, which is the format Google recommends. Add a JSON-LD block to pass.

### What happens if the JSON-LD price doesn't match the page?

Google requires markup to match visible content, and an agent quoting a stale price is worse than none. The scanner flags pages where a single-offer JSON-LD price isn't shown in the page text.

## Run your store on a platform built for agentic commerce

Medusa is open-source, headless commerce with a full Store API agents can call and storefronts you control end to end.

### Deploy on Medusa Cloud

Open-source commerce with a full Store API, hosted and scaled for you. Your storefront, your data model.

- [Deploy on Medusa Cloud](https://cloud.medusajs.com?utm_source=agent-friendly-scorer&utm_medium=referral&utm_campaign=markdown-cta&utm_content=%2Fchecks%2Fproduct-json-ld)
- [Medusa Cloud docs](https://docs.medusajs.com/cloud?utm_source=agent-friendly-scorer&utm_medium=referral&utm_campaign=markdown-cta&utm_content=%2Fchecks%2Fproduct-json-ld)

### Storefront skills for coding agents

Teach Claude Code, Cursor or Codex to build storefronts with the patterns these checks look for.

```bash
npx skills add medusajs/medusa-agent-skills
```

[View the skill on GitHub](https://github.com/medusajs/medusa-agent-skills/tree/main/plugins/ecommerce-storefront/skills/storefront-best-practices)

---

Scan your store for free at https://is-agentic-storefront.com/