gaash.ai

Technical & Structure · 8 min read · September 24, 2026 · , gaash.ai

Schema.org for online shops: Product, Offer, shipping and returns markup

An online shop's markup restates four things its pages already show: the product, the offer with its price, currency and availability, the shipping to each destination, and the return policy. schema.org calls them Product, Offer, OfferShippingDetails and MerchantReturnPolicy, and Google documents them for merchant listings. Generate them from the same record as the page, in JSON-LD, and state nothing the page does not show.

What markup does on a shop, and what Google documents

Structured data labels facts that are already on the page, so a parser does not have to guess which number is the price and which is the shipping cost. Google's product documentation separates two uses. Product snippets are for pages where the product cannot be bought, such as a review. Merchant listings are for pages where a buyer can purchase from you, and they carry the detail a shop needs: sizing, shipping details and return policy information. A shop's product pages are merchant listing pages.

Three limits apply. Google's introduction to structured data recommends JSON-LD, says to mark up only what a visitor can see, and its general guidelines say Google does not guarantee that structured data will show up in results. Its page on AI features says AI Overviews and AI Mode need no special markup. And the assistants' crawler documentation says nothing about how schema.org markup is used. Markup is a second, exact statement of your facts, useful to any parser that reads it, and not a lever.

The types a shop uses, and what goes in each

The schema.org types an online shop uses, where each goes, the properties that carry the facts, and a note on each.
TypeWhere it goesProperties to fillNote
OnlineStore, a kind of OrganizationOnce, on the page that states your returns policy, or the homepagename, url, logo, sameAs, contactPoint, hasMerchantReturnPolicyThe shop itself; it carries no rating
ProductEach product pagename, description, image, sku, gtin or mpn where one exists, brand, material, size, color, offersOne product per page, or a group of variants
OfferInside the Product, as offersprice, priceCurrency, availability, itemCondition, url, shippingDetailsThe price and stock the page shows, in the currency it shows
OfferShippingDetailsInside the Offer, as shippingDetailsshippingDestination, shippingRate, deliveryTimeOne block per destination and rate
MerchantReturnPolicyOn the shop node, as hasMerchantReturnPolicyapplicableCountry, returnPolicyCategory, merchantReturnDays, returnMethod, returnFeesInside an Offer only where one product's terms differ
ProductGroupA page that sells several sizes or colorsproductGroupID, variesBy, hasVariantEach variant is a Product with its own offer
AggregateRating and ReviewInside the Product onlyratingValue, reviewCount; author, reviewRatingOnly for reviews customers left for that product on your own site

Each type is defined on schema.org, and we checked every one there before writing this. The Product page lists the product properties above, and the Offer page defines price, priceCurrency and availability. OnlineStore is schema.org's type for an e-commerce site, a subtype of Organization by way of OnlineBusiness, and hasMerchantReturnPolicy is defined on Organization as well as on Product and Offer. That is why one policy can be stated once for the whole shop.

A product page template, with brackets

