Skip to content

Optimising Product Pages for AI Agents: The Complete Technical Guide (2026)

An AI agent can read your product page and still refuse to recommend it. The three attributes that decide whether it can transact, the crawler tokens that decide whether it ever sees you, and what each platform emits by default. With our own audit of 36 UK and EU product pages.

The Sellarix team · 23 Jul 2026 · 36 min read

An AI shopping agent can read your product page, work out exactly what you sell, and still refuse to recommend it. Not because your price is too high. Because it can't tell whether that price is still valid, when the thing would arrive, or whether the customer could send it back.

That's the whole problem in one sentence, and it's a different problem from search engine optimisation. Google will happily rank a page it can't buy from. An agent won't put something in a basket it can't stand behind.

We audited 36 product pages across 14 UK and EU stores in July 2026, reading only the raw HTML with an identified crawler. Not one page carried all three of the attributes an agent needs. Thirty of the thirty-six carried none of them. The full method and the store names are further down, and you can run the same check on your own store with the free AI-readiness scanner, which uses exactly the field definitions in this guide.

This is long. It's meant to be the thing you send your developer. If you want the shorter, protocol-level view first, including what changed when OpenAI stepped back from agent checkout in March 2026, start with the agentic commerce readiness checklist.

What actually changed, and what didn't

Two things changed in 2026, and they get confused with each other constantly.

The first is discovery. Assistants started answering shopping questions with specific products rather than lists of shops, which means a retrieval layer now decides which SKUs get named. The second is transaction. Protocols arrived that let an agent complete a purchase without a human clicking through your checkout, and the two big ones come from OpenAI and Google respectively.

What didn't change is the plumbing. There is no new markup language. Google's own guidance on its AI features says it plainly: "You don't need to create new machine readable files, AI text files, or markup to appear in these features. There's also no special schema.org structured data that you need to add."[14]

That line gets quoted as "so you don't have to do anything," which is the wrong reading. Google is saying there's no secret AI schema. It isn't saying your existing schema is complete. Most of it isn't, and the gaps sit precisely where agents look.

Why agents are stricter than search engines

A search engine is optimising for a click. An agent is optimising for a decision it has to defend.

If it says "buy this, it'll be with you Thursday, and you've got 30 days to change your mind," and any part of that turns out to be wrong, the failure lands on the assistant rather than on you. So agents behave like a cautious buyer with a compliance department. They want the price confirmed as current, the delivery window stated, and the return terms readable before they commit. Anything they can't confirm, they route around.

Your product gets described instead of sold.

Does this actually affect you?

Probably, but not equally, and not urgently for everyone. Here's the honest split.

You're affected now if

You sell considered purchases where people ask for advice before buying. Furniture, technical outdoor kit, audio, baby equipment, anything with specifications. Those are the categories where assistants get used as a shortlisting tool, and the peer-reviewed data backs this up: the effect of AI referrals is stronger in complex product categories[35].

You're also affected if you sell branded goods that other retailers stock. Without a GTIN an agent can't tell that your listing and the listing on a marketplace are the same product, so it can't put you in the comparison at all.

You can probably wait if

You sell impulse or habit purchases at low value, your traffic is overwhelmingly paid social, or your catalogue is bespoke one-offs where comparison doesn't apply. The work below is still cheap, and it also earns you Google merchant listing eligibility, so it isn't wasted. But it isn't the fire.

The one thing everybody should check today

Whether your edge is blocking AI crawlers. Two minutes, free to fix, and it makes every other item on this list irrelevant if you get it wrong. Skip to the crawler section if you only read one part.

The three attributes an agent needs before it can transact

This is the sharpest difference between traditional SEO and agent readiness, and it's the thing almost nobody has done.

An agent has to answer three questions before it recommends buying:

  1. Is this still the price? Maps to offers.priceValidUntil.
  2. When does it arrive? Maps to offers.shippingDetails.deliveryTime.
  3. Can it go back? Maps to offers.hasMerchantReturnPolicy.merchantReturnDays.

Miss any one and the agent has a product it can talk about but can't responsibly close. SALT.agency audited 141 product pages across 29 top retailers in July 2026 and found 70% lacked all three. Only 18% carried priceValidUntil, 13% carried shippingDetails.deliveryTime, and 11% carried merchantReturnDays[1].

priceValidUntil, and why it's a date not a flag

Schema.org defines it as "the date after which the price is no longer available", expected type Date, sitting on Offer[3]. Google's merchant listing documentation adds the sting: "Your listing may not display if the priceValidUntil property indicates a past date."[2]

Read that twice. A stale priceValidUntil is worse than none at all. Plenty of stores set it once during a migration, hardcode a date, and quietly stop being eligible for merchant listings eighteen months later without anyone noticing.

So it has to roll forward. Either drive it from your pricing calendar, so it genuinely says when the current price expires, or emit today plus a fixed window from the template. BigCommerce's default theme does the second thing, and we'll come back to that.

shippingDetails.deliveryTime, and why a shipping rate isn't enough

A lot of stores emit OfferShippingDetails with a shippingRate and stop there. That answers "how much does postage cost," which is not the question. The question is when it turns up.

