# How do I make product variants and stock readable for AI agents?

> Agents fail on unlabeled swatches and greyed-out sizes. Give each variant a URL and a text stock state, and mark it up as a ProductGroup with variesBy.

- Canonical URL: https://aiscovery.io/resources/product-variants-stock-ai-agents
- Markdown version: https://aiscovery.io/resources/product-variants-stock-ai-agents.md
- Last updated: 2026-10-21

Give every variant its own URL that preselects it, write each option's name and stock state as text in the page, and describe the product as a `ProductGroup` with `variesBy`, one `Product` per variant and an `availability` value on each offer. Agents fail when sizes and colours exist only as unlabeled swatches, or when sold out is shown only as a greyed-out button.

- Topic: Product facts
- Author: Aiscovery team
- Published: 2026-10-21
- Updated: 2026-10-21
- Reading time: 9 min read

## Key facts

- Google's variant markup is a `ProductGroup` with `variesBy`, `hasVariant` and `productGroupID`.
- Google says the site must be able to preselect each variant with a distinct URL, and recommends product markup in the initial HTML.
- Shopify's `structured_data` filter outputs a ProductGroup with variants, but no `variesBy`, colour or size (Dawn and Horizon demo stores, October 2026).
- WooCommerce core marks up a variable product as an `AggregateOffer` with a price range, not a ProductGroup.
- Google accepts one `availability` value per offer, such as `InStock`, `OutOfStock`, `BackOrder` or `PreOrder`.

## Where variant pickers lose AI agents

Browsing agents read a page through its text, its accessibility tree and screenshots. A picker that a person understands at a glance can carry no words at all. Anthropic's computer use docs note that some interface elements, such as dropdowns, can be tricky for the model to operate with the mouse.

Variant picker problems and what the agent sees:

| On the page | What the agent sees | Fix |
| --- | --- | --- |
| Colour swatches with no text | A row of unnamed buttons or images | A visible or screen-reader label with the colour name |
| Sold-out size shown greyed out or struck through | A size that looks selectable | The words Sold out in the label, and a disabled control |
| Price and stock change only after a click | The first variant's price for every variant | A URL per variant that renders its own price and stock |
| Custom dropdown built from divs | Text with no control to operate | A native `select` or radio buttons |
| Stock message loaded by an app after the page | Nothing, if it reads the page before the app runs | Render the stock state in the HTML |

> **Where agents get stuck.** Asked whether a size is in stock, an agent that can't tell a greyed-out size from an available one either guesses or says it couldn't check. Both lose the sale to a store whose page says Sold out in words.

## Make the picker readable

### 1. Label every option in text

Use radio buttons or a native `select` with real labels. A swatch can stay visual, but its label must name the colour. Put the stock state in the label too.

**HTML: labeled options with a stock state**

```html
<fieldset>
  <legend>Size</legend>
  <input type="radio" id="size-s" name="size" value="S">
  <label for="size-s">S</label>
  <input type="radio" id="size-m" name="size" value="M" disabled>
  <label for="size-m">M, sold out</label>
</fieldset>
<p id="stock-status" aria-live="polite">Olive, size S: in stock, ships in 1 to 2 business days.</p>
```

### 2. Give each variant a URL that preselects it

Shopify themes already use `?variant=` with the variant ID. Open one directly and check that the page shows that variant's price, stock and image before you click anything. Google requires this for variant markup.

### 3. Write stock as words

In stock, Only 3 left, Sold out, Back order: ships in 2 weeks, Pre-order: ships in March. A colour or an icon alone carries no meaning to an agent.

### 4. Keep the default variant honest

If the first variant is sold out, don't preselect it with no message. Preselect an available one, or say clearly that the selected option is sold out and which ones aren't.

Stock states, on-page text and schema.org availability:

| Your stock state | Say on the page | availability |
| --- | --- | --- |
| In stock | In stock | `InStock` |
| Low stock | Only 3 left | `InStock` or `LimitedAvailability` |
| Out of stock, can't be ordered | Sold out | `OutOfStock` |
| Out of stock, still orderable | Back order: ships in 2 weeks | `BackOrder` |
| Not released yet | Pre-order: ships on a date | `PreOrder` |
| Won't come back | No longer available | `Discontinued` |

## Mark up variants as a ProductGroup

Google's variant markup groups variants under a `ProductGroup`. `variesBy` names the dimensions (Google supports colour, size, suggested age, suggested gender, material and pattern), `hasVariant` lists one `Product` per variant with its own `sku` or `gtin`, and `productGroupID` is the parent SKU. Each variant's offer carries its own price, URL and availability.

### 1. On Shopify, write your own ProductGroup

The theme's `{{ product | structured_data }}` leaves out `variesBy`, colour and size. Replace that line in the product section with your own JSON-LD, so the page doesn't carry two product groups. This version maps the first option to colour and the second to size; change it to match your options. It marks variants that keep selling at zero stock as `BackOrder`.

**Liquid: sections/main-product.liquid (Dawn) or your product section**

```liquid
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "ProductGroup",
  "name": {{ product.title | json }},
  "productGroupID": {{ product.id | json }},
  "url": {{ product.url | prepend: request.origin | json }},
  "variesBy": ["https://schema.org/color", "https://schema.org/size"],
  "hasVariant": [
    {%- for variant in product.variants -%}
    {
      "@type": "Product",
      "name": {{ product.title | append: " - " | append: variant.title | json }},
      "sku": {{ variant.sku | json }},
      "color": {{ variant.options[0].name | json }},
      "size": {{ variant.options[1].name | json }},
      "offers": {
        "@type": "Offer",
        "url": {{ variant.url | prepend: request.origin | json }},
        "price": {{ variant.price | divided_by: 100.0 | json }},
        "priceCurrency": {{ cart.currency.iso_code | json }},
        "availability": "https://schema.org/{% if variant.available %}{% if variant.inventory_policy == 'continue' and variant.inventory_quantity <= 0 %}BackOrder{% else %}InStock{% endif %}{% else %}OutOfStock{% endif %}"
      }
    }{% unless forloop.last %},{% endunless %}
    {%- endfor -%}
  ]
}
</script>
```

