Skip to content
Published Authored byBilly Reiner

Schema · How-to

Shopify shippingDetails schema

shippingDetails is the Offer property that declares shipping costs, destinations, and delivery time for a product1. Its value is an OfferShippingDetails object with shippingRate (MonetaryAmount), shippingDestination (DefinedRegion with addressCountry), and deliveryTime (ShippingDeliveryTime with handlingTime + transitTime). For Google's shipping details enhancement on a merchant listing, all three are required6. Since November 2025 Google also reads a store-wide shipping policy from Organization markup (hasShippingService), and it recommends that route for standard shipping, keeping shippingDetails on the Offer for products that ship differently2. Some SEO publications claim Google AI Overviews requires shippingDetails on every product; no Google documentation says so.

The 2026 install pattern: describe standard shipping once, under Organization, and add OfferShippingDetails on the Offer only for products with different rates or times, or if you don't publish a store-wide policy. Either way, map the rate to the merchant's actual shipping rates and deliveryTime to the handling + transit windows configured in Shopify Settings > Shipping and delivery3. For free-shipping stores, the rate value is 0; for paid shipping, it's the real rate per destination region.

What OfferShippingDetails is

Per Schema.org v30.0, OfferShippingDetails 'represents information about shipping destinations.' Multiple OfferShippingDetails objects can attach to one Offer to represent different rates, costs, and delivery times across destinations. Inheritance: Thing > Intangible > StructuredValue > OfferShippingDetails. Key properties: shippingRate (MonetaryAmount or ShippingRateSettings), shippingDestination (DefinedRegion), deliveryTime (ShippingDeliveryTime), doesNotShip (Boolean), shippingOrigin (DefinedRegion).

Mental model: OfferShippingDetails declares the fulfilment story for one destination. A Shopify store shipping to US, Canada, and the UK should ideally emit three OfferShippingDetails sub-objects per product — one per region with the right rate and time for that region. For stores with a flat-rate global shipping policy, one OfferShippingDetails with broad applicability is enough.

What Google documents, and the AI Overviews claim

Google's merchant shipping policy documentation splits shipping in two. Standard shipping for the business goes in a ShippingService nested under Organization with hasShippingService, on a single page; you don't need it on every page. A product with non-standard shipping gets OfferShippingDetails directly under its Offer, which supports a subset of the Organization-level properties and overrides the standard policy for that product. Some SEO publications go further and say Google AI Overviews demands shippingDetails on every product. No Google documentation says that, so this page doesn't repeat it.

OfferShippingDetails fields