The answer lives in deliveryTime, a ShippingDeliveryTime carrying handlingTime and transitTime, each a QuantitativeValue with a minimum, a maximum and a unit[4]. Our scanner deliberately only counts a deliveryTime that carries a real window. An empty ShippingDeliveryTime node scores nothing, because it tells an agent nothing.

hasMerchantReturnPolicy.merchantReturnDays, and the "see our policy" trap

merchantReturnDays has to be a number[5]. "Contact us to arrange a return" is a sentence, not data.

And this is the one where UK and EU merchants have an advantage they mostly don't use. You already have a statutory 14-day cancellation window, so the number exists. It just isn't published anywhere a machine can read it.

Google treats both shippingDetails and hasMerchantReturnPolicy as recommended rather than required, and suggests global policies can sit on Organization instead of on every offer[2]. Fine for Google. Weaker for agents, which are reading one product page in isolation and may never fetch your homepage. Put it on the offer.

What we found: 36 product pages, 14 UK and EU stores, one that got it right

Published research on this is almost entirely US and almost entirely enterprise. So we ran our own, small, on this side of the Atlantic.

The method, stated plainly

On 23 July 2026 we attempted 30 UK and EU stores across Shopify, Adobe Commerce, headless builds and custom platforms. For each one we fetched robots.txt, found product URLs from the sitemap, and requested up to three product pages with an honestly identified crawler user agent. No JavaScript was executed. We read exactly what the raw HTTP response contained, because that's what GPTBot, OAI-SearchBot, PerplexityBot and ClaudeBot get.

Fourteen of the thirty gave us readable product pages, 36 in total: cotswoldoutdoor.com, finisterre.com, heals.com, hifiheadphones.co.uk, lucyandyak.com, mountainwarehouse.com, naturalbabyshower.co.uk, passenger-clothing.com, rudehealth.com, sealskinz.com, seasaltcornwall.com, wearewild.com, wexphotovideo.com and whitestuff.com.

This is a small sample chosen by hand. It is not a study. Treat it as a spot check, and go and run the same check on your own store rather than trusting our thirty-six pages.

What the pages carried

SignalPresentShare of 36
Product JSON-LD in the raw HTML2672%
Readable without JavaScript3392%
offers.priceValidUntil26%
shippingDetails.deliveryTime411%
hasMerchantReturnPolicy.merchantReturnDays26%
GTIN617%
MPN1542%
SKU1953%
All three agentic attributes00%
None of the three3083%

Then we checked ourselves, because the first pass could have been unfair

Parsing JSON-LD strictly can undercount. If a store publishes a ProductGroup and hangs every offer off hasVariant, a parser that reads offers on the top node misses things that are genuinely there. Finisterre's product pages do exactly that.

So we went back and ran a second, deliberately dumb pass: does the string appear anywhere in the HTML at all, including inside inline JavaScript. That can only over-count. Twenty-nine of the thirty-six pages answered on the second pass, and the numbers barely moved. priceValidUntil appeared on 2 of 29. Anything shipping-time-shaped on 3. Anything return-policy-shaped on 7.

One page out of twenty-nine carried all three. A Shopify store, naturalbabyshower.co.uk, selling baby equipment. Twenty-one carried none.

What surprised me

Not the low numbers. I expected those. What surprised me is that server-side rendering has largely been solved and identifiers have not.

92% of the pages we read came back with real content before JavaScript ran, which is much better than the received wisdom about headless builds suggests. White Stuff runs a Nuxt front end and its product markup was in the response. Mountain Warehouse was the exception, and it was the exception in the way that matters most: no product markup in the raw HTML at all.

Meanwhile only 17% published a GTIN under a strict parse, and 45% on the loose one. For clothing and homeware brands selling own-label goods that's arguably defensible, since there may be no barcode. For a retailer like Wex Photo Video, selling third-party Nikon and Fujifilm accessories that exist on a dozen other sites, it isn't. Those products have GTINs. They just aren't on the page.

The caveat I have to state

Thirteen of the thirty homepages answered 403, 429 or nothing at all to an identified bot. Some of that is ordinary rate limiting rather than a policy, and our crawler visited a few of those stores more than once across the run, so don't read it as "43% of UK stores block AI." Read it as this: an unfamiliar, honestly-identified crawler has a meaningfully worse time reaching a UK store than a browser does, and nobody at those stores has ever checked.

Stable identifiers: GTIN, MPN, SKU

Without an identifier an agent can show you. It can't compare you.

SALT found 65% of top retailers publish no GTIN[1]. Our smaller sample was worse. Google's product documentation asks for "all applicable global identifiers" and the most specific GTIN that applies to the product[6].

Three rules cover almost every case.

  • Use the real barcode in gtin13 or gtin12. Don't put your internal SKU in a gtin field. A wrong identifier is worse than a missing one, because it merges your product with somebody else's.
  • Own-brand with no barcode gets mpn plus brand. That's the documented substitute and it's perfectly acceptable.
  • Every variant needs its own identifier and its own offer. One identifier on the parent tells an agent nothing about the size the customer asked for. If you use ProductGroup, set productGroupID and variesBy, and give each variant a real offer.

