SEO Development

Machine-Readable Offers: Structured Data That AI Agents Can Actually Buy From

10 min read

For most of the past decade, the case for structured data was rich results: review stars in the listing, a price under the snippet, a few extra pixels of search real estate. Useful, measurable, and easy to push down the backlog when something shinier turned up.

That calculus has changed. AI shopping agents — the assistant-led buying experiences taking shape inside ChatGPT, Perplexity and Google's own agentic surfaces — shortlist, compare and increasingly transact on a shopper's behalf. They do not admire your photography. They parse your Product and Offer markup, cross-check it against your Merchant Center feed, and decide whether your product is safe to recommend.

And when your data is incomplete or stale, nothing visibly breaks. There is no error page, and no Search Console report addressed to agents. You are simply absent from the answer.

Silence is the failure mode.

~70%

of product pages across the Shopify audits we run pass Google's Rich Results Test yet are missing at least one field an agent needs to act on — most often shipping costs, return policy or product identifiers. A composite figure from our own audit work, not a survey.


Rich results were the rehearsal

We covered the rich-results foundation in our structured data primer for subscription stores: Product schema, FAQ markup, validation, what you can earn in the SERP. All of it still stands. But that work was written for a renderer, a search engine deciding whether your snippet deserves stars. An agent is a buyer, and buyers ask harder questions.

A validator asks whether your markup is parseable. An agent asks whether it is actionable: can the price be trusted, can the variant be resolved, can shipping be costed, can the purchase be unwound if it goes wrong. A human shopper fills gaps by reading the page; an agent fills gaps by recommending someone else's product instead.

Valid is not the same as buyable.

That distinction reframes the whole job. Having a Product node stopped being the goal the moment software started spending money; the goal now is a complete, current, internally consistent offer. The wider picture, from discovery through to checkout and policies, is in our AI shopping agents readiness guide; this post is the data layer of that work, done properly.


Completeness over presence

Strip the schema.org vocabulary away and an agent has three questions: what is this, what does it cost right now, and what happens after I buy it. Your markup either answers all three or it doesn't. Here is the anatomy of an offer that survives all three layers of scrutiny.

Layer 1 · Identity

What is this, exactly?

Lets an agent reconcile your listing against the wider product graph and compare it like-for-like with other merchants.

name brand image gtin mpn sku

Layer 2 · Transaction

What does it cost, right now?

The fields an agent acts on directly. Every one must reflect the specific variant being bought, at this moment, in this currency.

price priceCurrency availability itemCondition priceValidUntil url

Layer 3 · Total cost & trust

What happens after I buy it?

How an agent does landed-cost arithmetic and assesses risk. The most commonly missing layer, and the cheapest differentiation available.

shippingDetails hasMerchantReturnPolicy aggregateRating

The anatomy of a machine-readable offer: an agent has to clear all three layers before it will quote, recommend or buy.

The identity layer is what most Shopify stores half-complete. gtin and mpn let an agent resolve your product to a known entity and compare your offer against every other merchant selling the same thing. Without identifiers you are a fuzzy string match, and fuzzy string matches lose to competitors whose products resolve cleanly.

The transaction layer looks done until you read it as a parser. itemCondition feels redundant on a DTC store where everything is new, but to a literal-minded agent absence is ambiguity, and ambiguity is grounds for exclusion. priceValidUntil is a promise about the future; an expired one on a live page is a quiet credibility leak.

The third layer is where agents diverge most from classic SEO. shippingDetails and hasMerchantReturnPolicy exist so software can compute the landed price, the delivery window, and the exit route if the customer changes their mind. In our experience these two properties are missing from the overwhelming majority of Shopify PDPs. aggregateRating then breaks ties between otherwise equivalent offers.


The staleness problem

Staleness is the quieter failure. A brand we worked with ran a weekend flash sale through a pricing app: the theme showed the sale price, the JSON-LD, hand-maintained in a metafield, kept the old one, and the Merchant Center feed re-synced overnight with a third value. For roughly thirty hours, three sources disagreed about what the product cost.

Classic search is forgiving about this. Google quietly drops the price snippet and moves on, and most merchants never notice. Agents are not forgiving: consistency between markup, feed and rendered page is itself a trust signal, and a mismatch is reliability data that outlives the fix.

"An agent doesn't see a pricing glitch. It sees a merchant whose data can't be trusted at the moment of transaction."

The fixes are mostly architectural. Generate JSON-LD from the live product object at render time rather than maintaining prices by hand in metafields. Audit app-injected markup, because two Product nodes with conflicting prices is worse than one wrong one. And treat priceValidUntil with respect: set it when a price genuinely has an end date, and never let one expire on a live offer.


Variants: the single-offer trap

Shopify's default themes are where good intentions ship a single offer. Out of the box, many themes emit one Offer carrying the first available variant's price, or an AggregateOffer holding only a low and high price. Either passes validation. Neither lets an agent buy the 5kg bag.