One Product block per product page, in the HTML the server sends. Replace every bracket with the value the page shows, and delete any property you cannot fill truthfully rather than leaving a placeholder. Numbers go in without currency symbols or thousands separators.

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "Product",
  "@id": "https://[your domain]/products/[product-slug]#product",
  "name": "[product name, as in the page's heading]",
  "description": "[the page's first paragraph: what it is and the need it meets]",
  "image": ["https://[your domain]/images/[product-photo].jpg"],
  "sku": "[your SKU]",
  "gtin13": "[the barcode number, only if the product has one]",
  "brand": { "@type": "Brand", "name": "[brand name]" },
  "material": "[material, as the page states it]",
  "color": "[color]",
  "size": "[size]",
  "offers": {
    "@type": "Offer",
    "url": "https://[your domain]/products/[product-slug]",
    "price": "[the price the page shows, digits only]",
    "priceCurrency": "[the currency the page shows: USD, CAD, EUR or AED]",
    "availability": "https://schema.org/InStock",
    "itemCondition": "https://schema.org/NewCondition",
    "shippingDetails": [
      {
        "@type": "OfferShippingDetails",
        "shippingDestination": {
          "@type": "DefinedRegion",
          "addressCountry": "[two-letter country code, such as CA]"
        },
        "shippingRate": {
          "@type": "MonetaryAmount",
          "value": "[shipping cost to that country, 0 if free]",
          "currency": "[the same currency as the price]"
        },
        "deliveryTime": {
          "@type": "ShippingDeliveryTime",
          "handlingTime": { "@type": "QuantitativeValue", "minValue": "[days]", "maxValue": "[days]", "unitCode": "DAY" },
          "transitTime": { "@type": "QuantitativeValue", "minValue": "[days]", "maxValue": "[days]", "unitCode": "DAY" }
        }
      }
    ],
    "hasMerchantReturnPolicy": { "@id": "https://[your domain]/#returns" }
  }
}
</script>
  • availability takes one value from schema.org's list, such as InStock, OutOfStock, PreOrder, BackOrder or MadeToOrder: the one the page states for this variant today.
  • shippingDetails holds one block per destination and rate. A shop that ships to four markets at four prices writes four blocks, each matching a row of its shipping page.
  • The Offer points at the shop's return policy by its @id instead of repeating it. Add a MerchantReturnPolicy inside the Offer only for a product whose terms differ, such as a final-sale item.
  • Leave out gtin13 when the product has no barcode, and brand when you do not know it. An empty property states nothing; a wrong one states something false.

The return policy, stated once for the whole shop

A return policy that covers everything you sell belongs on the shop's own node, on the page that states the policy in words, with an @id the product offers can point at.

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "OnlineStore",
  "@id": "https://[your domain]/#shop",
  "name": "[shop name, exactly as in the footer]",
  "url": "https://[your domain]/",
  "logo": "https://[your domain]/[logo].png",
  "sameAs": ["[your marketplace storefront URL]", "[your social profile URL]"],
  "contactPoint": {
    "@type": "ContactPoint",
    "contactType": "customer service",
    "email": "[contact email]"
  },
  "hasMerchantReturnPolicy": {
    "@type": "MerchantReturnPolicy",
    "@id": "https://[your domain]/#returns",
    "applicableCountry": ["[country codes the policy covers, such as US and CA]"],
    "returnPolicyCategory": "https://schema.org/MerchantReturnFiniteReturnWindow",
    "merchantReturnDays": "[days, as the returns page states]",
    "returnMethod": "https://schema.org/ReturnByMail",
    "returnFees": "https://schema.org/FreeReturn",
    "merchantReturnLink": "https://[your domain]/returns"
  }
}
</script>

returnPolicyCategory names the kind of policy: a finite window, in which case merchantReturnDays says how many days; no returns; or no time limit. schema.org's fourth value, unspecified, tells a reader nothing. returnFees says who pays: FreeReturn, ReturnFeesCustomerResponsibility when the buyer pays the postage, or ReturnShippingFees when you charge a set fee, which then needs its amount. If your terms differ by market, write one MerchantReturnPolicy per group of countries.

The markup restates your policy; it does not set it. What a return policy has to offer depends on where you sell and where the buyer lives, and that differs between the US, Canada, the EU and the UAE. Settle the policy with your counsel, then write it once, in the words of the returns page, and copy those words into the markup.

Ratings and reviews: what Google's rules allow in Product markup

Google's review snippet rules decide what may go in. Ratings must come directly from users. Reviews or ratings from other websites must not be aggregated. Fake reviews, and incentivized reviews that do not disclose the incentive, are excluded. And when a business controls the reviews about itself, its pages with LocalBusiness or Organization markup are not eligible for review stars, which Google calls self-serving.

For a shop that settles three cases. Ratings customers gave a product on your own site, shown on that product's page, may go in the Product node as an aggregateRating or as Review nodes. A marketplace's rating of the same product may not, however it reached your page. And the OnlineStore node carries no rating at all; reviews of the shop itself go on the page as quoted text, with a first name, the date and the platform named.