If your product data is spread across a spreadsheet, an ERP and three apps, this is the point where that stops being survivable. We wrote about when that tips over in do you need a PIM.

Crawler access: the check that makes the rest of this pointless

A blocked crawler is a hard stop. Everything else in this guide only matters for the bots that can reach you.

The seven tokens

These are the exact user-agent strings our scanner checks, and they're the ones that decide whether an assistant can read, cite or shop your catalogue.

TokenWhoWhat it does
GPTBotOpenAICrawls pages for model training[7]
OAI-SearchBotOpenAIBuilds the ChatGPT search index. Sites opted out "will not be shown in ChatGPT search answers"[7]
ChatGPT-UserOpenAIFetches a page live when a user or agent asks about it[7]
PerplexityBotPerplexityIndexes pages for Perplexity answers and shopping[8]
ClaudeBotAnthropicCrawls pages for Claude[9]
Google-ExtendedGoogleControls Gemini and AI Overviews grounding. Does not affect Google Search crawling[10]
BingbotMicrosoftFeeds Bing, and Copilot answers are built on the Bing index

Anthropic has since split its crawling into three tokens: ClaudeBot for training, Claude-User for live fetches, and Claude-SearchBot for search indexing[9]. If you want to be cited by Claude without contributing to training, that's now a decision you can actually make.

Blocking a bot is not always a mistake

Here's a real example from our sample. Thomann, the German musical instrument retailer, has this in its robots.txt[12]:

User-agent: GPTBot
Disallow: /

Every AI-visibility tool on the market would flag that red. But look at what they blocked and what they didn't. GPTBot is the training crawler. OAI-SearchBot, the one that decides whether you appear in ChatGPT search answers, is untouched. Thomann opted out of training and stayed in shopping results.

That's a coherent position, and it's the position most brands say they want when you ask them. So the honest advice isn't "allow everything." It's: know which token does what, and make the block on purpose.

robots.txt is the easy half. Your WAF is the hard half

This is where most stores actually fail, and where the failure is invisible from the admin.

Cloudflare changed the default on 1 July 2025. From that date, "every new domain will now be asked if they want to allow AI crawlers, giving customers the choice upfront to explicitly allow or deny AI crawlers access"[11]. Existing customers kept their settings. New ones got asked, and a lot of people clicked block without much thought, because it was framed as protecting your content.

It also blocks the thing that puts your products in front of buyers. Both are true. Just decide which one you meant.

A WAF rule fires before robots.txt is ever read, so a perfect robots.txt file proves nothing. The only test that counts is the one-liner:

curl -A "GPTBot" -s -o /dev/null -w "%{http_code}\n" https://yourstore.com/products/your-best-seller
curl -A "OAI-SearchBot" -s -o /dev/null -w "%{http_code}\n" https://yourstore.com/products/your-best-seller
curl -A "PerplexityBot" -s -o /dev/null -w "%{http_code}\n" https://yourstore.com/products/your-best-seller

You want 200 back. If you get 403, you have a WAF problem. If you get 429 on the first request of the day, you have a rate-limiting problem, and an agent that gets throttled once usually doesn't come back.

The robots.txt block, if you want them in

Named groups go above your wildcard group. A bot uses its own group when it has one and ignores the rest[13].

# Answer-engine crawlers. Allow these if you want to be found and cited.
User-agent: GPTBot
Allow: /

User-agent: OAI-SearchBot
Allow: /

User-agent: ChatGPT-User
Allow: /

User-agent: PerplexityBot
Allow: /

User-agent: ClaudeBot
Allow: /

User-agent: Google-Extended
Allow: /

User-agent: Bingbot
Allow: /

Sitemap: https://yourstore.com/sitemap.xml

On Shopify that file is Online Store, Themes, Edit code, robots.txt.liquid.

Agents don't run your JavaScript

They fetch your HTML and read it. That's it.

If your price only appears after hydration, it doesn't exist as far as they're concerned. Same for your product grid, your variant selector and your delivery estimate. Google's own merchant listing guidance says it directly: "If you're a merchant optimizing for all types of shopping results, we recommend putting Product structured data in the initial HTML for best results."[2]

Two traps worth naming.

Tag-manager schema. Plenty of agencies inject JSON-LD through Google Tag Manager because it's faster than touching the theme. Googlebot renders, so it works for Google. Agents don't render, so it works for nobody else. If your structured data comes from GTM, it is invisible to every crawler in the table above.

The "it's server-rendered" assumption. Next.js and Nuxt sites are usually fine, as our sample showed. But a route that opts into client rendering, or a price that comes from a separate pricing API called on mount, breaks the same way a full single-page app does. Check the page, not the framework.

The test, again, is one line:

curl -s https://yourstore.com/products/your-best-seller | grep -c 'application/ld+json'

If this whole area is new to you, our piece on page speed covers the rendering side of the same problem from the human visitor's angle. The fixes overlap more than you'd expect.

Category and policy pages, because a product page alone isn't enough

Auditing only product pages answers half the question. An agent works out what you sell from your collections before it opens a single product, and it can't complete a purchase without your terms.

Collections need three things

