Free tool · no email required

Product schema markup generator

Fill in the product, or paste the page source and let the tool read what is already there. It returns Product JSON-LD, the Liquid snippet that renders it per product, and the list of properties you are missing — separated by who actually needs each one.

What product schema markup is

Product schema markup is a block of JSON-LD in a page’s HTML that states, in a fixed vocabulary, what the product is: name, price, currency, availability, brand, identifiers, shipping and returns. Google reads it to build a product rich result, Merchant Center reads it to list the item, and language models read it when they answer a buying question. Without it, all three are guessing from marketing copy.

Generate the markup.

Prefilled with a worked example — an illustrative product, not a real store. Replace it with your own; everything stays in this browser tab.

Optional. Reads any Product JSON-LD, microdata and Open Graph tags already on the page and fills the fields below from them. A URL will not work here: the browser blocks reading another site’s HTML, and this tool runs entirely in your browser.

The product

The text a model paraphrases. Facts travel; adjectives do not.

The offer

In Shopify the GTIN is the variant’s barcode field. It is empty by default, and Merchant Center expects it wherever the manufacturer assigned one.

Shipping and returns

Shipping and returns are the two a model wants most and a Shopify store almost never publishes in machine-readable form.

Specs and reviews

Size, material, servings, compatibility. This is the block that decides whether a model can match your product to a specific requirement.

Left empty in the example on purpose. Only fill these in if the reviews are real and displayed on the page — marking up ratings you do not show is a manual-action risk, so the tool omits the block unless both fields are set.

Product JSON-LD

{
  "@context": "https://schema.org",
  "@type": "Product",
  "name": "Daily Greens Powder — 30 servings",
  "description": "A 30-serving greens powder with 12 g of fibre per scoop, unflavoured, mixes cold without a shaker.",
  "image": "https://example-store.myshopify.com/cdn/shop/files/daily-greens.jpg",
  "sku": "DG-30",
  "brand": {
    "@type": "Brand",
    "name": "Example Brand"
  },
  "additionalProperty": [
    {
      "@type": "PropertyValue",
      "name": "Servings",
      "value": "30"
    },
    {
      "@type": "PropertyValue",
      "name": "Net weight",
      "value": "240 g"
    },
    {
      "@type": "PropertyValue",
      "name": "Form",
      "value": "Powder"
    },
    {
      "@type": "PropertyValue",
      "name": "Fibre per serving",
      "value": "12 g"
    }
  ],
  "offers": {
    "@type": "Offer",
    "price": "42.00",
    "priceCurrency": "USD",
    "availability": "https://schema.org/InStock",
    "itemCondition": "https://schema.org/NewCondition",
    "url": "https://example-store.myshopify.com/products/daily-greens",
    "shippingDetails": {
      "@type": "OfferShippingDetails",
      "shippingRate": {
        "@type": "MonetaryAmount",
        "value": "0",
        "currency": "USD"
      },
      "shippingDestination": {
        "@type": "DefinedRegion",
        "addressCountry": "US"
      },
      "deliveryTime": {
        "@type": "ShippingDeliveryTime",
        "transitTime": {
          "@type": "QuantitativeValue",
          "minValue": 2,
          "maxValue": 5,
          "unitCode": "DAY"
        }
      }
    },
    "hasMerchantReturnPolicy": {
      "@type": "MerchantReturnPolicy",
      "applicableCountry": "US",
      "returnPolicyCategory": "https://schema.org/MerchantReturnFiniteReturnWindow",
      "merchantReturnDays": 30,
      "returnMethod": "https://schema.org/ReturnByMail",
      "returnFees": "https://schema.org/FreeReturn"
    }
  }
}

Shopify snippet

