AI crawlers read your HTML, not your JavaScript
Before you measure whether an AI assistant cites you, check whether it can read you at all. The test is one curl command, and the result is often uncomfortable.
- ai-visibility
- crawling
- geo
Shopping
Matching offers across retailers is an identifier problem before it is a model problem. Blocking, attribute normalisation, and why four verdicts beat true/false.
Two listings. Sony WH-1000XM5 Wireless Noise Cancelling Headphones - Black and SONY WH1000XM5B Over-Ear Bluetooth Headphone Black + Carry Case. Same product?
The first is. The second is a bundle, and if your repricer treats it as the same product you will undercut yourself against an offer that includes an accessory you are not shipping. Multiply by a catalogue of fifty thousand SKUs across six retailers and this stops being a string-similarity exercise and becomes the thing that decides whether your pricing is right.
Product matching — entity resolution for commerce — is where a lot of teams reach for an embedding model first. That is the wrong first move. The right order is identifiers, then blocking, then attributes, and only then a model for what is left.
Before any similarity computation, try to match on identifiers:
An exact GTIN match resolves without a model, deterministically and reproducibly. That is worth engineering effort to preserve: carry identifiers through your whole pipeline rather than discarding them at import because a few rows were empty.
Where identifiers let you down, and it is worth knowing before you trust them:
So: trust identifiers first, verify them, and never let an identifier match override a flat contradiction in the attributes.
Comparing every product against every candidate is quadratic and dies at real catalogue sizes. The standard fix is blocking — cheaply partition into buckets that plausibly contain matches, then do expensive comparison only within a bucket.
Practical blocking keys, usually combined:
SONY, Sony, sony corp collapse to one key.WH-1000XM5 and WH1000XM5B share a normalised token that is far more discriminative than any other word in either string.Blocking sets the ceiling on recall: a true match in no shared block can never be found downstream. If matches are missing, look at blocking before you look at the scorer. The instinct to tune the comparison step is usually misdirected effort.
same from variant #This is the step that separates a matcher that works from one that looks like it works.
Extract and normalise the attributes that define a configuration — colour, capacity, size, model year, connectivity, region — and compare them explicitly. The hard part is not comparison, it is normalisation: 256GB, 256 GB, 256gb and 0.25TB are one value; Black, Midnight Black and BLK usually are; Graphite may or may not be.
Two rules that prevent most damage:
same.match / no match cannot express what is actually on the shelf. The taxonomy that survives contact with real listings:
| verdict | meaning | why it must be separate |
|---|---|---|
same | identical product configuration | the only one safe to reprice against |
variant | same model, different configuration | related, comparable, not interchangeable |
bundle | your product plus something else | the price includes goods you are not selling |
different | a different product | may still be a competitor, is not a comparison |
bundle is the one people leave out and the one that causes the expensive mistakes. A bundle looks extremely similar to the base product on every text signal — same brand, same model number, nearly the same title — and its price is higher for a legitimate reason. Fold it into same and your repricer reads a phantom price gap and chases it.
Every verdict should carry a confidence and a line of evidence. Without evidence, a wrong match is unfixable: you cannot tell whether the extractor, the normaliser or the scorer was at fault.
POST /v1/commerce/match finds candidate offers across shopping and marketplace surfaces and returns a verdict per candidate:
import { Dataswap } from 'dataswap';
const dataswap = new Dataswap({ apiKey: process.env.DATASWAP_API_KEY });
const m = await dataswap.commerce.match({
product: {
title: 'Sony WH-1000XM5 Wireless Headphones',
brand: 'Sony',
gtin: '4548736132115',
attrs: { color: 'black' },
},
targets: ['shopping', 'amazon'],
limit: 20,
gl: 'us',
});
// Only `same` is safe to price against.
const comparable = m.matches.filter((c) => c.verdict === 'same' && c.confidence >= 0.8);
for (const c of comparable) {
console.log(c.seller, c.price, c.currency, '—', c.evidence);
}import os
from dataswap import Dataswap
client = Dataswap(api_key=os.environ["DATASWAP_API_KEY"])
m = client.commerce_match(
{"title": "Sony WH-1000XM5 Wireless Headphones", "brand": "Sony", "gtin": "4548736132115"},
targets=["shopping", "amazon"],
limit=20,
)
same = [c for c in m["matches"] if c["verdict"] == "same"]Each result carries source, title, brand, gtin, price, currency, url, seller, asin, rating, reviews_count, plus verdict, confidence (0–1) and evidence. Candidates that match on identifier are resolved without a model — which is the reason to send gtin whenever you have one.
Expect this call to take a while — it fans out across several surfaces before reasoning over them. If the API hands the work off with a 202, both SDKs wait for the job and return the result, so nothing changes in your code; just do not set an aggressive per-request timeout.
For the category-level view rather than the product-level one, POST /v1/commerce/shelf measures where a brand appears across a set of category keywords, how its prices sit against the shelf, and which products the AI Overview cites:
const shelf = await dataswap.commerce.shelf({
brand: 'Acme',
keywords: ['noise cancelling headphones', 'wireless over-ear headphones'],
competitors: ['Sony', 'Bose'],
gl: 'us',
});
console.log(shelf.shelf_share_pct, shelf.ai_citation_rate);
console.log('failed surfaces:', shelf.coverage.failed_surfaces);A matcher without a labelled evaluation set is a matcher whose accuracy nobody knows. Build one — a few hundred hand-labelled pairs covering the hard cases, not the easy ones — and measure precision and recall separately.
For repricing, precision dominates. A false same changes a price against a product that is not yours; a false different merely leaves a competitor unobserved. Those costs are not symmetric, so do not optimise a single blended score. Set the confidence threshold where precision is acceptable, and route everything below it to human review rather than guessing.
Sample the reviewed cases back into the evaluation set. Matching quality degrades as catalogues and listing conventions drift, and the only way to notice is to keep measuring.
Condition is not in the title, and it changes everything. New, refurbished, open-box and used listings can be textually identical. If your source does not expose condition explicitly, you cannot safely reprice against marketplace offers.
Regional variants look identical and are not. Different plugs, voltages, warranties, frequency bands. Same model number, same title, genuinely different products for a buyer.
Marketplace sellers are not retailers. An offer from a third-party seller on a marketplace competes differently from the marketplace's own offer. seller is there to be used.
Prices carry conditions the number omits. Shipping, tax treatment, membership pricing and promotional bundling all sit outside the price field. Comparing bare numbers across surfaces compares things that are not comparable.
A null metric is not a zero. On the shelf endpoint, null means a surface failed and that keyword left the denominator. Read coverage before quoting any percentage — a share over eight keywords is not comparable with a share over ten. Metrics are computed in code; only the written reading of them comes from a model.
Bundles are adversarial. Marketplaces reward listings that look like the base product. Expect the hardest cases to be the ones deliberately constructed to be hard.
Legal note on repricing. Automated price coordination between competitors is a competition-law problem in many jurisdictions, and "the algorithm did it" is not a defence. Observing public prices is ordinary; coordinating on them is not. If your pricing logic reacts to named competitors, that is a conversation for your legal team.
Identifiers first — carry and validate them. Block before you compare, and fix recall problems in the blocking step. Normalise the attributes that define a configuration, and treat a contradiction as decisive and a gap as unknown. Emit four verdicts with confidence and evidence, never a boolean. Then build an evaluation set and tune for precision, because the errors are not symmetric.
Route schemas are in the API reference, the docs cover the credit envelope, idempotency and how long-running calls hand off, and digital shelf monitoring is the product page for the category-level view.
Before you measure whether an AI assistant cites you, check whether it can read you at all. The test is one curl command, and the result is often uncomfortable.
Search results are the wrong shape for a prompt. How to turn a SERP into grounding context an agent can use: dedupe, rerank, budget tokens, keep citations.