A machine-readable list (ItemList or CollectionPage)[15], product links as real anchors in the HTML, and the grid present before JavaScript runs. Infinite scroll is fine for humans. Keep paginated <a href> links in the markup underneath it so the rest of the collection stays reachable. Add BreadcrumbList too, because it's how an agent works out where a collection sits in your catalogue.

A collection that renders client-side is a dead end. Every product below it is invisible no matter how good the product template is. Our scanner counts three or more crawlable product links as the floor for a real grid, which is deliberately low and still catches plenty of stores.

Policy pages need a number

An agent reads your returns page to check its answer. "See our returns policy" tells it nothing. Write the window as a number in the visible copy, and put MerchantReturnPolicy schema on the page as well. Then link returns, shipping and terms from the footer of every page, because that's where an agent looks first and it's one line of theme code.

The protocols, and their real status

These move fast and most articles about them are six months stale. Here's where they actually are.

ACP, from OpenAI and Stripe

The Agentic Commerce Protocol is an open spec co-developed by OpenAI and Stripe[18][16]. A merchant implements three things: a product feed, checkout endpoints, and delegated payments[17].

The under-reported bit: in March 2026 OpenAI moved Instant Checkout out of product listings in the chat and into merchant apps inside ChatGPT. The stated reason was that transaction complexity, inventory, tax and pricing, is better handled per merchant[19]. ACP survives as the infrastructure. The consumer surface changed.

Practical read for a mid-market merchant: ACP is not something you implement on a Tuesday. If you're on Shopify or Stripe it arrives through them. If you're not, it's a genuine engineering project, and the surface it plugs into has already moved once this year.

UCP, from Google

The Universal Commerce Protocol is Google's open standard, co-developed with Shopify, Etsy, Wayfair, Target and Walmart[22]. Google describes it as enabling "direct, instant purchases across AI surfaces like AI Mode in Google Search and the Gemini app"[20]. You stay merchant of record, which means you keep the customer relationship, the tax position and the fulfilment obligation[23].

The part you can act on today is the manifest. A UCP profile is a JSON file at /.well-known/ucp, and Google's requirement is blunt: it "must be publicly accessible and not require any authentication"[21]. It declares the services and capabilities you support, your payment handlers, and public signing keys.

It's also the cleanest binary signal there is that a store is set up for agents, which is exactly why our scanner checks for it and scores it low. Almost nobody has one. Worth little today, possibly worth a lot next year.

Status check before you plan around it: UCP is still gated. Google runs a waitlist and is expanding into new verticals from a standing start[20]. Don't rebuild your checkout for it yet.

MCP, which is a different animal

Model Context Protocol is a standard for connecting AI models to tools and data[24]. It isn't a commerce protocol. It's the transport a lot of commerce tooling now rides on.

Shopify implements UCP across the buyer journey through UCP-compliant MCP servers covering discovery, carts, checkout and orders[25], and Shopify's own announcement notes UCP supports REST, MCP, AP2 or A2A as transports[38]. If you're not on a platform that ships this, MCP is not your first job. Your product data is.

If you're on Shopify

Two separate things are going on here, and conflating them is the most common mistake I see.

Agentic Storefronts is on by default. Your schema is not

Shopify's help centre states plainly that "Agentic storefronts is active by default for eligible stores", covering ChatGPT, Google AI Mode and Gemini, Microsoft Copilot and Meta[26]. Direct checkout in Copilot is "activated by default for eligible stores" and can be turned off per channel[27].

You'll read a lot of posts saying Google AI Mode and Copilot need a manual toggle that's off by default. That isn't what Shopify's documentation says today, and I'd rather correct it than repeat it.

What is true, and what almost everyone misses, is the dependency underneath. Google AI Mode and Gemini reach your products through the Google and YouTube sales channel, and Meta through the Facebook and Instagram channel[26]. If that channel isn't installed and your products aren't syncing to it, "on by default" gets you nothing on those surfaces. Shopify also notes Google AI Mode and Gemini "is in early access for Shopify's agentic storefronts, and isn't yet available for all stores."[26]

Go and look now. Settings, Sales channels, Agentic. Then check the Google and YouTube channel separately and confirm your product sync is actually green.

What Shopify Catalog does, and what it doesn't cover

For the connected AI channels, Shopify Catalog is "the authoritative product data feed to your agentic channels", and products are listed "with their title, description, options, images, price, availability, and other key attributes"[28]. Shopify says Catalog data converts at twice the rate of scraped product data[39], which is vendor-published and directionally believable. Shopify sells the platform that claim describes.

Here's the bit people get wrong. Catalog covers the channels Shopify has deals with. It does not cover Perplexity reading your page, Claude fetching it live, an AI Overview grounding on it, or any assistant without a Shopify integration. Those all read your HTML.

So on-page schema still matters on Shopify, and it matters exactly as much as it does on WooCommerce.

What Dawn actually emits, which is less than you'd hope

Dawn calls the built-in structured_data Liquid filter, which outputs @context, @id, @type, brand, category, description, image, name, url, and an offers node with availability, price, priceCurrency and url[29].

No priceValidUntil. No shippingDetails. No hasMerchantReturnPolicy. No gtin. No mpn.