{%- comment -%}
  Product JSON-LD. Put this in snippets/product-schema.liquid and render it
  from templates/product.liquid with {% render 'product-schema' %}.

  Shopify's own themes emit a minimal Product block already. Check for one
  before adding this — two Product blocks on one URL is worse than neither,
  because the crawler has to guess which is authoritative.
{%- endcomment -%}
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "Product",
  "name": {{ product.title | json }},
  "description": {{ product.description | strip_html | truncate: 500 | json }},
  "image": {{ product.featured_image | image_url: width: 1200 | prepend: "https:" | json }},
  "sku": {{ product.selected_or_first_available_variant.sku | json }},
  "brand": { "@type": "Brand", "name": {{ product.vendor | json }} },
  "additionalProperty": [
    {%- for field in product.metafields.specs -%}
    { "@type": "PropertyValue", "name": {{ field.first | json }}, "value": {{ field.last | json }} }{%- unless forloop.last -%},{%- endunless -%}
    {%- endfor -%}
  ],
  "offers": {
    "@type": "Offer",
    "url": {{ shop.url | append: product.url | json }},
    "price": {{ product.selected_or_first_available_variant.price | divided_by: 100.0 | json }},
    "priceCurrency": {{ cart.currency.iso_code | json }},
    "itemCondition": "https://schema.org/NewCondition",
    "availability": "https://schema.org/{% if product.selected_or_first_available_variant.available %}InStock{% else %}OutOfStock{% endif %}"
    ,
    "hasMerchantReturnPolicy": {
      "@type": "MerchantReturnPolicy",
      "applicableCountry": "US",
      "returnPolicyCategory": "https://schema.org/MerchantReturnFiniteReturnWindow",
      "merchantReturnDays": 30,
      "returnMethod": "https://schema.org/ReturnByMail",
      "returnFees": "https://schema.org/FreeReturn"
    }
  }
}
</script>

The snippet renders from the product object, so one file is correct for every product. A JSON-LD block hard-coded into the template is correct for one.

What is missing

11 of 14 properties present · 3 missing

  • gtin GTIN / barcode missing
  • aggregateRating Rating and review count missing
  • mpn MPN missing
  • name Product name present
  • image Image URL present
  • description Description present
  • offers.price Price present
  • offers.priceCurrency Currency present
  • offers.availability Availability present
  • brand.name Brand present
  • sku SKU present
  • offers.shippingDetails Shipping cost and speed present
  • offers.hasMerchantReturnPolicy Return window present
  • additionalProperty Itemised specs present

How the generator reads a page

Four passes, in descending order of trust. Anything read from the visible page rather than from declared markup is labelled a guess, because a tool that silently guesses is how a wrong price ends up in a JSON-LD block.

  1. 1

    Existing JSON-LD

    Every application/ld+json block is parsed, including nodes inside a @graph. If more than one Product block exists, the tool says so — that is a defect worth fixing before anything else.

  2. 2

    Microdata

    itemprop attributes, which older themes still emit. Read, never generated.

  3. 3

    Open Graph and product meta tags

    og:title, og:image, product:price:amount and their siblings. Usually present, occasionally stale — they fill gaps rather than override declarations.

  4. 4

    The visible page

    The h1, and two-column tables or definition lists that look like a spec block. Everything from this pass is flagged, because a spec table and a shipping table are identical to a parser.

What it cannot see: anything rendered after page load. If your reviews, specs or price are injected by an app’s JavaScript, they are not in the source you pasted and they may not be in what a crawler stores either. That is a finding, not a limitation of the tool.

Which properties who needs

Most schema guides give you one list. There are three, they disagree, and the third one is the reason this tool exists.

Product properties by consumer. Google and Merchant Center columns follow their published documentation; the model column is our own reading.
Property Rich result Merchant Center Models Where it lives in Shopify
Product name name Required Required Required product.title
Image URL image Recommended Required Recommended product.featured_image | image_url
Description description Recommended Required Required product.description | strip_html
Price offers.price Required Required Required product.selected_or_first_available_variant.price
Currency offers.priceCurrency Required Required Required cart.currency.iso_code
Availability offers.availability Recommended Required Required variant.available
GTIN / barcode gtin Recommended Required Recommended variant.barcode
Brand brand.name Recommended Required Required product.vendor
SKU sku Recommended Required variant.sku
MPN mpn Recommended Recommended variant.sku or a metafield
Rating and review count aggregateRating Recommended Recommended Required your review app's metafields
Shipping cost and speed offers.shippingDetails Recommended Recommended Required shipping profile, hand-entered
Return window offers.hasMerchantReturnPolicy Recommended Recommended Required policy page, hand-entered
Itemised specs additionalProperty Recommended Required metafields

Sources. The rich-result column follows Google Search Central’s Product structured data documentation; the Merchant Center column follows the Merchant Center product data specification. Both change — this table was checked by hand on 2026-09-09. The model column is a judgement, not a published requirement. No model vendor publishes a required-field list. It is our reading of which fields decide whether a product can be matched to a buying question, and whether it correlates with citation frequency is metric to confirm — we will publish that when we have enough before-and-after data of our own to mean it.

