Optimization Guide
Ecommerce Aggregate Offer Schema — AggregateOffer, Multi-Seller Listing & Price Range Structured Data
AI shopping agents handling queries like "cheapest new iPhone 16 Pro across sellers," "best-rated used MacBook Air under $600," or "compare refurbished vs new Sony headphones pricing" need machine-readable price ranges, seller identities, and condition spectrums on a single product page. Shopify's default single-Offer JSON-LD can't express this. AggregateOffer is the schema.org solution — covering multi-seller marketplace pages, wide price-range variant products, and new-plus-used dual-condition listings.
AggregateOffer instead of Offer when a product page shows multiple sellers or a price range. Set lowPrice (lowest), highPrice (highest), offerCount (number of distinct offers), and priceCurrency. Include each individual Offer in the offers array with Offer.seller (Organization) and itemCondition. For Shopify multi-variant price ranges, calculate min/max prices in Liquid and conditionally render AggregateOffer vs single Offer.
When to Use AggregateOffer vs Offer
Schema.org's AggregateOffer is a subtype of Offer designed specifically for situations where one product page represents multiple individual offers. The key decision point is whether your page shows one price or a price range that depends on seller or condition selection.
| Scenario | Use | Why |
|---|---|---|
| Single seller, one fixed price | Offer |
No aggregation needed — one offer, one price |
| Single seller, variants with same price | Offer |
Price is not variable — single Offer with eligibleQuantity for sizes |
| Single seller, variants with different prices | AggregateOffer |
Expresses price range honestly — e.g., S/M/L at $29, XL/2XL at $34 |
| Multi-seller marketplace page | AggregateOffer |
Each seller is a distinct Offer with its own seller, price, condition |
| New + used condition on one page | AggregateOffer |
Different itemCondition per Offer, different price — aggregate wraps both |
| Multiple storage/config tiers (64GB/128GB/256GB) | AggregateOffer |
Each configuration is a distinct Offer at its own price point |
Shopify's default behavior is to always output a single Offer with the lowest available variant price — even for products where the highest variant price is 3× the lowest. This creates incorrect structured data: an AI agent sees "price: $29" for a product that actually costs up to $89 in the larger size. AggregateOffer corrects this by expressing the honest price range.
Full AggregateOffer Example — Multi-Seller Marketplace Page
The classic marketplace use case: a single product listed by multiple third-party sellers on a Shopify store using the Shopify marketplace framework or a multi-vendor app (WCFM, MultiSafepay MarketPlace, Zegsu).
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Product",
"name": "Sony WH-1000XM5 Wireless Noise-Cancelling Headphones",
"description": "Industry-leading noise cancellation, 30-hour battery, multipoint Bluetooth. The Sony WH-1000XM5 available from multiple verified sellers.",
"sku": "WH-1000XM5",
"mpn": "WH1000XM5/B",
"gtin13": "4548736132115",
"brand": { "@type": "Brand", "name": "Sony" },
"offers": {
"@type": "AggregateOffer",
"lowPrice": "249.00",
"highPrice": "349.99",
"priceCurrency": "USD",
"offerCount": 4,
"offers": [
{
"@type": "Offer",
"price": "249.00",
"priceCurrency": "USD",
"availability": "https://schema.org/InStock",
"itemCondition": "https://schema.org/UsedCondition",
"url": "https://example-marketplace.com/products/sony-wh1000xm5?seller=gadgetresale",
"seller": {
"@type": "Organization",
"name": "GadgetResale Pro",
"url": "https://gadgetresalepro.com",
"sameAs": ["https://www.trustpilot.com/review/gadgetresalepro.com"],
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "4.7",
"reviewCount": "312",
"bestRating": "5"
}
},
"description": "Grade A refurbished — 90-day warranty, original accessories included"
},
{
"@type": "Offer",
"price": "299.00",
"priceCurrency": "USD",
"availability": "https://schema.org/InStock",
"itemCondition": "https://schema.org/RefurbishedCondition",
"url": "https://example-marketplace.com/products/sony-wh1000xm5?seller=certifiedtech",
"seller": {
"@type": "Organization",
"name": "CertifiedTech Outlet",
"url": "https://certifiedtech.com",
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "4.9",
"reviewCount": "1240",
"bestRating": "5"
}
},
"description": "Sony Certified Refurbished — 1-year manufacturer warranty"
},
{
"@type": "Offer",
"price": "328.00",
"priceCurrency": "USD",
"availability": "https://schema.org/InStock",
"itemCondition": "https://schema.org/NewCondition",
"url": "https://example-marketplace.com/products/sony-wh1000xm5?seller=audioworld",
"seller": {
"@type": "Organization",
"name": "AudioWorld Direct",
"url": "https://audioworlddirect.com",
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "4.8",
"reviewCount": "892",
"bestRating": "5"
}
},
"description": "Brand new in sealed retail box — US warranty"
},
{
"@type": "Offer",
"price": "349.99",
"priceCurrency": "USD",
"availability": "https://schema.org/InStock",
"itemCondition": "https://schema.org/NewCondition",
"url": "https://example-marketplace.com/products/sony-wh1000xm5?seller=premiumav",
"seller": {
"@type": "Organization",
"name": "Premium AV Store",
"url": "https://premiumavstore.com"
},
"description": "New sealed box, includes Sony 2-year extended warranty registration"
}
]
}
}
</script>
The outer AggregateOffer provides the price summary signal (lowPrice: $249, highPrice: $349.99, offerCount: 4). AI agents parsing this can answer "Sony WH-1000XM5 price range" ($249–$350) and "best-rated seller" (CertifiedTech at 4.9★ from 1,240 reviews) from structured data alone, without scraping the page.
Shopify Variant Price Range — AggregateOffer Pattern
The most common single-seller use case for AggregateOffer in Shopify is a product where size, configuration, or storage variant substantially changes the price. The standard Shopify JSON-LD outputs only the lowest variant price, which misrepresents the product to AI agents.
{% comment %} product-schema-aggregate.liquid — AggregateOffer for variant price ranges {% endcomment %}
{% assign prices = product.variants | map: 'price' %}
{% assign min_price = prices | sort | first %}
{% assign max_price = prices | sort | last %}
{% assign offer_count = product.variants | where: 'available', true | size %}
{% if min_price == max_price %}
{% comment %} All variants same price — use single Offer {% endcomment %}
{% assign use_aggregate = false %}
{% else %}
{% assign use_aggregate = true %}
{% endif %}
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Product",
"name": {{ product.title | json }},
"description": {{ product.description | strip_html | truncatewords: 60 | json }},
"sku": {{ product.selected_or_first_available_variant.sku | json }},
"brand": { "@type": "Brand", "name": {{ product.vendor | json }} },
"offers": {
{% if use_aggregate %}
"@type": "AggregateOffer",
"lowPrice": {{ min_price | money_without_currency | json }},
"highPrice": {{ max_price | money_without_currency | json }},
"offerCount": {{ offer_count }},
"priceCurrency": {{ cart.currency.iso_code | json }},
"offers": [
{% for variant in product.variants %}
{% if variant.available %}
{
"@type": "Offer",
"price": {{ variant.price | money_without_currency | json }},
"priceCurrency": {{ cart.currency.iso_code | json }},
"availability": "https://schema.org/InStock",
"url": "{{ shop.url }}{{ product.url }}?variant={{ variant.id }}",
"sku": {{ variant.sku | json }},
"name": {{ variant.title | json }}
}{% unless forloop.last %},{% endunless %}
{% endif %}
{% endfor %}
]
{% else %}
"@type": "Offer",
"price": {{ min_price | money_without_currency | json }},
"priceCurrency": {{ cart.currency.iso_code | json }},
"availability": {% if product.available %}"https://schema.org/InStock"{% else %}"https://schema.org/OutOfStock"{% endif %},
"url": "{{ shop.url }}{{ product.url }}"
{% endif %}
}
}
</script>
This Liquid snippet conditionally outputs AggregateOffer (when variants have different prices) or a plain Offer (when all variants share the same price). The forloop.last check prevents trailing commas in the Offer array.
itemCondition Spectrum for Refurb and Resale Marketplaces
When AggregateOffer covers products across multiple condition states, the itemCondition on each individual Offer is what enables AI agents to filter by condition. The schema.org OfferItemCondition vocabulary has six values:
| itemCondition value | Schema.org URL | When to use |
|---|---|---|
| NewCondition | https://schema.org/NewCondition |
Factory sealed, never used, original packaging |
| RefurbishedCondition | https://schema.org/RefurbishedCondition |
Restored to manufacturer spec — use for Certified Refurbished, OEM-refurbished, or third-party Grade A refurb with warranty |
| UsedCondition | https://schema.org/UsedCondition |
Pre-owned, may show wear, may lack original packaging |
| DamagedCondition | https://schema.org/DamagedCondition |
Visibly damaged, cosmetic defects, sold as-is |
| OldCondition | https://schema.org/OldCondition |
Vintage/antique items sold because of their age — not just pre-owned |
| OpenBoxCondition | https://schema.org/OpenBoxCondition |
Opened but unused — returned, display unit, or scratch-and-dent |
For each condition tier, add a description on the Offer explaining what that condition means for your specific grading system (e.g., "Grade A: 0–2 minor scratches, battery above 85%, all original accessories"). AI agents use this free-text description alongside the structured itemCondition to answer nuanced condition queries like "like-new condition, no major scratches."
Offer.seller with Trust Signals for Marketplace Pages
When multiple sellers list the same product, the seller identity and reputation are the primary differentiators. The Offer.seller Organization entity can carry trust signals that AI shopping agents use to rank offers beyond just price:
{
"@type": "Offer",
"price": "299.00",
"priceCurrency": "USD",
"seller": {
"@type": "Organization",
"name": "TechVault Pro",
"url": "https://techvaultpro.com",
"telephone": "+1-800-555-0199",
"address": {
"@type": "PostalAddress",
"addressCountry": "US",
"addressLocality": "Austin",
"addressRegion": "TX"
},
"sameAs": [
"https://www.trustpilot.com/review/techvaultpro.com",
"https://www.bbb.org/us/tx/austin/profile/electronics/techvault-pro-0000-0000"
],
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "4.8",
"bestRating": "5",
"reviewCount": "2341"
},
"vatID": "US-27-3456789"
},
"warranty": {
"@type": "WarrantyPromise",
"durationOfWarranty": { "@type": "QuantitativeValue", "value": "1", "unitCode": "ANN" },
"warrantyScope": "All defects, including battery, screen, and all components"
}
}
The sameAs array pointing to Trustpilot and BBB profiles enables AI agents to cross-reference seller reputation from external verification sources — not just the seller's own claims. AI trust systems trained on marketplace data weigh third-party verification URLs in sameAs as stronger signals than unverifiable internal ratings. See Ecommerce Seller Organization Schema for the full seller trust signal pattern.
5 Common AggregateOffer Schema Mistakes
| # | Mistake | Impact | Fix |
|---|---|---|---|
| 1 | Using single Offer for wide price-range variant products (e.g., $29–$89 by size) | AI agents see only the lowest price (Shopify default) and treat the product as a $29 item — buyers reaching the page expecting $29 find the size they want costs $89, causing distrust and exit. Price misrepresentation in structured data also risks Google's product data quality penalties | Use AggregateOffer with lowPrice and highPrice reflecting the actual variant price range, and include individual Offer entries for each significantly-priced variant |
| 2 | Omitting offerCount from AggregateOffer | Google's rich result eligibility for marketplace-style product pages requires offerCount. Missing it reduces the likelihood of the enhanced "N offers" display in search results and signals to AI agents that the aggregation is incomplete | Always set offerCount to the integer count of available (in-stock) Offer entries in the offers array |
| 3 | No itemCondition on individual Offers in a new+used AggregateOffer | Without condition values on individual Offers, AI agents cannot distinguish new from used pricing — "best price for new Sony headphones" queries cannot filter out used-condition offers that are cheaper but not what the buyer wants | Set itemCondition on every individual Offer within AggregateOffer — do not rely on AggregateOffer-level condition since it applies to all child Offers equally |
| 4 | Seller entity has no aggregateRating or sameAs verification URLs | On marketplace pages, buyers rely on seller reputation to choose between equal-price offers. Without aggregateRating on the Offer.seller entity, AI agents have no structured signal for seller quality — all sellers appear equally trustworthy regardless of actual reputation | Add aggregateRating to each Offer.seller Organization, and include sameAs pointing to Trustpilot, BBB, or Google Business Profile — these verification links carry stronger trust weight than internal ratings |
| 5 | AggregateOffer lowPrice and highPrice inconsistent with actual variant prices | If lowPrice/highPrice don't match the actual range of child Offer prices, Google's structured data validator flags it as an error and may reduce rich result eligibility. AI agents that cross-check prices against individual Offer entries may also flag the page as having data quality issues | Calculate lowPrice and highPrice programmatically from the same variant data used to populate individual Offer entries — never hardcode them independently |
Frequently Asked Questions
When should I use AggregateOffer vs single Offer?
Use AggregateOffer when: (1) multiple distinct sellers list the same product on one page; (2) a product has variants at substantially different price points (different by more than ~15%); (3) the page shows both new and used condition options. Use a single Offer when one seller offers the product at one price, or when all variants share the same price. For Shopify stores, the most common AggregateOffer case is a product where apparel sizes, storage configurations, or material choices drive price differences. See also Shopify ProductGroup Variant Schema for the variant-per-Offer pattern within a ProductGroup.
What is offerCount and why does it matter?
offerCount is the integer number of distinct offers for the product — sellers × conditions. Google uses it for rich result display in search (showing "4 offers from $249" in product panels). AI agents use it as a marketplace signal: a high offerCount suggests competitive pricing across multiple sources. Always compute it from the actual available Offer count — out-of-stock Offers should not be counted. For Shopify variant price ranges, offerCount = number of in-stock variants with distinct price points.
How does Offer.seller work in multi-seller AggregateOffer?
Each Offer in the array has a seller property pointing to an Organization entity: { '@type': 'Organization', 'name': 'Seller Name', 'url': 'https://sellersite.com' }. Add aggregateRating and sameAs (Trustpilot, BBB, Google Business) to the Organization for trust signals. This pattern is identical for both Shopify marketplace apps and multi-vendor stores. For the full seller Organization trust pattern, see Ecommerce Seller Organization Schema.
Can AggregateOffer show new and used condition offers together?
Yes — this is AggregateOffer's primary use case alongside marketplace listings. Include Offer entries with different itemCondition values: NewCondition, RefurbishedCondition, UsedCondition, OpenBoxCondition. The AggregateOffer lowPrice reflects the cheapest offer (usually used) and highPrice the most expensive (usually new). AI agents filtering by condition use itemCondition on individual Offers, not on the AggregateOffer level. For the full condition vocabulary, see Shopify Pre-Owned & Resale Condition Schema.
Does Shopify support AggregateOffer natively?
No — Shopify's built-in product JSON-LD always outputs a single Offer with the lowest variant price. To implement AggregateOffer, add a custom script block in your theme's product.liquid that conditionally renders AggregateOffer when product.price_varies is true (Shopify's built-in flag for products with price-varying variants). Use Liquid to loop through variants and calculate min/max prices. Alternatively, use a structured data app like Schema Plus or Rich Snippets which support AggregateOffer generation automatically. Run a CatalogScan audit to check whether your current theme is outputting correct price ranges or single-price misrepresentation.
Are your multi-variant price ranges misrepresented in structured data?
CatalogScan audits your Shopify store's structured data and identifies products where single-Offer output misrepresents variant price ranges, missing seller trust signals, incorrect itemCondition values, and absent offerCount — ensuring AI shopping agents see accurate pricing and seller data.
Run Free Scan