Our sample bore that out exactly. Every default-ish Shopify store we read came back with the same shape, and the ones carrying more had either an app or a hand-edited theme doing the work.

The fix

Stop using the filter on the product template and emit the JSON-LD yourself. In sections/main-product.liquid, replace the structured_data call with an explicit block that reads your metafields for delivery and returns, and your barcode field for the GTIN. Shopify stores the barcode per variant, so the data is nearly always already in the admin. It just isn't on the page.

Half a day on the template, and it applies to every SKU you'll ever add.

Not on Shopify? The other platforms

This is where the gap is widest, and where nobody is writing, because most agentic-commerce content is really Shopify content wearing a hat.

WooCommerce

Better than its reputation, and short in one specific place.

Reading the core class directly, WooCommerce outputs name, url, description, image, sku, gtin when valid, offers, aggregateRating and review. The Offer includes price, priceCurrency, priceSpecification, availability, url, seller and, usefully, priceValidUntil[30].

So Woo gives you one of the three for free, plus a GTIN field in core. What it does not emit is shippingDetails, hasMerchantReturnPolicy or mpn[30].

The fix is a filter in your child theme's functions.php. No plugin needed, and I'd argue no plugin wanted: free SEO plugins mostly don't add these fields either, and paid ones add a dependency for about thirty lines of code.

add_filter( 'woocommerce_structured_data_product_offer', function ( $offer ) {
    $offer['shippingDetails'] = [
        '@type'               => 'OfferShippingDetails',
        'shippingRate'        => [
            '@type'    => 'MonetaryAmount',
            'value'    => '4.95',
            'currency' => 'GBP',
        ],
        'shippingDestination' => [
            '@type'          => 'DefinedRegion',
            'addressCountry' => 'GB',
        ],
        'deliveryTime'        => [
            '@type'        => 'ShippingDeliveryTime',
            'handlingTime' => [
                '@type'    => 'QuantitativeValue',
                'minValue' => 0,
                'maxValue' => 1,
                'unitCode' => 'DAY',
            ],
            'transitTime'  => [
                '@type'    => 'QuantitativeValue',
                'minValue' => 2,
                'maxValue' => 4,
                'unitCode' => 'DAY',
            ],
        ],
    ];

    $offer['hasMerchantReturnPolicy'] = [
        '@type'                => 'MerchantReturnPolicy',
        'applicableCountry'    => 'GB',
        'returnPolicyCategory' => 'https://schema.org/MerchantReturnFiniteReturnWindow',
        'merchantReturnDays'   => 30,
        'returnMethod'         => 'https://schema.org/ReturnByMail',
        'returnFees'           => 'https://schema.org/FreeReturn',
    ];

    return $offer;
}, 10, 1 );

Hardcoding the shipping rate is fine if you have one rate. If you have zones, read them from the zone that matches the offer instead, and be honest rather than publishing a rate most customers won't get.

Magento and Adobe Commerce

The hardest of the four, and the one with the biggest gap between documentation and reality.

Adobe's own documentation says "By default, structured data markup that is based on the schema.org standard is added to your store's product template."[31] True, and thin. What ships is basic markup, not a complete merchant listing, and there's no priceValidUntil, no shippingDetails and no hasMerchantReturnPolicy anywhere in it.

Both Adobe Commerce stores in our sample carried Product JSON-LD with mpn and sku, and none of the three agentic attributes. That JSON-LD is coming from a theme or an extension, not from core.

The right fix is a small module with a block that renders the JSON-LD into product.info.main, reading your delivery estimate and returns window from configuration rather than hardcoding them. That's a day of a Magento developer's time and it survives upgrades, which hand-edited template files do not.

One Magento-specific trap our audit turned up, and it's worth ten minutes on any Magento store. Two of the stores we sampled Disallow: /catalog/product/view/ in robots.txt for every user agent, which is standard hygiene because those are non-canonical duplicates. Correct. What isn't correct is when those same URLs end up in a secondary sitemap, which is how our crawler found them. If a URL is in a sitemap and disallowed in robots.txt, you're pointing every crawler, AI or otherwise, at a door you've locked. Check that your sitemaps only list your pretty URLs.

BigCommerce

Quietly the best of the four out of the box, and nobody says so.

Reading the Cornerstone theme's schema partial directly, BigCommerce emits name, sku, mpn, a correctly-suffixed gtin (it picks gtin8, gtin12, gtin13 or gtin14 from the length of the value), brand, description, image, itemCondition, availability, price, priceCurrency, and this line[32]:

"priceValidUntil": "{{moment (moment add='31536000000') 'YYYY-MM-DD' }}"

That's today plus one year, computed at render time. It rolls forward automatically and can never go stale. It's the pattern I'd recommend to everyone else, and BigCommerce has shipped it as a default for years.

What's missing is the same pair as everywhere else: no shippingDetails, no hasMerchantReturnPolicy. Add them to the same partial. Be aware that theme updates can overwrite the file, so keep the change in version control and re-apply it after each Cornerstone bump.

Headless and custom builds

You have no excuse and no default, which is roughly a fair trade.

