If you offer standard, express or conditional free shipping, put the same rules in your product markup (or the Merchant Center product shipping attributes) so search engines can parse what you actually publish. The goal is consistency: the customer‑facing text is the canonical rule; structured data should reflect it, not replace or contradict it.
Contents
Capture the customer‑facing shipping facts to publish
Start by writing, verbatim, what a shopper sees on the product page about shipping. Include:
- Costs and thresholds (for example “free over £30”, or “£4.95 flat”);
- Destinations and exclusions (which countries or regions are covered);
- Estimated handling + delivery ranges (e.g. “1–2 business days handling, 3–5 business days delivery”);
- Any conditional rules (for example free shipping only for standard service, or only for domestic orders).
Keep this text on the page or link to a clearly visible shipping policy. Structured data must reflect what a real customer sees; don’t rely on markup as the only place the rule appears.
Map those facts to structured‑data fields search engines recognise
Search engines recognise Offer.shippingDetails (see schema.org) and specific fields that Google’s merchant listing parsing expects. Use one shippingDetails entry per distinct rule or destination. Helpful mappings:
Flat rate or explicit price
Use shippingRate — include numeric value and currency (value/maxValue + currency as supported). Google’s parsing expects a currency and a value or maxValue for the shippingRate.
Free over a threshold (conditional free shipping)
Model the condition as a shippingDetails entry where possible. For a common pattern like “free over £30” you can encode the threshold using DeliveryChargeSpecification and eligibleTransactionVolume; for example:
{
"@type": "OfferShippingDetails",
"shippingDestination": {"@type":"DefinedRegion","addressCountry":"GB"},
"shippingRate": {
"@type": "DeliveryChargeSpecification",
"eligibleTransactionVolume": {"@type":"PriceSpecification","minValue": 30, "priceCurrency": "GBP"},
"price": 0,
"priceCurrency": "GBP"
}
}
Note: if your platform cannot emit DeliveryChargeSpecification or eligibleTransactionVolume, keep the threshold text visible on the page and publish separate shippingDetails entries for each distinct condition or destination so machines still see distinct rules.
Country or region
Use shippingDestination.addressCountry — publish one entry per destination where the rule differs.
Estimated delivery time
Use deliveryTime (a ShippingDeliveryTime) and include handlingTime and transitTime ranges when appropriate.
For reference: schema.org — OfferShippingDetails and Google merchant listing guidance.
Reference schema and Google’s merchant listing guidance while you map fields: see schema.org’s OfferShippingDetails and Google’s merchant listing requirements for shipping fields in structured data (merchant listing structured data). Those pages explain which properties are expected for parsing and that multiple shippingDetails entries are allowed.
Choose an implementation path
Pick the approach that fits your shop:
- Plugin/platform output: if your CMS or an SEO/structured‑data plugin can emit Offer.shippingDetails, use its documented fields and test the generated JSON‑LD.
- Merchant Center product shipping: for feed‑driven shops or when you prefer to control shipping in Google’s account, set product‑level shipping attributes in Merchant Center (these can override account‑level shipping). Google’s product‑level shipping guidance.
- Manual JSON‑LD snippet: if the plugin can’t emit the shipping properties, add a small Offer.shippingDetails JSON‑LD block on the product page that mirrors the visible rules. Keep it consistent with the on‑page text.
For WooCommerce shops, check whether your shipping plugin or theme already provides structured data before adding manual markup. See WooCommerce’s shipping documentation for platform‑specific notes: WooCommerce Shipping documentation.
Inspect the page’s JSON‑LD and run engine tests
How to check what’s actually published (observable steps you can run yourself):
- Open the product page, view source or use developer tools and search for the
<script type="application/ld+json">block containing the Product → Offer JSON‑LD. - Look for Offer.shippingDetails entries and confirm the values match the visible customer‑facing text (same costs, same destination country codes, same delivery ranges).
- Use observable checks so you can see how Google reads the URL or feed: inspect the product URL with Search Console’s URL Inspection (Search Console URL Inspection), use Merchant Center’s Product Inspector to confirm how product‑level shipping attributes and page markup are parsed (Product Inspector), and paste your JSON‑LD into Google’s product or merchant‑listing guidance pages to view quick syntax and parsing warnings (Product structured data, Merchant listing structured data).
- If you use Merchant Center, use the product inspector there to check account/ feed parsing and whether product‑level shipping attributes are read by the platform. See how Merchant Center accepts product shipping attributes.
Note: structured data makes the information machine‑readable. It does not by itself guarantee inclusion in any special search UI or a merchant feature — eligibility and display remain discretionary and account‑dependent. Google’s developer docs explicitly note that markup does not guarantee a feature.
Reconcile structured data with Merchant Center / feed settings
If you advertise via Merchant Center or upload a feed, decide which source governs the truth: account‑level shipping rules, product‑level feed attributes, or page markup. Merchant Center supports product‑level shipping attributes that can override account defaults; use that when you need feed‑level control. See Google’s note that product‑level shipping can override account settings: Product shipping in Merchant Center and feed shipping attributes.
Checklist (what to do now) and a short fictional example
- Copy the exact shipping text from one product page into a single document.
- Decide whether you’ll use plugin output, Merchant Center product attributes, or a manual JSON‑LD snippet.
- If using plugin output, read the plugin docs and inspect a live product page for Offer.shippingDetails fields.
- If adding markup manually, add one Offer.shippingDetails entry per distinct rule or destination and keep the visible page text unchanged.
- Inspect the page source for JSON‑LD, run Google’s structured‑data checks and the Merchant Center inspector (if applicable), and reconcile any parsing warnings.
- Document which source (page markup or Merchant Center feed) is the authoritative one for each product.
Fictional example (clearly labelled): A retailer sells a vase and offers free shipping within the UK for orders over £30 and a £6.00 flat rate to EU countries. They add the same policy sentence on the product page, then publish two Offer.shippingDetails entries: one with a zero shippingRate and shippingDestination.addressCountry set to “GB”, and a second with shippingRate = 6.00 and shippingDestination for EU country codes. They inspect the product page’s JSON‑LD and run Google’s structured‑data checks to confirm the two entries are parsed without mismatches.
Here is an illustrative JSON‑LD fragment you can adapt—adjust field names to the exact property names your platform or plugin expects and validate it with Google’s tools before publishing:
{
"@context": "https://schema.org",
"@type": "Product",
"name": "Example Vase",
"offers": {
"@type": "Offer",
"priceCurrency": "GBP",
"price": "45.00",
"shippingDetails": [
{
"@type": "OfferShippingDetails",
"shippingDestination": { "@type": "DefinedRegion", "addressCountry": "GB" },
"shippingRate": { "@type": "MonetaryAmount", "value": 0, "currency": "GBP" },
"deliveryTime": { "@type": "ShippingDeliveryTime", "handlingTime":{"@type":"QuantitativeValue","minValue":1,"maxValue":2}, "transitTime": {"@type":"QuantitativeValue","minValue":3,"maxValue":5} }
},
{
"@type": "OfferShippingDetails",
"shippingDestination": { "@type": "DefinedRegion", "addressCountry": "FR" },
"shippingRate": { "@type": "MonetaryAmount", "value": 6.00, "currency": "GBP" },
"deliveryTime": { "@type": "ShippingDeliveryTime", "handlingTime":{"@type":"QuantitativeValue","minValue":1,"maxValue":2}, "transitTime": {"@type":"QuantitativeValue","minValue":5,"maxValue":8} }
}
]
}
}
Finally, track the change: record the product URL, the date you updated markup or feed, and any parsing warnings you fixed. That gives you an observable before/after when you re‑run the structured‑data checks.
Related guides from this site: Validate product structured data with Google’s Rich Results Test and URL Inspection and WooCommerce shipping costs appear too late: show a useful estimate before checkout.
Key official references cited above: schema.org — OfferShippingDetails, Google’s merchant listing structured data, and Google’s product structured data guidance.