The properties Google requires for the shipping details enhancement and Schema.org defines: shippingRate (a MonetaryAmount with value and currency; Schema.org also allows ShippingRateSettings, but Google's Offer-level documentation types the rate as MonetaryAmount), shippingDestination (a DefinedRegion with addressCountry — ISO 3166-1 alpha-2 — and optionally addressRegion or postalCode ranges), deliveryTime (a ShippingDeliveryTime with handlingTime QuantitativeValue and transitTime QuantitativeValue, both expressed in days).

  • shippingRate — required. MonetaryAmount with value (Number) and currency (Text, ISO 4217), matching the offer's currency. One rate per OfferShippingDetails. For free shipping, value 0.
  • shippingDestination — required. DefinedRegion with addressCountry (one or array). For US only: 'US'. For multi-country: ['US', 'CA', 'GB'].
  • deliveryTime — required for the shipping details enhancement. ShippingDeliveryTime with handlingTime (QuantitativeValue minValue/maxValue/unitCode 'DAY' — typically 0–1 or 0–2 days) and transitTime (QuantitativeValue minValue/maxValue/unitCode 'DAY' — typically 2–5 or 3–7 days). Google requires the day counts to be non-negative whole numbers.
  • doesNotShip — optional. Boolean. Use when the merchant doesn't ship to specific regions.
  • shippingOrigin — optional. DefinedRegion. The fulfilment country, useful for international duty calculations.

JSON-LD example — OfferShippingDetails inside Offer

The block below shows shippingDetails inside the Offer sub-object of a Shopify Product block: the product-level form, for a product whose shipping differs from the store standard or for a store that doesn't publish a store-wide policy. Hard-coded US-only with $0 shipping and 0–1 day handling + 2–5 day transit. Adjust per the merchant's actual Shopify shipping zone configuration. Store-wide shipping uses a ShippingService under Organization instead; the Shopify schema generator's policies output shows that shape.

JSON-LD shippingDetails inside Offer, inside Product, inside main-product.liquid
"shippingDetails": {
  "@type": "OfferShippingDetails",
  "shippingRate": {
    "@type": "MonetaryAmount",
    "value": 0,
    "currency": {{ cart.currency.iso_code | json }}
  },
  "shippingDestination": {
    "@type": "DefinedRegion",
    "addressCountry": "US"
  },
  "deliveryTime": {
    "@type": "ShippingDeliveryTime",
    "handlingTime": {
      "@type": "QuantitativeValue",
      "minValue": 0,
      "maxValue": 1,
      "unitCode": "DAY"
    },
    "transitTime": {
      "@type": "QuantitativeValue",
      "minValue": 2,
      "maxValue": 5,
      "unitCode": "DAY"
    }
  }
}

The rate's currency comes from cart.currency.iso_code, the currency the shopper sees and the one the offer price is in, rather than shop.currency5. Google requires the shipping rate currency to match the offer currency6.

Mapping Shopify shipping zones to OfferShippingDetails

Shopify exposes shipping configuration at Settings > Shipping and delivery > Shipping (Shipping zones, with countries per zone and rates per zone). For accurate schema, each zone should map to one OfferShippingDetails sub-object — one for US, one for Canada, one for International. The shippingRate per OfferShippingDetails comes from the cheapest standard rate per zone (the rate most relevant for the structured data). Flat-rate stores can hard-code one object; multi-zone stores hard-code one object per zone from the values in Settings > Shipping and delivery, and update the markup whenever a rate changes. For many zones, the Organization-level ShippingService is easier to maintain, because it's written once rather than on every product.

Validation

Rich Results Test against a PDP with shippingDetails should report Product detected under Merchant listings, the Offer and its OfferShippingDetails parsed, zero errors. If deliveryTime, shippingDestination or shippingRate is missing, the shipping details enhancement can't use the object. An Organization-level ShippingService is tested separately: the Rich Results Test lists Shipping policies as a supported type. If you also set shipping in Merchant Center, those settings take precedence over the markup, so keep the two in agreement.

A subtle gotcha: Google accepts unitCode as "DAY" or "d" for handling and transit times, and nothing else6. Codes such as "days" or "HR" won't be read. Uppercase "DAY" matches the UN/CEFACT code list Schema.org uses.

Shopify gotchas on shippingDetails

Four gotchas. First: hard-coding shippingRate value 0 on a store with paid shipping — the markup is structurally valid but misrepresents the actual cost to shoppers. Use the merchant's real rate. Second: emitting shippingDestination as a single country on a multi-country store, leaving other regions unrepresented. Third: omitting deliveryTime, which Google requires for the shipping details enhancement. Fourth: using fractions for handlingTime / transitTime (handlingTime minValue: 0.5). Schema.org accepts any number, but Google requires non-negative whole numbers of days.

A fifth gotcha for stores with carrier-calculated shipping (USPS, FedEx, UPS): the actual shipping cost is computed at checkout based on weight/dimensions, not knowable in advance. At product level, Google's shippingRate is a MonetaryAmount that needs a value or a maxValue plus a currency6, so state the most a shopper in that region would pay rather than inventing an exact figure. The Organization-level ShippingService accepts richer conditions (order value, weight, rate settings) and is the better fit for rate tables. Either way, do not emit zero on a store where shipping is genuinely paid.