Nothing is emitted for you, so everything is a decision. That means you can get this completely right in an afternoon. It also means nobody will tell you when it breaks, and a schema regression looks exactly like a working page in a browser.

Three things matter more here than anywhere else.

  • Render the JSON-LD on the server. In Next.js that means it comes from the server component or the route handler, not from an effect that runs on mount. If your framework streams, make sure the script tag lands in the first flush.
  • Put it under test. One integration test per template that fetches the rendered HTML, parses the JSON-LD and asserts that priceValidUntil, deliveryTime and merchantReturnDays are present and non-empty. Ten lines. It's the only thing that stops a refactor silently deleting your agent readiness.
  • Watch the price source. The most common headless failure we see is markup rendered server-side from a cached catalogue while the visible price comes from a live pricing call. The agent reads the cached one. If those two can disagree, you're publishing a price you won't honour, which is a worse problem than having no schema at all.

If you're weighing a re-platform while you're in here anyway, the trade-offs are in our pieces on headless commerce and choosing an ecommerce platform.

llms.txt: cheap to publish, and near-worthless

You'll be told to add one. Here's the evidence.

Ahrefs checked 137,210 domains that received traffic in May 2026. 28% published a valid llms.txt. 97% of those files received zero requests. Of the requests that did land, 96% came from bots, and AI retrieval bots, the ones that would actually cite you, accounted for 1.1%. The single biggest fetcher category was SEO audit tools at 21.7%[33].

Sit with that for a second. The main consumer of llms.txt is the tools that check whether you have an llms.txt.

Google's John Mueller compared it to the keywords meta tag, noting that server logs show AI services don't even check for it[34]. And Google's AI-features guidance says you don't need new machine-readable files at all[14].

Our scanner checks for llms.txt and gives it a weight of zero, and says so in the report. That's deliberate. Plenty of readiness tools score you down for not having one, because a checklist item that takes five minutes to fix makes for a satisfying report. It doesn't make it true.

Publish one if you like. It costs nothing. Just don't let it displace an afternoon on your product template, and be suspicious of any audit that ranks it above your crawler access.

The complete PDP checklist

Everything below is deterministically checkable from outside your store, in one crawl. This is the artefact. Copy it into your ticket tracker.

Access

  • robots.txt allows GPTBot, OAI-SearchBot, ChatGPT-User, PerplexityBot, ClaudeBot, Google-Extended, Bingbot, or blocks specific ones on purpose
  • Named groups sit above the wildcard group
  • WAF and bot-management rules verified with curl -A, returning 200 not 403
  • No 429 on a first request from an unfamiliar crawler
  • Sitemap declared in robots.txt, and nothing in the sitemap is disallowed

Product page: the three that decide it

  • offers.priceValidUntil present, a future date, rolling forward automatically
  • offers.shippingDetails.deliveryTime present with handlingTime and transitTime, each carrying real minimum and maximum values
  • offers.hasMerchantReturnPolicy.merchantReturnDays present as a number

Product page: the floor

  • Product JSON-LD in the raw server response, not injected by a tag manager
  • name, image, offers.price, offers.priceCurrency, availability
  • gtin13 or gtin12 from the real barcode, or mpn plus brand for own-label
  • sku present, with per-variant offers carrying per-variant identifiers
  • Real product content in the HTML before JavaScript runs
  • Validates at validator.schema.org

Category pages

  • ItemList or CollectionPage schema naming what's in the collection
  • Product grid as real anchors in the HTML, three or more
  • Readable without JavaScript
  • BreadcrumbList present

Policy pages

  • Returns, shipping and terms linked from the homepage and the footer
  • Returns window stated as a number in the visible copy
  • MerchantReturnPolicy or OfferShippingDetails schema on the page
  • Readable without JavaScript

Site level

  • Organization and WebSite schema on the homepage
  • Crawlable catalogue links on the homepage, as ordinary anchors
  • Guest checkout available
  • /.well-known/ucp manifest, if you want the strongest available signal
  • llms.txt: optional, worth nothing, don't let it displace the rest

The copy-paste template

This is the whole thing with the three critical attributes filled in correctly. Put it in the head of the server-rendered product page. It's the same shape our scanner hands back as a fix, so if you paste this and re-run the scan, that check goes green.

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "Product",
  "name": "Merino Runner",
  "description": "Lightweight merino trainer, 240g, machine washable.",
  "image": ["https://yourstore.com/img/merino-runner.jpg"],
  "sku": "MR-42-BLK",
  "gtin13": "5012345678900",
  "mpn": "MR42BLK",
  "brand": { "@type": "Brand", "name": "Your Brand" },
  "offers": {
    "@type": "Offer",
    "url": "https://yourstore.com/products/merino-runner",
    "price": "98.00",
    "priceCurrency": "GBP",
    "availability": "https://schema.org/InStock",

    "priceValidUntil": "2026-12-31",

    "shippingDetails": {
      "@type": "OfferShippingDetails",
      "shippingRate": {
        "@type": "MonetaryAmount",
        "value": "4.95",
        "currency": "GBP"
      },
      "shippingDestination": {
        "@type": "DefinedRegion",
        "addressCountry": "GB"
      },
      "deliveryTime": {
        "@type": "ShippingDeliveryTime",
        "handlingTime": {
          "@type": "QuantitativeValue",
          "minValue": 0,
          "maxValue": 1,
          "unitCode": "DAY"
        },
        "transitTime": {
          "@type": "QuantitativeValue",
          "minValue": 2,
          "maxValue": 4,
          "unitCode": "DAY"
        }
      }
    },

    "hasMerchantReturnPolicy": {
      "@type": "MerchantReturnPolicy",
      "applicableCountry": "GB",
      "returnPolicyCategory": "https://schema.org/MerchantReturnFiniteReturnWindow",
      "merchantReturnDays": 30,
      "returnMethod": "https://schema.org/ReturnByMail",
      "returnFees": "https://schema.org/FreeReturn"
    }
  }
}
</script>

