Short answer
How do you optimise a product page?
Most of e-commerce SEO is not writing copy but fixing product data. The product page needs complete Product and Offer markup, a price and currency identical to what the page shows, and a GTIN in numerical form. Variant products are tied together with ProductGroup. Generative engines read this data when recommending products; missing data makes a product impossible to recommend with its price.
Key takeaways
- The fields Google requires for merchant listing experiences are few: on Product, name, image and offers; on Offer, a price greater than zero and priceCurrency in ISO 4217 form.
- The GTIN must be given in numerical form; Google does not support the URL form for GTINs.
- Only pages where a shopper can purchase the product are eligible for merchant listings — not pages carrying links to other sites that sell it.
- Variant products use ProductGroup: variants are distinguished by sku or gtin, productGroupID stays identical across them, and variesBy is written with the full schema.org URL (such as https://schema.org/color).
- For variant markup to work, each variant must be directly selectable through a distinct URL using query parameters.
- An out-of-stock product's page is not deleted; availability is updated to OutOfStock and alternatives are shown on the page.
Why does e-commerce SEO not start with writing copy?
On an e-commerce site the content is product data, not blog posts. A search engine and a generative engine ask the same questions when recommending a product: what is it called, who makes it, what does it cost, in which currency, is it in stock, what is the returns position. If the answers are not on the page in structured form, the product stays unrecommendable no matter how well it is written about.
So the order is settled: product data first, page copy second, category content last. Projects that start from the other end typically end up with a beautifully written catalogue whose prices are invisible to machines — and a product whose price cannot be read falls out of scope on every question that asks for a comparison.
Which data is required on a product page?
The set of fields Google requires for merchant listing experiences is smaller than people expect; the real difference is made by the recommended ones. Without the required fields a product is not listed at all; without the recommended ones it is listed, but incompletely.
Fields for a merchant listing
name
- Status
- Required
- Note
- Product name, identical to the page
image
- Status
- Required
- Note
- Product image
offers
- Status
- Required
- Note
- Containing an Offer object
offers.price
- Status
- Required
- Note
- Must be greater than zero
offers.priceCurrency
- Status
- Required
- Note
- In ISO 4217 form (TRY, EUR, USD)
gtin / gtin8 / gtin13
- Status
- Recommended
- Note
- Numerical form; the URL form is not supported
sku
- Status
- Recommended
- Note
- Merchant-specific product code
brand.name
- Status
- Recommended
- Note
- Brand name
availability
- Status
- Recommended
- Note
- InStock, OutOfStock, PreOrder
itemCondition
- Status
- Recommended
- Note
- New, used, refurbished
shippingDetails
- Status
- Recommended
- Note
- Shipping information
hasMerchantReturnPolicy
- Status
- Recommended
- Note
- Return terms
aggregateRating
- Status
- Recommended
- Note
- Only a real rating that is visible on the page
| Field | Status | Note |
|---|---|---|
| name | Required | Product name, identical to the page |
| image | Required | Product image |
| offers | Required | Containing an Offer object |
| offers.price | Required | Must be greater than zero |
| offers.priceCurrency | Required | In ISO 4217 form (TRY, EUR, USD) |
| gtin / gtin8 / gtin13 | Recommended | Numerical form; the URL form is not supported |
| sku | Recommended | Merchant-specific product code |
| brand.name | Recommended | Brand name |
| availability | Recommended | InStock, OutOfStock, PreOrder |
| itemCondition | Recommended | New, used, refurbished |
| shippingDetails | Recommended | Shipping information |
| hasMerchantReturnPolicy | Recommended | Return terms |
| aggregateRating | Recommended | Only a real rating that is visible on the page |
Google's rule is explicit: only pages where a shopper can purchase the product are eligible for merchant listing experiences, not pages linking to other sites that sell it. Comparison pages and affiliate listings fall outside for exactly this reason.
{
"@context": "https://schema.org",
"@type": "Product",
"name": "Ergo 300 office chair",
"image": ["https://example.com/img/ergo-300.jpg"],
"description": "Office chair with adjustable lumbar support and 4D armrests.",
"sku": "ERG-300-BLK",
"gtin13": "8691234567895",
"brand": { "@type": "Brand", "name": "Kuzey" },
"offers": {
"@type": "Offer",
"url": "https://example.com/product/ergo-300",
"price": "8450.00",
"priceCurrency": "TRY",
"availability": "https://schema.org/InStock",
"itemCondition": "https://schema.org/NewCondition",
"hasMerchantReturnPolicy": {
"@type": "MerchantReturnPolicy",
"applicableCountry": "TR",
"returnPolicyCategory": "https://schema.org/MerchantReturnFiniteReturnWindow",
"merchantReturnDays": 14,
"returnMethod": "https://schema.org/ReturnByMail",
"returnFees": "https://schema.org/FreeReturn"
}
}
}Note that gtin13 is quoted and contains digits only. Writing the barcode as an address (the URL form) is not supported and causes the field to be ignored entirely.
How do you mark up product variants?
In a shop with colour and size options this is the most expensive markup mistake there is. Mark the same chair's four colours as four separate Products and an engine sees four different products, each a near-duplicate of the others; done properly it sees one parent product with four variants attached.
- The parent node is of type ProductGroup, and the only field it requires is name.
- productGroupID is the parent's code (the parent sku) and is written identically on the ProductGroup and on every variant.
- Variants are either listed under hasVariant or attached upward from their own pages with isVariantOf.
- Every variant needs its own sku or gtin; that is what distinguishes them.
- The variesBy field names the distinguishing properties by their full schema.org URL: https://schema.org/color, https://schema.org/size. The supported values are colour, size, suggested age, suggested gender, material and pattern.
{
"@context": "https://schema.org",
"@type": "ProductGroup",
"name": "Ergo 300 office chair",
"productGroupID": "ERG-300",
"variesBy": ["https://schema.org/color"],
"hasVariant": [
{
"@type": "Product",
"sku": "ERG-300-BLK",
"color": "Black",
"gtin13": "8691234567895",
"offers": {
"@type": "Offer",
"url": "https://example.com/product/ergo-300?color=black",
"price": "8450.00",
"priceCurrency": "TRY",
"availability": "https://schema.org/InStock"
}
},
{
"@type": "Product",
"sku": "ERG-300-GRY",
"color": "Grey",
"gtin13": "8691234567901",
"offers": {
"@type": "Offer",
"url": "https://example.com/product/ergo-300?color=grey",
"price": "8450.00",
"priceCurrency": "TRY",
"availability": "https://schema.org/OutOfStock"
}
}
]
}The condition is this: each variant must be directly selectable through its own URL query parameter. If the colour choice only changes in JavaScript while the address stays the same, the variants cannot be surfaced individually even with perfect markup.
When variants sit on separate pages the rule changes: each page must carry full, self-contained markup. The ProductGroup definition is repeated on every variant page, with the other variants listed as stub entries carrying only a url. In the single-page structure there must instead be exactly one canonical URL for the ProductGroup, and selecting a variant does not change it.
Why must the feed and the page say the same thing?
The product feed and the markup on the page describe the same product through two channels, and when they disagree neither side wins — the product loses credibility in both. The most common disagreement is price: the feed updates nightly while the page changes instantly, so at the start of a promotion the two records do not match.
- Price and currency must update in the feed and the page markup at the same moment; generating both from one source solves this structurally.
- Availability follows the same rule: the page saying "sold out" while the markup still says InStock is among the most common causes of a listing being dropped.
- The GTIN must be identical and numerical in both channels, and the most specific GTIN that applies to the product is the one to use.
- If the brand reads "Kuzey" in the feed and "Kuzey Mobilya" on the page, two different manufacturers appear; pick one spelling and hold it everywhere.
- A description copied from the manufacturer shares the same text with hundreds of retailers in both channels; the only thing that makes the product distinguishable is the usage and dimension detail you write yourself.
Category page or product page?
The distinction is intent. Someone searching "office chair" has not decided yet and wants to see options; a category page answers that. Someone searching "Ergo 300 price" has decided and is verifying; a product page answers that. Chasing both on one page means half-serving both intents.
In generative engines the distinction sharpens further, because a question like "which office chair should I buy" sends the engine looking for a page it can compare from. Putting a table on the category page that compares dimensions, materials and use cases is the shortest route to making that page quotable.
Should an out-of-stock product page be deleted?
No. Deleting the page throws away the link equity and the search history that accumulated at that address, and when the product returns you start from zero. The right move is to update availability in the Offer markup to OutOfStock, state the situation plainly on the page, and show alternatives.
If the product is gone for good the decision changes: redirect the page to its nearest equivalent with a 301. Redirect to the equivalent product, not the category — bulk-redirecting to a category drops the user on a list that does not contain what they were looking for, and on the search side it gets treated as a soft 404.
How do you write a product description that gets quoted?
A description copied from the manufacturer shares its text with hundreds of retailers, so no engine has a reason to prefer it. What distinguishes a page is the information the manufacturer did not write: who the product suits, which size matches which use, what it is used alongside, and where it falls short.
- Put a 40–75 word answer passage at the top of the page: what the product is, who it suits and what distinguishes it, in one paragraph.
- Write the specifications as a table rather than prose; engines lift a table whole and reformat it at answer time.
- Give dimensions with units and in one system; the adjective "wide" enters no comparison, "64 cm" does.
- Write the three most frequent questions into the product page — delivery time, return terms, compatibility. These are the last questions before a decision.
- Show customer reviews on the page; a rating that appears in the markup must be visible on the page, and a rating you generated yourself cannot be published.
Frequently asked
What happens if I copy the manufacturer's description?
- The page is not penalised, but it becomes indistinguishable. Among hundreds of retailers publishing the same text there is no reason for an engine to pick you; at that point the choice shifts to price, brand recognition and how complete the technical data is. The dimension, usage and compatibility detail you write yourself is the only thing separating your page from the rest.
Do I have to show the price in the markup?
- If you want merchant listing experiences, yes: price and currency are required fields and the price must be greater than zero. In a model that does not publish prices — quote-based sales, dealer pricing — Product markup is still written, but merchant listings should not be expected. There, the product page's job is to be comparable on technical data instead of price.
Should filter pages be indexed?
- Most should not. The product of colour, size and price-range filters generates thousands of addresses, nearly all of them marginal copies of one another. The rule: filter combinations with genuine search demand — "black office chair", say — get their own page and are indexed; the rest are tied back to the main category with a canonical.
Can we publish our own scoring as aggregateRating?
- No. A rating in the markup must be visible on the page and must come from genuine customer reviews. A score a company awards its own product is an unverifiable claim; markup like that carries an enforcement risk and, on the generative side, drags down the credibility of the whole page.
Sources
- 01Merchant listing (Product) structured data — Google Search Central, 2026
- 02Product variant (ProductGroup) structured data — Google Search Central, 2026
- 03Product data specification (GTIN, brand, availability) — Google Merchant Center, 2026
- 04Structured data markup that Google Search supports — Google Search Central, 2026
- 05GEO: Generative Engine Optimization — Aggarwal, Murahari, Rajpurohit, Kalyan — KDD 2024, 2024
Queries this page answers
- ecommerce seo
- product page seo
- product schema markup
- product variant structured data
- out of stock seo