If the first case applies, the rating sits inside the Product, beside offers, and states what the page shows:

"aggregateRating": {
  "@type": "AggregateRating",
  "ratingValue": "[the average shown on this product page]",
  "reviewCount": "[the number of reviews shown on this product page]"
}

The reviews behind that number have to be readable on the same page, and review markup for each one names its author and its rating. Leave the block out entirely until you collect reviews yourself; a star widget that loads from a review service is not the same as reviews your page states.

Keeping the markup equal to the page

Markup that disagrees with its page is worse than none, because it states a second price with confidence. Google's structured data guidelines require markup to describe the visible page and not mislead. In our audit of 9,483 small business websites, 13.7% had business markup missing the fields that would make it usable; a Product block with a placeholder price or an empty shipping rate is the shop's version of that. Four habits keep a shop's markup true.

  • Generate it from the product record that the page template reads, so a price change reaches both at once.
  • Put it in the HTML the server sends. Google renders JavaScript as a separate, deferred step, and the assistants' crawler documentation promises no rendering at all, so markup that a script adds after load may be missing when a page is fetched.
  • Change availability in the page and in the markup together, per variant, on the day the stock changes.
  • Keep one currency per page and per block: if a buyer in Canada is shown a price in CAD, the Offer on that page says CAD.

Checking the markup before and after a change

  1. View the source of one product page and find the application/ld+json block. If it is not in the source, a script adds it later.
  2. Compare its price, currency, availability and shipping cost with the page's text, for the default variant and one other.
  3. Run the page through a structured data validator and read the warnings for merchant listings. Delete a property you cannot fill truthfully rather than faking it.
  4. Check that every Offer's return policy @id points at the policy on the shop node.
  5. Search the markup for aggregateRating, and confirm every rating came from reviews customers left on your site for that product.
  6. Repeat on the next product page whenever the template changes, and after every platform or theme update.

The general rules for placing and testing markup are in setting up schema.org correctly, and which types describe a business are in structured data for AI. For what the product page itself has to say before any of this is written, see product data for AI; for where markup fits in the rest of a shop's work, the overview for online shops.

Common questions

Our shop platform already outputs Product markup. Do we need to do anything?

Check what it outputs before trusting it. View the source of a product page: platform markup may carry the name, price and availability and leave out shipping and returns, or show the default variant on every variant's page. Compare every value with the page, add what is missing through the platform's settings or theme, and remove anything that is not true.

Can we put our marketplace rating in the Product markup?

No. Google's review snippet rules say not to aggregate reviews or ratings from other websites, and a marketplace's score for your product is exactly that. The Product node may carry ratings customers gave the product on your own site, shown on the page. A marketplace review can still be quoted as text, with a first name, the date and the platform, where the platform's terms allow it.

Should shipping and returns be marked up on every product page?

Shipping, yes: each Offer carries its own shippingDetails, because the destinations, costs and times are part of what that offer means, and the blocks can be generated from one shipping table. The return policy goes once on the shop node, and each Offer points at it by @id. Only a product whose terms differ gets a policy of its own.

Will Product markup make an assistant recommend our products?

No assistant operator claims that it does, and we have measured no effect of markup on its own. Google says its AI features need no special markup, and that even correct structured data is not guaranteed to show up in results. What markup does is state price, stock, shipping and returns exactly, for any parser that reads the page. It is worth most when the page's text says the same thing, which is why the text comes first.

Sources

The percentage is this site's own, from the audit named below, and describes all 40 trades in the sample. Every type and property named here was checked on schema.org on the day of writing; what Google reads and requires is from its documentation, linked where it is used.

Sources: our audit of 9,483 small-business sites, with its method · Google, product structured data · Google, review snippet rules · Google, introduction to structured data · Google, structured data general guidelines · Google, AI features and your website · Google, JavaScript SEO basics · schema.org, Product · schema.org, Offer · schema.org, Organization

All articles