Four things to get right when you adapt it. priceValidUntil must come from your pricing calendar or a rolling date, never a hardcoded constant. deliveryTime needs both handling and transit, because a shipping rate on its own doesn't answer "when does it arrive". merchantReturnDays must be a number. And the whole block belongs in the server response.

The honest counterweight

Now the part most content on this topic leaves out.

AI referrals are still small. The peer-reviewed number, from Kaiser and Schulze in Marketing Science 45(4), covers 973 ecommerce sites with $20bn of combined annual revenue over twelve months, comparing more than 50,000 ChatGPT-referred transactions against 164 million from traditional channels. ChatGPT accounted for less than 0.2% of all traffic, with conversion above paid social but below every other established channel. Their conclusion is that this is a new and developing channel serving niche informational needs, not a broad conversion driver[35].

The growth rates are real too. Shopify reported AI-driven orders up nearly 13 times year on year in Q1 2026, AI-driven traffic up 8 times, and new buyer orders from AI arriving at nearly twice the rate of traditional organic search[36]. Adobe, across more than a trillion visits to US retail sites, put AI-referred traffic up 138% year on year in May 2026 and up more than fourteenfold since October 2024, with machine-readability running from 47% in furniture to 63% in cosmetics[37].

Both sets of numbers are true at the same time. Thirteen times a very small number is still a small number.

So I'm not going to tell you you're bleeding money today, because you probably aren't, and a sharp reader would catch me at it. What I'll say instead is that this is cheap now and expensive to retrofit. A rolling priceValidUntil and a delivery window on the product template is half a day while you're already in there. Doing it across four locales, three themes and a legacy PIM in eighteen months, under pressure, because a channel suddenly matters, is a project.

That's the trade. Not panic. Sequencing.

What to do this week

  1. Run the curl test on one product page. Three commands, three user agents, three status codes. Ten minutes. If any come back 403, that's your entire week's work and it's worth more than everything else on this list.
  2. View source on your best-selling product page and search for priceValidUntil. Not in the console. In the raw source. You'll know in thirty seconds where you stand.
  3. Run the free scanner on your live store. It samples your homepage, five product pages, three category pages and a policy page, then tells you which of these checks you fail with the actual evidence. Same field definitions as this guide.
  4. Fix the template, not the page. One change to the product template covers every SKU. One change to a page covers one page.
  5. Pull your barcodes out of the admin. On Shopify they're already stored per variant. On Woo there's a core GTIN field. Most stores have the data and simply aren't publishing it.
  6. Put a number on your returns page and add MerchantReturnPolicy schema to it. An hour, on two pages.
  7. If you're on Shopify, open Settings, Sales channels, Agentic and check the Google and YouTube channel is installed and syncing. "Agentic storefronts is on" is not the same thing.
  8. Write the test. Whatever your stack, one assertion that the three attributes exist in the rendered HTML. Otherwise this decays quietly.

What's still uncertain, said out loud

Four things I can't tell you, and I'd rather say so than pretend.

Whether the protocols consolidate. ACP and UCP overlap heavily and are backed by companies that compete with each other. OpenAI has already moved its consumer surface once this year[19]. Anyone telling you which one wins is guessing.

Whether agents will read pages at all in two years, or whether everything routes through platform feeds like Shopify Catalog. If it's feeds, on-page schema matters less for merchants on big platforms and just as much for everyone else. That asymmetry is one of the reasons cross-platform work is worth doing at all.

How much of the traffic is real buying intent. The peer-reviewed data says conversion is mid-table[35]. The platform data says it's twice organic for new buyers[36]. They're measuring different populations over different windows and I don't think either is wrong.

Whether blocking training crawlers costs you anything. Thomann blocks GPTBot and allows OAI-SearchBot[12]. That should be free. Nobody has published evidence either way, and I've seen no test that would settle it.

If you're also working through EU AI Act transparency duties on the same product pages, the marking and disclosure obligations are covered in our EU AI Act compliance guide. They overlap more than you'd expect. The product template that carries your delivery window is usually the one carrying AI-generated imagery, and both changes land in the same file.

The takeaway

Three attributes, seven crawler tokens, one rule about JavaScript. That's most of it. The protocols are worth understanding and mostly not worth implementing yet unless your platform does it for you.

Our thirty-six pages found one store that got all three right, and it was a Shopify shop selling prams. Everyone else, including some very good, very well-run brands, is publishing a product an agent can describe but can't sell.