The three nobody fills in

  • Shipping “Free over $50, arrives Tuesday” answers half of a buying question. In Shopify it lives in a shipping profile that no crawler can read, which is why it has to be restated in the markup.
  • Returns The second-most common qualifier in a considered purchase, and it sits in a policy page nothing associates with the product.
  • Itemised specs Servings, material, dimensions, compatibility. Without them a model can describe your product but cannot match it to a requirement — and matching to a requirement is what a category question is.

What this tool does not do

It does not validate against Google. It generates markup that follows the documented shape and tells you what is absent. Whether Google accepts the rendered page is a question only Google can answer — run the live URL through the Rich Results Test and the Schema Markup Validator once the snippet is installed. Validate the rendered page, never the snippet on its own.

It does not fetch your store. The whole tool runs in your browser, and a browser cannot read another origin’s HTML — the request is blocked before it is sent. Pasting the source is the honest version of the same operation. When a fetch service sits behind this page the URL field will start working, and nothing else about the page will change.

It does not invent review data. The aggregateRating block is omitted unless you supply both a rating and a count, and you should only supply them if both are genuinely displayed on the page. Marking up ratings you do not show is against Google’s guidelines and a common cause of a manual action.

It does not promise placement. Structured data makes a product legible to a crawler and a model. Legibility is a precondition for accurate citation, not a purchase of it. Any tool claiming schema markup will get you recommended by ChatGPT is describing a mechanism that does not exist.

Definitions

JSON-LD
Structured data expressed as a JSON block in the page rather than as attributes threaded through the markup. Google’s recommended format, and the one this tool emits.
GTIN
Global Trade Item Number — the barcode number a manufacturer assigns to a product. In Shopify it is the variant’s barcode field. Merchant Center expects it wherever one exists.
Merchant listing
The Google Search result type for a product you can buy, as distinct from a product page in ordinary results. Shipping and return properties are what distinguish the two.
Rich result
A search result rendered with extra detail — price, availability, rating — drawn from structured data rather than from the page copy.
Answer engine optimisation
Getting a brand cited inside an AI-generated answer rather than ranked in a list of links. Product data legibility is the part of it a store controls directly.

Questions about product schema

Doesn’t Shopify add product schema automatically?

Most themes emit a minimal Product block — usually name, image, price and availability, and nothing else. Dawn does. What they leave out is everything in the right-hand columns of the table on this page: GTIN, shipping, returns and itemised specs. Before you add anything, view the page source and search for application/ld+json, because two Product blocks on one URL is worse than one thin block: the crawler has to decide which is authoritative, and it may not pick yours.

JSON-LD or microdata?

JSON-LD. Google recommends it, it sits in one block rather than being threaded through your markup, and it survives a theme change. Microdata still works and older themes still emit it — this tool reads it if it finds it, but it will not generate it.

Why does Merchant Center keep telling me the GTIN is missing?

Because it is conditionally required: where the manufacturer has assigned a barcode, Merchant Center expects it, and for most consumer goods one exists. In Shopify it lives on the variant as the barcode field, which is empty by default and stays empty unless somebody fills it in. If your product genuinely has no GTIN — you manufacture it yourself and never registered one — the brand and MPN pair is the documented substitute.

Will adding schema make ChatGPT recommend my product?

No, and anyone telling you otherwise is selling something. Structured data makes your product legible: it tells a model what the thing is, what it costs, whether it is in stock and what it is made of. Legibility is a precondition for being cited accurately, not a placement you can buy. Whether it changes how often you are named is something we can only answer with our own before-and-after data, and we do not have enough of it yet to publish a figure.

Can I mark up reviews the page doesn’t display?

No. Marking up ratings that are not visible on the page is against Google’s structured-data guidelines and is a common cause of a manual action. This tool omits aggregateRating unless you give it both a rating value and a review count, and you should only do that if both are genuinely on the page.

Why does it ask me to paste page source instead of a URL?

Because the tool runs entirely in your browser, and a browser is not allowed to read another site’s HTML — the request is blocked before it is sent. Pasting the source is the honest version of the same thing. When we put a fetch service behind this, the URL field starts working and nothing else about the page changes.

Does anything I type get sent to you?

No. Parsing, generating and the audit all run in this tab. There is no endpoint behind this page, nothing is logged, and there is no email field standing between you and the output.

Find out what you’re losing.

Before you commit to anything, we tell you exactly what you’re losing and what it costs to stop it. Two weeks. Fixed fee. Credited in full against any build you go ahead with.

Fee
$1,500–$3,000, fixed
Duration
Two weeks
Credited
In full, against any build
You supply
Read access + one 45-minute call