The failure is concrete: a shopper asks an agent for the best price on your 12-pack, the agent reads an offer describing the single, and your product is either quoted wrongly or skipped as unresolvable. For subscription brands with size and bundle variants, this is the most common structural gap we find.

Default theme

  • One Offer for the whole product
  • Price taken from the first available variant
  • Availability ignores sold-out variants
  • Agent quotes the wrong price, or skips you entirely

Per-variant offers

  • One Offer per variant in the offers array
  • Each carries its own sku, gtin, price, availability
  • Variant-specific URL resolves the exact selection
  • Agent can transact against the precise thing the shopper asked for

The single-offer trap versus per-variant offers: same product, same validator result, completely different outcome for an agent.

The fix is one Offer per variant, each with its own sku, gtin, price, availability and a variant-specific url so the agent lands on the exact selection. Keep an AggregateOffer alongside if you want the price-range display in classic results, but the per-variant entries are what agents transact against.


Representing subscription pricing without confusing the parser

Subscriptions are where the schema.org vocabulary runs out of road. There is no first-class recurring-offer type that the major consumers reliably read; UnitPriceSpecification can express billing duration and increment, but support is patchy and thinly documented. The temptation is to do something clever. Resist it — agents punish clever. Three patterns hold up in practice:

1. Anchor on the one-time price

The canonical Offer carries the one-off price, because that is the like-for-like comparable every agent understands. Putting the subscribe-and-save price in your only offer creates a permanent mismatch with what a one-time buyer sees at checkout: a staleness problem you inflicted on yourself.

2. Add the recurring option as a second, well-labelled Offer

A separate Offer with a clear name and description, plus a priceSpecification of type UnitPriceSpecification carrying the billing terms. Parsers that understand it gain the data; parsers that don't will ignore it. Well-formed extras cost nothing.

3. State the terms in prose too

Agents read rendered text as well as markup. Plain language near the price — "£39 every 4 weeks, cancel anytime" — is machine-readable in the way that matters most, and it covers the consumers that never look at your JSON-LD at all.

This is also a forcing function for keeping selling plans honest. If your markup promises a 10% subscription saving and the selling plan configuration has quietly drifted to 5%, an agent will eventually notice before you do, and it will remember which of the two sources lied.


Merchant Center: the second pipe

Agents do not live on crawled HTML alone. Google's shopping stack, and the agentic experiences built on top of it, draw heavily on the Shopping Graph, which is fed primarily by Merchant Center. For a large class of queries the feed is the primary source and your on-page markup is the corroboration. Two pipes, one truth — or at least there should be.

That has two implications. First, feed quality work is agent work: titles, identifiers, availability and price accuracy in the feed determine what a whole category of agents sees, and we've covered the mechanics in our Google Shopping feed guide for Shopify. Second, consistency across the pipes matters as much as quality within each. Price and availability mismatches between feed and page already trigger Merchant Center warnings and item disapprovals; the same mismatches quietly degrade agent confidence.

Shipping and return settings configured at account level in Merchant Center are machine-readable answers too. Align them with your on-page shippingDetails and hasMerchantReturnPolicy so an agent gets the same story wherever it looks.


A monitoring workflow that catches drift

None of this stays fixed on its own. Themes get updated, apps inject markup, prices change, selling plans drift. The stores that stay agent-readable run a standing workflow rather than a one-off project:

  • Validate twice. The Rich Results Test answers Google eligibility; the Schema.org validator catches structural problems Google's tool tolerates. They find different bugs.

  • Diff markup against the source of truth. Crawl your top product pages weekly and compare the JSON-LD price and availability against the Admin API. Alert on any mismatch, however small.

  • Read Merchant Center diagnostics as an agent-trust dashboard. Price and availability warnings are mismatch reports between your two pipes, not just ad-account housekeeping.

  • Track priceValidUntil expiries. An expired promise on a live offer is a silent downgrade. Calendar it like a certificate renewal.

  • Re-test after every theme or app change that touches the PDP. App-injected duplicate Product nodes are the most common regression we see in audit work.

  • Fetch your pages as a bot occasionally. What you see in a browser is not always what server-rendered HTML hands to an agent, especially where markup is injected client-side.


The shortlist is being drawn now

Agentic commerce is arriving unevenly: discovery first, comparison second, autonomous checkout last. But the shortlists are being assembled today, from data that exists today. An agent that learns your offers are complete and consistent keeps coming back. One that gets burned by a stale price has no reason to.

Everything in this post is unglamorous plumbing, which is precisely why it compounds. Your competitors will write the AI strategy deck; very few of them will add return policy markup to their variants.

Treat your product data as an API contract with software that spends money: version it with the discipline of a codebase, monitor it like uptime, and assume every gap is filled by a competitor. The brands doing that now will be the default answer when buying agents arrive in volume. Everyone else will be wondering why traffic that used to convert never arrives at all.