Go and view source on your best seller. Search for priceValidUntil. What did you find?

Sources

  1. 70% Of Top Retailers Are Invisible To Agentic Commerce. Reza Moaiandin, SALT.agency, published in Search Engine Journal, 21 July 2026. 207 product pages sampled, 141 audited, across 29 retailers. Accessed 23 July 2026.
  2. Merchant listing (Product) structured data. Google Search Central. Accessed 23 July 2026.
  3. priceValidUntil. Schema.org. Accessed 23 July 2026.
  4. OfferShippingDetails. Schema.org. Accessed 23 July 2026.
  5. MerchantReturnPolicy. Schema.org. Accessed 23 July 2026.
  6. Product structured data overview. Google Search Central. Accessed 23 July 2026.
  7. OpenAI crawlers and user agents. OpenAI developer documentation. Accessed 23 July 2026.
  8. PerplexityBot and Perplexity-User. Perplexity documentation. Accessed 23 July 2026.
  9. Does Anthropic crawl data from the web, and how can site owners block the crawler? Anthropic support. Accessed 23 July 2026.
  10. Google common crawlers, including Google-Extended. Google Search Central. Accessed 23 July 2026.
  11. Cloudflare Just Changed How AI Crawlers Scrape the Internet-at-Large. Cloudflare press release, 1 July 2025.
  12. thomann.de/robots.txt. Retrieved 23 July 2026. A named example of a deliberate GPTBot block with OAI-SearchBot left open.
  13. Introduction to robots.txt. Google Search Central. Accessed 23 July 2026.
  14. AI features and your website. Google Search Central. Accessed 23 July 2026.
  15. ItemList. Schema.org. Accessed 23 July 2026.
  16. Agentic Commerce Protocol specification. Maintained by OpenAI and Stripe. Accessed 23 July 2026.
  17. OpenAI commerce documentation. Product feed, checkout endpoints and delegated payments. Accessed 23 July 2026.
  18. Stripe powers Instant Checkout in ChatGPT and releases the Agentic Commerce Protocol. Stripe newsroom. Accessed 23 July 2026.
  19. OpenAI shifts checkout plans in its agentic commerce strategy. Digital Commerce 360, 6 March 2026.
  20. Getting started with Universal Commerce Protocol on Google. Google for Developers. Accessed 23 July 2026.
  21. UCP profile. Google for Developers. The /.well-known/ucp manifest specification. Accessed 23 July 2026.
  22. Under the Hood: Universal Commerce Protocol (UCP). Google Developers Blog, January 2026.
  23. About the Universal Commerce Protocol and UCP-powered checkout on Google. Google Merchant Center Help. Accessed 23 July 2026.
  24. Model Context Protocol. Accessed 23 July 2026.
  25. Agentic commerce on Shopify. shopify.dev. Accessed 23 July 2026.
  26. Shopify agentic storefronts. Shopify Help Center. Accessed 23 July 2026.
  27. Using AI channels with direct checkout. Shopify Help Center. Accessed 23 July 2026.
  28. Shopify Catalog and product discovery for agentic storefronts. Shopify Help Center. Accessed 23 July 2026.
  29. structured_data Liquid filter. shopify.dev. The default Dawn product JSON-LD output. Accessed 23 July 2026.
  30. WC_Structured_Data. WooCommerce core source, trunk branch. Accessed 23 July 2026.
  31. Meta data. Adobe Commerce documentation, Experience League. Accessed 23 July 2026.
  32. Cornerstone product schema partial. BigCommerce Cornerstone theme source, master branch. Accessed 23 July 2026.
  33. We Analyzed 137K Sites: 97% of llms.txt Files Never Get Read. Louise Linehan, Ahrefs, June 2026. Server-log data from May 2026. Ahrefs sells SEO tooling, so read the framing with that in mind. The log data is the log data.
  34. Google Says llms.txt Comparable To Keywords Meta Tag. Search Engine Journal, reporting John Mueller. Accessed 23 July 2026.
  35. Frontiers: ChatGPT Referrals to E-Commerce Websites: How Do LLMs Compare Against Traditional Channels? Maximilian Kaiser and Christian Schulze, Marketing Science 45(4), published 21 April 2026. 973 websites, $20bn revenue, August 2024 to July 2025. Peer-reviewed.
  36. Shopify Q1 2026 earnings call transcript. 5 May 2026. Company-reported figures on AI-driven orders and traffic.
  37. Adobe: AI-referred traffic to retail sites doubles in a year. Digital Commerce 360, 17 June 2026, reporting Adobe Analytics data from more than one trillion visits to US retail sites.
  38. AI commerce at scale. Shopify newsroom. UCP transports and agentic storefronts. Accessed 23 July 2026.
  39. Agentic Commerce on Shopify: How It Works. Shopify, 2026. Vendor-published; Shopify sells the platform this describes.

Our own audit data: 30 UK and EU stores attempted, 36 product pages read across 14 stores, 23 July 2026. Fetched with an identified crawler user agent, raw HTML only, no JavaScript executed. Field definitions match those used by the Sellarix AI-readiness scanner. A small hand-picked sample, not a study.

Frequently asked