### 2. On WooCommerce, check what core outputs

Core marks up a variable product as one `AggregateOffer` with a low and a high price. A URL that selects one variation gets that variation's offer with `inProductGroupWithID`. For full ProductGroup markup you need an SEO plugin that supports it or the `woocommerce_structured_data_product` filter.

### 3. Test a variant URL

Run a product URL and one `?variant=` URL through Google's Rich Results Test, and check that each variant's price and availability match the page.

## Check what the store exposes

Both platforms publish product data you can read without logging in. Compare it with what the page shows: if the data says a size is available and the page doesn't say so in words, an agent reading the page misses it.

**Terminal: Shopify product JSON and the WooCommerce Store API**

```bash
# Shopify: variants with price (in cents) and available
curl -s https://yourstore.com/products/your-handle.js

# WooCommerce: public, unauthenticated Store API
curl -s 'https://yourstore.com/wp-json/wc/store/v1/products?stock_status=outofstock'
```

Checking stock and comparing two products are key shopping tasks in Aiscovery's AI agent test runs. Named AI agents try them on your store in a read-only browser, and the step screenshots show which option they picked and what they read. See [how a test run works](https://aiscovery.io/product/agent-readiness-audit).

## Keep going

- [Delivery prices](https://aiscovery.io/resources/why-ai-agents-cant-find-delivery-price): The other fact agents fail on before checkout.
- [Aiscovery on Shopify](https://aiscovery.io/platforms/shopify): What the site check and test runs look for on Shopify stores.
- [Aiscovery on WooCommerce](https://aiscovery.io/platforms/woocommerce): What commonly breaks for AI agents on WooCommerce stores.
- [Request a demo](https://aiscovery.io/demo): Watch named AI agents check stock and compare products on your store.

## Questions store owners ask

**Q: Do AI agents read structured data or the visible page?**

A: It depends on the tool. Agents that browse read the visible page, its text and its accessibility tree; search systems that index your store can also read structured data. Make both say the same thing.

**Q: Shopify already outputs a ProductGroup. Why change it?**

A: The structured_data filter lists the variants, but on the demo stores we checked it has no variesBy, colour or size, which Google uses to tell variants apart.

**Q: What availability should I use for Continue selling when out of stock?**

A: BackOrder, if the variant is at zero or below and still orderable, with the expected ship time on the page. Shopify only offers that setting when inventory is tracked.

**Q: Should each variant have its own page?**

A: No. Google supports one page for the group, as long as each variant can be preselected with a distinct URL, such as Shopify's ?variant= parameter.

**Q: How many variants can an agent see in Shopify's product JSON?**

A: Shopify's Ajax product JSON contains up to 250 variants in its variants array.

## Sources

Every technical claim on this page comes from the vendor's own documentation, checked on the date shown as Updated. Crawler names and settings change, so check the source before you edit a live store.

1. [Product variant structured data (ProductGroup, Product)](https://developers.google.com/search/docs/appearance/structured-data/product-variants) (Google Search Central)
2. [Merchant listing (Product, Offer) structured data](https://developers.google.com/search/docs/appearance/structured-data/merchant-listing) (Google Search Central)
3. [structured_data filter](https://shopify.dev/docs/api/liquid/filters/structured_data) (Shopify.dev)
4. [Product API reference (Ajax API)](https://shopify.dev/docs/api/ajax/reference/product) (Shopify.dev)
5. [variant (Liquid object)](https://shopify.dev/docs/api/liquid/objects/variant) (Shopify.dev)
6. [Selling out of stock products](https://help.shopify.com/en/manual/products/inventory/getting-started-with-inventory/selling-when-out-of-stock) (Shopify Help Center)
7. [class-wc-structured-data.php](https://github.com/woocommerce/woocommerce/blob/trunk/plugins/woocommerce/includes/class-wc-structured-data.php) (WooCommerce on GitHub)
8. [Store API](https://developer.woocommerce.com/docs/apis/store-api/) (WooCommerce Developer Docs)
9. [Store API: products](https://developer.woocommerce.com/docs/apis/store-api/resources-endpoints/products/) (WooCommerce Developer Docs)
10. [Computer use tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/computer-use-tool) (Claude Developer Platform)

## More guides

- [How do I stop pop-ups and cookie banners from blocking AI agents?](https://aiscovery.io/resources/stop-popups-blocking-ai-agents) (Page basics)
- [How do I allow ChatGPT and other AI agents on my Shopify store?](https://aiscovery.io/resources/allow-ai-agents-shopify-robots-txt) (Shopify)
- [How do I check which AI crawlers my store blocks?](https://aiscovery.io/resources/check-which-ai-crawlers-your-store-blocks) (AI crawlers)

## See what AI agents do in your store.

A short call: we run the site check on your store, show what robots.txt tells each AI crawler, and walk through a test run where named AI agents try key shopping tasks.

[Request a demo](https://aiscovery.io/demo)

Have a real workflow to test now? [Apply as a launch partner](https://aiscovery.io/partners)

---

This is the Markdown version of the page for AI agents. The HTML page at the canonical URL is authoritative. Site index: https://aiscovery.io/llms.txt
