Skip to main content
Glama

WEM Price Compare

Search products across retailers

search_products
Read-only

Searches WEM's catalogue and its connected retailer sources for products matching a query. The query can be a product name or an identifier: a barcode (EAN/UPC/GTIN), an Amazon ASIN, an MPN, a merchant SKU that names one catalogue product, a WEM ID or a wem3.ai/pl/{slug} URL, and several identifiers may be comma-separated. When the query identifies a product WEM holds, a catalogMatch block (catalogMatches for several) gives that product's retailer offers; a barcode, ASIN, WEM ID or URL match skips the live retailer search, so products then holds catalogue offers only. Barcode, ASIN and slug matches are identity-resolved; MPN, SKU and title matches are inferred. Accessories, bundles, other models and listings priced far below the product's other offers are withheld and counted by reason in filtered.

Input Schema

NameRequiredDescriptionDefault
gtinNoA product barcode (EAN/UPC/GTIN) the host already has. When given, retailer sources are searched by this barcode instead of by the query words.
limitNoHow many listings to request from each retailer source (default 10, max 30). The combined list can hold more.
queryYesA product name, or an identifier: barcode (EAN/UPC/GTIN), ASIN, MPN, merchant SKU, WEM ID or wem3.ai/pl URL. Several identifiers may be comma-separated.
sort_byNoOrder of the live retailer listings (default relevance). Not applied when the query identifies a product.
categoryNoOptional. Keeps only live retailer listings whose own category name matches this text exactly; catalogue matches ignore it. Leave it out unless the shopper names a retailer's category.
currencyNoISO 4217 code for the shopper's market. Default GBP. Results and price filters use this currency.
max_priceNoHighest price to include, in `currency`. Applies to retailer listings, not to `catalogMatch`.
min_priceNoLowest price to include, in `currency`. Applies to retailer listings, not to `catalogMatch`.
providersNoRetailer sources to search live, e.g. ["amazon", "awin"]. Omit for all. Catalogue matches are not limited by it.

Output Schema

NameRequiredDescriptionDefault
nextNoPresent when WEM's set of offers is thin. chrome_extension is a page that compares the same product on any retailer page, including shops without an affiliate programme.
linksNoThe prices in this result as lines, cheapest first. Give these to the user when you name a price from this result. Each line opens the retailer page for that price; a retailer URL found elsewhere may be a different listing or price.
queryYes
searchNoPresent when this result names no offer. `url` is WEM's results page for the query and `markdown` is the same link, pre-formatted. WEM searches retailers live on that page, including shops it holds no affiliate programme with, so an empty or withheld result here is not evidence the product is unavailable or unpriced.
partialNoTrue when the live retailer fan-out hit its 3-second budget and some providers had not answered. The rows returned are real; the market may be wider. Ask again for the rest.
coverageNoPresent when surviving rows include a marketplace cluster at similar prices. That is not a retail floor — the cheapest marketplace listing is not the deal. The listings stay in the payload, each with its url. `next`, when present, is the Chrome extension page for shops WEM does not yet hold as partners.
degradedNoPresent when this call hit its time budget and skipped enrichment. The offers returned are COMPLETE — only the extras were dropped. A missing catalogue block or missing observed retailers does not mean WEM holds nothing, and no retry is needed.
filteredNoWithheld candidates tallied by reason (e.g. accessories, another model, a bundle or multipack, a replica, a listing priced far below the product’s other offers). Report this count rather than implying the search was exhaustive, and never quote a withheld listing’s price as the cheapest or as a deal.
productsYes
unpricedNoListings whose price WEM may not show — today, Amazon rows Amazon’s API did not price just now. The row carries `priceNote` (e.g. "See price at Amazon") in place of a figure. No price is stated, estimated or compared for one; it is not the cheapest and is not counted in a lowest price or a saving.
disclosureYesWEM’s affiliate disclosure for this answer, written to be quoted as it is. Its wording changes with the rows: it states whether every outbound link is affiliate-tracked, only some are, or none are. A version from an earlier answer may not fit this one: a row marked `affiliate: false` earns WEM nothing.
sizeSpreadNoPresent when the returned rows are different sizes and the query named none, so price ordering is meaningless: the cheapest row is cheapest because it is less of the product. Do NOT name a cheapest, a lowest price, or a best deal across these rows. The size belongs beside every price. Where rows carry `unitPrice`, that is the like-for-like figure, and it is the figure to rank on when the question is which is better value. The rows are real listings, each with its url.
catalogMatchNoOffers WEM holds under one catalogue product. Check `identityBasis`: on `barcode` the product carries a GTIN and every offer here is that same item, so prefer them and cite their prices over anything in `products`. On `curated-grouping` the product carries no barcode, the grouping is an inference like any title match, and a cheaper row in `products` may well be the same item — see `cheaperElsewhere`. Whether the shopper’s words reached this product by barcode or by name is `identity`, a separate question: on `inferred`, say WEM matched it by name.
totalResultsYesCount of products returned, after weak matches were withheld.
catalogMatchesNoPresent when the query was a comma-separated batch of identifiers and more than one catalogue product resolved. Each entry has the same shape as `catalogMatch`. Prefer barcode-basis blocks; treat MPN/SKU/title blocks as inferred.
pending_providersNoProviders still running when the budget expired. Present only with partial.

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed2 schema fields changed
    • changedOutput schema / properties / catalogMatch / properties / lowPrice / description
      Previous value: -"Lowest CURRENT price in `offers` (priceStatus `current`). Null when none is current: never fill it from an older or undated row."New value: +"Lowest CURRENT price in `offers` (priceStatus `current`) from a shop not out of stock when last checked. Null when there is none: never fill it from an older or undated row or a row with `inStock: false`."
    • changedOutput schema / properties / catalogMatches / items / properties / lowPrice / description
      Previous value: -"Lowest CURRENT price in `offers` (priceStatus `current`). Null when none is current: never fill it from an older or undated row."New value: +"Lowest CURRENT price in `offers` (priceStatus `current`) from a shop not out of stock when last checked. Null when there is none: never fill it from an older or undated row or a row with `inStock: false`."
  2. Changed4 schema fields changed
    • changedOutput schema / properties / catalogMatch / properties / offers / items / properties / url / description
      Previous value: -"WEM tracked link to the retailer page for this offer. WEM never takes payment."New value: +"The page this price was read on. Give this url to the user when you name the price; a different url can be a different listing or price. The full URL; a shortened form is a different link. WEM never takes payment."
    • changedOutput schema / properties / catalogMatches / items / properties / offers / items / properties / url / description
      Previous value: -"WEM tracked link to the retailer page for this offer. WEM never takes payment."New value: +"The page this price was read on. Give this url to the user when you name the price; a different url can be a different listing or price. The full URL; a shortened form is a different link. WEM never takes payment."
    • addedOutput schema / properties / links
      Added value: +{
      +  "description": "The prices in this result as lines, cheapest first. Give these to the user when you name a price from this result. Each line opens the retailer page for that price; a retailer URL found elsewhere may be a different listing or price.",
      +  "items": {
      +    "properties": {
      +      "currency": {
      +        "type": [
      +          "string",
      +          "null"
      +        ]
      +      },
      +      "markdown": {
      +        "description": "The same link as `[Retailer — £0.00](url)`, for hosts rendering markdown.",
      +        "type": "string"
      +      },
      +      "price": {
      +        "description": "Null when the row has no figure. The markdown then says where to see the price.",
      +        "type": [
      +          "number",
      +          "null"
      +        ]
      +      },
      +      "retailer": {
      +        "type": "string"
      +      },
      +      "url": {
      +        "description": "The page this price was read on. The full URL; a shortened or rewritten form is a different link.",
      +        "type": "string"
      +      }
      +    },
      +    "type": "object"
      +  },
      +  "type": "array"
      +}
    • changedOutput schema / properties / products / items / properties / url / description
      Previous value: -"WEM tracked link to the retailer page for this offer. WEM never takes payment."New value: +"The page this price was read on. Give this url to the user when you name the price; a different url can be a different listing or price. The full URL; a shortened form is a different link. WEM never takes payment."
  3. Changed1 schema field changed
    • changedOutput schema / properties / coverage / properties / provider / description
      Previous value: -"aliexpress, or marketplace when mixed."New value: +"The marketplace (e.g. aliexpress), or marketplace when mixed."
  4. Changed8 schema fields changed
    • changedInput schema / properties / providers / description
      Previous value: -"Retailer sources to search live, e.g. [\"ebay\", \"awin\"]. Omit for all. Catalogue matches are not limited by it."New value: +"Retailer sources to search live, e.g. [\"amazon\", \"awin\"]. Omit for all. Catalogue matches are not limited by it."
    • changedOutput schema / properties / catalogMatch / properties / offers / items / properties / channel / description
      Previous value: -"retailer is a named shop (Boots, Currys). marketplace is eBay/AliExpress/Temu-style parallel listings. A marketplace cluster is not a set of competing authorised retailers."New value: +"retailer is a named shop (Boots, Currys). marketplace is AliExpress/Temu-style parallel listings. A marketplace cluster is not a set of competing authorised retailers."
    • changedOutput schema / properties / catalogMatch / properties / offers / items / properties / provider / description
      Previous value: -"Retailer slug, e.g. \"ebay\", \"currys\"."New value: +"Retailer slug, e.g. \"amazon\", \"currys\"."
    • changedOutput schema / properties / catalogMatches / items / properties / offers / items / properties / channel / description
      Previous value: -"retailer is a named shop (Boots, Currys). marketplace is eBay/AliExpress/Temu-style parallel listings. A marketplace cluster is not a set of competing authorised retailers."New value: +"retailer is a named shop (Boots, Currys). marketplace is AliExpress/Temu-style parallel listings. A marketplace cluster is not a set of competing authorised retailers."
    • changedOutput schema / properties / catalogMatches / items / properties / offers / items / properties / provider / description
      Previous value: -"Retailer slug, e.g. \"ebay\", \"currys\"."New value: +"Retailer slug, e.g. \"amazon\", \"currys\"."
    • changedOutput schema / properties / coverage / properties / provider / description
      Previous value: -"ebay, aliexpress, or marketplace when mixed."New value: +"aliexpress, or marketplace when mixed."
    • changedOutput schema / properties / products / items / properties / channel / description
      Previous value: -"retailer is a named shop (Boots, Currys). marketplace is eBay/AliExpress/Temu-style parallel listings. A marketplace cluster is not a set of competing authorised retailers."New value: +"retailer is a named shop (Boots, Currys). marketplace is AliExpress/Temu-style parallel listings. A marketplace cluster is not a set of competing authorised retailers."
    • changedOutput schema / properties / products / items / properties / provider / description
      Previous value: -"Retailer slug, e.g. \"ebay\", \"currys\"."New value: +"Retailer slug, e.g. \"amazon\", \"currys\"."
  5. Changed3 schema fields changed
    • addedOutput schema / properties / catalogMatch / properties / offers / items / properties / shipping / description
      Added value: +"Delivery as the retailer stated it: `free`, or `cost` in the offer currency. Absent or null means not stated: say delivery is shown at the shop, never that it is free. Offers are ordered by item price; add a stated cost before calling one cheaper delivered."
    • addedOutput schema / properties / catalogMatches / items / properties / offers / items / properties / shipping / description
      Added value: +"Delivery as the retailer stated it: `free`, or `cost` in the offer currency. Absent or null means not stated: say delivery is shown at the shop, never that it is free. Offers are ordered by item price; add a stated cost before calling one cheaper delivered."
    • addedOutput schema / properties / products / items / properties / shipping / description
      Added value: +"Delivery as the retailer stated it: `free`, or `cost` in the offer currency. Absent or null means not stated: say delivery is shown at the shop, never that it is free. Offers are ordered by item price; add a stated cost before calling one cheaper delivered."
  6. Changed56 schema fields changed
    • changedInput schema / properties / category / description
      Previous value: -"Filter by category (electronics, fashion, beauty, home, sports, collectibles)"New value: +"Optional. Keeps only live retailer listings whose own category name matches this text exactly; catalogue matches ignore it. Leave it out unless the shopper names a retailer's category."
    • addedInput schema / properties / currency
      Added value: +{
      +  "description": "ISO 4217 code for the shopper's market. Default GBP. Results and price filters use this currency.",
      +  "type": "string"
      +}
    • changedInput schema / properties / gtin / description
      Previous value: -"Product barcode (EAN/UPC/GTIN) when the host already has one. Feed rows are matched on this before title."New value: +"A product barcode (EAN/UPC/GTIN) the host already has. When given, retailer sources are searched by this barcode instead of by the query words."
    • changedInput schema / properties / limit / description
      Previous value: -"Max results to return (default 10, max 30)"New value: +"How many listings to request from each retailer source (default 10, max 30). The combined list can hold more."
    • changedInput schema / properties / max_price / description
      Previous value: -"Maximum price filter (GBP)"New value: +"Highest price to include, in `currency`. Applies to retailer listings, not to `catalogMatch`."
    • changedInput schema / properties / min_price / description
      Previous value: -"Minimum price filter (GBP)"New value: +"Lowest price to include, in `currency`. Applies to retailer listings, not to `catalogMatch`."
    • changedInput schema / properties / providers / description
      Previous value: -"Limit to specific providers (e.g. [\"ebay\", \"awin\"]). Omit for all."New value: +"Retailer sources to search live, e.g. [\"ebay\", \"awin\"]. Omit for all. Catalogue matches are not limited by it."
    • changedInput schema / properties / query / description
      Previous value: -"Product name, barcode (EAN/UPC/GTIN), ASIN, MPN, merchant SKU, WEM ID, wem3.ai/pl URL, or comma-separated IDs of those kinds."New value: +"A product name, or an identifier: barcode (EAN/UPC/GTIN), ASIN, MPN, merchant SKU, WEM ID or wem3.ai/pl URL. Several identifiers may be comma-separated."
    • changedInput schema / properties / sort_by / description
      Previous value: -"Sort order for results"New value: +"Order of the live retailer listings (default relevance). Not applied when the query identifies a product."
    • changedOutput schema / properties / catalogMatch / properties / identity / description
      Previous value: -"How the shopper’s words reached this product. Decides how you describe the match: on `inferred`, say WEM matched it by name (not by barcode) and do not call the identity confirmed, even when `identityBasis` is `barcode`; on `exact`, the barcode itself resolved it."New value: +"How the shopper’s words reached this product. On `inferred`, WEM matched it by name (not by barcode) and the identity is not confirmed, even when `identityBasis` is `barcode`; on `exact`, the barcode itself resolved it."
    • changedOutput schema / properties / catalogMatch / properties / offers / items / properties / affiliate / description
      Previous value: -"False when this link earns WEM nothing: the shop has no affiliate programme with WEM, or this link does not carry one. Still a real offer: list it with the others, and never call its link affiliate-tracked. Absent (or true) means the link is affiliate-tracked. `disclosure` already states whether all, some or none of the links are tracked; relay that rather than your own wording."New value: +"False when this link earns WEM nothing: the shop has no affiliate programme with WEM, or this link does not carry one. It is still a real offer, and its link is not affiliate-tracked. Absent (or true) means the link is affiliate-tracked. `disclosure` states whether all, some or none of the links are tracked."
    • changedOutput schema / properties / catalogMatch / properties / offers / items / properties / channel / description
      Previous value: -"retailer is a named shop (Boots, Currys). marketplace is eBay/AliExpress/Temu-style parallel listings. Do not present a marketplace cluster as competing authorised retailers."New value: +"retailer is a named shop (Boots, Currys). marketplace is eBay/AliExpress/Temu-style parallel listings. A marketplace cluster is not a set of competing authorised retailers."
    • changedOutput schema / properties / catalogMatch / properties / offers / items / properties / lastSeenAt / description
      Previous value: -"When WEM last READ this price from the retailer or a datafeed — the same reading `priceAgeDays` counts from, so the two always agree. Null means WEM cannot date the price: say it is undated, never that it is current."New value: +"When WEM last READ this price from the retailer or a datafeed — the same reading `priceAgeDays` counts from, so the two always agree. Null means WEM cannot date the price: it is undated, not current."
    • changedOutput schema / properties / catalogMatch / properties / offers / items / properties / price / description
      Previous value: -"Indicative price. The retailer sets the final price at checkout. Null, with `priceNote`, when WEM may not show this listing’s price: show the note and the link, never a figure of your own."New value: +"Indicative price. The retailer sets the final price at checkout. Null, with `priceNote`, when WEM may not show this listing’s price: the note stands in place of a figure."
    • changedOutput schema / properties / catalogMatch / properties / offers / items / properties / priceAgeDays / description
      Previous value: -"Calendar days (UK time) since WEM last READ this price: 0 read today, 1 yesterday. Null means WEM cannot say. Report null as undated; never present it as current."New value: +"Calendar days (UK time) since WEM last READ this price: 0 read today, 1 yesterday. Null means WEM cannot say. Null is undated, not current."
    • changedOutput schema / properties / catalogMatch / properties / offers / items / properties / priceNote / description
      Previous value: -"Present only when `price` is null: the words to show in its place, e.g. \"See price at Amazon\". An Amazon price is shown only when Amazon’s API supplied it just now."New value: +"Present only when `price` is null: the words that stand in its place, e.g. \"See price at Amazon\". An Amazon price is shown only when Amazon’s API supplied it just now."
    • changedOutput schema / properties / catalogMatch / properties / offers / items / properties / priceQualifier / description
      Previous value: -"Present when `price` is the OPENING price of a range, not the price of this item: a multi-variation listing where the seller advertises its cheapest variant and the shopper picks a size on the page. Quote it as \"from £X\", never as the price or the cheapest. Absent means the price is firm for the row as described."New value: +"Present when `price` is the OPENING price of a range, not the price of this item: a multi-variation listing where the seller advertises its cheapest variant and the shopper picks a size on the page. It reads as \"from £X\"; it is not the item's price or the cheapest. Absent means the price is firm for the row as described."
    • changedOutput schema / properties / catalogMatch / properties / offers / items / properties / priceRefresh / description
      Previous value: -"What re-reads this retailer’s prices: `live-api` a product-lookup API, `feed` a partner datafeed, `none` nothing on a schedule. `none` means the figure will not move on its own however long it sits — say so rather than quoting it flat, and date it with `priceAgeDays`."New value: +"What re-reads this retailer’s prices: `live-api` a product-lookup API, `feed` a partner datafeed, `none` nothing on a schedule. `none` means the figure will not move on its own however long it sits ; `priceAgeDays` dates it."
    • changedOutput schema / properties / catalogMatch / properties / offers / items / properties / priceStatus / description
      Previous value: -"`current`: WEM read this price within the last 14 days; only a current price may lead, be called the cheapest or lowest, be compared or enter a saving. `stale`: read longer ago. `undated`: WEM cannot say when. Show those two as \"last seen £X on 8 Sep\" or \"price not dated\", after the current ones, and never compare them. Absent on a row a provider answered just now."New value: +"`current`: WEM read this price within the last 14 days; only a current price may lead, be called the cheapest or lowest, be compared or enter a saving. `stale`: read longer ago. `undated`: WEM cannot say when. Those two read as \"last seen £X on 8 Sep\" or \"price not dated\", come after the current ones, and are not compared. Absent on a row a provider answered just now."
    • changedOutput schema / properties / catalogMatch / properties / offers / items / properties / unitPrice / description
      Previous value: -"Price per 100ml, 100g, litre, kg or item, from the size this row's title states. Present only when the rows differ in size, in one unit across all of them, and only for things bought by the amount (beauty, food, supplements), never an appliance's capacity. When comparing value across sizes, compare these, never the prices: a smaller size is not cheaper because its price is lower. Quote it beside the price (\"£29.95, £99.83 per 100ml\"). Absent does not mean the price is per unit."New value: +"Price per 100ml, 100g, litre, kg or item, from the size this row's title states. Present only when the rows differ in size, in one unit across all of them, and only for things bought by the amount (beauty, food, supplements), never an appliance's capacity. Across sizes, value is compared on these, not on the prices: a smaller size is not cheaper because its price is lower. It reads beside the price (\"£29.95, £99.83 per 100ml\"). Absent does not mean the price is per unit."
    • changedOutput schema / properties / catalogMatch / properties / offers / items / properties / url / description
      Previous value: -"WEM tracked link to the retailer. Send the user here — WEM never takes payment."New value: +"WEM tracked link to the retailer page for this offer. WEM never takes payment."
    • changedOutput schema / properties / catalogMatch / properties / unpriced / description
      Previous value: -"Listings whose price WEM may not show — today, Amazon rows Amazon’s API did not price just now. Give the link with `priceNote` (\"See price at Amazon\"). Never state, estimate or compare a price for one, never call it the cheapest, and never count it in a lowest price or a saving."New value: +"Listings whose price WEM may not show — today, Amazon rows Amazon’s API did not price just now. The row carries `priceNote` (e.g. \"See price at Amazon\") in place of a figure. No price is stated, estimated or compared for one; it is not the cheapest and is not counted in a lowest price or a saving."
    • changedOutput schema / properties / catalogMatch / properties / unpriced / items / properties / priceNote / description
      Previous value: -"What to show where a price would be, e.g. \"See price at Amazon\"."New value: +"What stands where a price would be, e.g. \"See price at Amazon\"."
    • changedOutput schema / properties / catalogMatch / properties / unpriced / items / properties / url / description
      Previous value: -"WEM tracked link. Relay it exactly."New value: +"WEM tracked link. The full URL; a shortened form is a different link."
    • changedOutput schema / properties / catalogMatches / items / properties / identity / description
      Previous value: -"How the shopper’s words reached this product. Decides how you describe the match: on `inferred`, say WEM matched it by name (not by barcode) and do not call the identity confirmed, even when `identityBasis` is `barcode`; on `exact`, the barcode itself resolved it."New value: +"How the shopper’s words reached this product. On `inferred`, WEM matched it by name (not by barcode) and the identity is not confirmed, even when `identityBasis` is `barcode`; on `exact`, the barcode itself resolved it."
    • changedOutput schema / properties / catalogMatches / items / properties / offers / items / properties / affiliate / description
      Previous value: -"False when this link earns WEM nothing: the shop has no affiliate programme with WEM, or this link does not carry one. Still a real offer: list it with the others, and never call its link affiliate-tracked. Absent (or true) means the link is affiliate-tracked. `disclosure` already states whether all, some or none of the links are tracked; relay that rather than your own wording."New value: +"False when this link earns WEM nothing: the shop has no affiliate programme with WEM, or this link does not carry one. It is still a real offer, and its link is not affiliate-tracked. Absent (or true) means the link is affiliate-tracked. `disclosure` states whether all, some or none of the links are tracked."
    • changedOutput schema / properties / catalogMatches / items / properties / offers / items / properties / channel / description
      Previous value: -"retailer is a named shop (Boots, Currys). marketplace is eBay/AliExpress/Temu-style parallel listings. Do not present a marketplace cluster as competing authorised retailers."New value: +"retailer is a named shop (Boots, Currys). marketplace is eBay/AliExpress/Temu-style parallel listings. A marketplace cluster is not a set of competing authorised retailers."
    • changedOutput schema / properties / catalogMatches / items / properties / offers / items / properties / lastSeenAt / description
      Previous value: -"When WEM last READ this price from the retailer or a datafeed — the same reading `priceAgeDays` counts from, so the two always agree. Null means WEM cannot date the price: say it is undated, never that it is current."New value: +"When WEM last READ this price from the retailer or a datafeed — the same reading `priceAgeDays` counts from, so the two always agree. Null means WEM cannot date the price: it is undated, not current."
    • changedOutput schema / properties / catalogMatches / items / properties / offers / items / properties / price / description
      Previous value: -"Indicative price. The retailer sets the final price at checkout. Null, with `priceNote`, when WEM may not show this listing’s price: show the note and the link, never a figure of your own."New value: +"Indicative price. The retailer sets the final price at checkout. Null, with `priceNote`, when WEM may not show this listing’s price: the note stands in place of a figure."
    • changedOutput schema / properties / catalogMatches / items / properties / offers / items / properties / priceAgeDays / description
      Previous value: -"Calendar days (UK time) since WEM last READ this price: 0 read today, 1 yesterday. Null means WEM cannot say. Report null as undated; never present it as current."New value: +"Calendar days (UK time) since WEM last READ this price: 0 read today, 1 yesterday. Null means WEM cannot say. Null is undated, not current."
    • changedOutput schema / properties / catalogMatches / items / properties / offers / items / properties / priceNote / description
      Previous value: -"Present only when `price` is null: the words to show in its place, e.g. \"See price at Amazon\". An Amazon price is shown only when Amazon’s API supplied it just now."New value: +"Present only when `price` is null: the words that stand in its place, e.g. \"See price at Amazon\". An Amazon price is shown only when Amazon’s API supplied it just now."
    • changedOutput schema / properties / catalogMatches / items / properties / offers / items / properties / priceQualifier / description
      Previous value: -"Present when `price` is the OPENING price of a range, not the price of this item: a multi-variation listing where the seller advertises its cheapest variant and the shopper picks a size on the page. Quote it as \"from £X\", never as the price or the cheapest. Absent means the price is firm for the row as described."New value: +"Present when `price` is the OPENING price of a range, not the price of this item: a multi-variation listing where the seller advertises its cheapest variant and the shopper picks a size on the page. It reads as \"from £X\"; it is not the item's price or the cheapest. Absent means the price is firm for the row as described."
    • changedOutput schema / properties / catalogMatches / items / properties / offers / items / properties / priceRefresh / description
      Previous value: -"What re-reads this retailer’s prices: `live-api` a product-lookup API, `feed` a partner datafeed, `none` nothing on a schedule. `none` means the figure will not move on its own however long it sits — say so rather than quoting it flat, and date it with `priceAgeDays`."New value: +"What re-reads this retailer’s prices: `live-api` a product-lookup API, `feed` a partner datafeed, `none` nothing on a schedule. `none` means the figure will not move on its own however long it sits ; `priceAgeDays` dates it."
    • changedOutput schema / properties / catalogMatches / items / properties / offers / items / properties / priceStatus / description
      Previous value: -"`current`: WEM read this price within the last 14 days; only a current price may lead, be called the cheapest or lowest, be compared or enter a saving. `stale`: read longer ago. `undated`: WEM cannot say when. Show those two as \"last seen £X on 8 Sep\" or \"price not dated\", after the current ones, and never compare them. Absent on a row a provider answered just now."New value: +"`current`: WEM read this price within the last 14 days; only a current price may lead, be called the cheapest or lowest, be compared or enter a saving. `stale`: read longer ago. `undated`: WEM cannot say when. Those two read as \"last seen £X on 8 Sep\" or \"price not dated\", come after the current ones, and are not compared. Absent on a row a provider answered just now."
    • changedOutput schema / properties / catalogMatches / items / properties / offers / items / properties / unitPrice / description
      Previous value: -"Price per 100ml, 100g, litre, kg or item, from the size this row's title states. Present only when the rows differ in size, in one unit across all of them, and only for things bought by the amount (beauty, food, supplements), never an appliance's capacity. When comparing value across sizes, compare these, never the prices: a smaller size is not cheaper because its price is lower. Quote it beside the price (\"£29.95, £99.83 per 100ml\"). Absent does not mean the price is per unit."New value: +"Price per 100ml, 100g, litre, kg or item, from the size this row's title states. Present only when the rows differ in size, in one unit across all of them, and only for things bought by the amount (beauty, food, supplements), never an appliance's capacity. Across sizes, value is compared on these, not on the prices: a smaller size is not cheaper because its price is lower. It reads beside the price (\"£29.95, £99.83 per 100ml\"). Absent does not mean the price is per unit."
    • changedOutput schema / properties / catalogMatches / items / properties / offers / items / properties / url / description
      Previous value: -"WEM tracked link to the retailer. Send the user here — WEM never takes payment."New value: +"WEM tracked link to the retailer page for this offer. WEM never takes payment."
    • changedOutput schema / properties / catalogMatches / items / properties / unpriced / description
      Previous value: -"Listings whose price WEM may not show — today, Amazon rows Amazon’s API did not price just now. Give the link with `priceNote` (\"See price at Amazon\"). Never state, estimate or compare a price for one, never call it the cheapest, and never count it in a lowest price or a saving."New value: +"Listings whose price WEM may not show — today, Amazon rows Amazon’s API did not price just now. The row carries `priceNote` (e.g. \"See price at Amazon\") in place of a figure. No price is stated, estimated or compared for one; it is not the cheapest and is not counted in a lowest price or a saving."
    • changedOutput schema / properties / catalogMatches / items / properties / unpriced / items / properties / priceNote / description
      Previous value: -"What to show where a price would be, e.g. \"See price at Amazon\"."New value: +"What stands where a price would be, e.g. \"See price at Amazon\"."
    • changedOutput schema / properties / catalogMatches / items / properties / unpriced / items / properties / url / description
      Previous value: -"WEM tracked link. Relay it exactly."New value: +"WEM tracked link. The full URL; a shortened form is a different link."
    • changedOutput schema / properties / coverage / description
      Previous value: -"Present when surviving rows include a marketplace cluster at similar prices. That is not a retail floor — do not name the cheapest marketplace listing as the deal. The listings are still in the payload; give the user those links. If `next` points at the Chrome extension, send the shopper there for shops WEM does not yet hold as partners."New value: +"Present when surviving rows include a marketplace cluster at similar prices. That is not a retail floor — the cheapest marketplace listing is not the deal. The listings stay in the payload, each with its url. `next`, when present, is the Chrome extension page for shops WEM does not yet hold as partners."
    • changedOutput schema / properties / degraded / description
      Previous value: -"Present when this call hit its time budget and skipped enrichment. The offers returned are COMPLETE — only the extras were dropped. Do not report a missing catalogue block or missing observed retailers as WEM holding nothing, and do not retry automatically."New value: +"Present when this call hit its time budget and skipped enrichment. The offers returned are COMPLETE — only the extras were dropped. A missing catalogue block or missing observed retailers does not mean WEM holds nothing, and no retry is needed."
    • changedOutput schema / properties / disclosure / description
      Previous value: -"Disclosure to relay once per answer, verbatim. Its wording changes with the rows: it states whether every outbound link is affiliate-tracked, only some are, or none are. Never substitute the version you saw last time — a row marked `affiliate: false` earns WEM nothing, and saying otherwise misdescribes it to the shopper."New value: +"WEM’s affiliate disclosure for this answer, written to be quoted as it is. Its wording changes with the rows: it states whether every outbound link is affiliate-tracked, only some are, or none are. A version from an earlier answer may not fit this one: a row marked `affiliate: false` earns WEM nothing."
    • changedOutput schema / properties / next / description
      Previous value: -"Where to send the shopper when WEM's set is thin. chrome_extension means compare the same product on any retailer page — WEM shows that price even without an affiliate programme."New value: +"Present when WEM's set of offers is thin. chrome_extension is a page that compares the same product on any retailer page, including shops without an affiliate programme."
    • changedOutput schema / properties / products / items / properties / affiliate / description
      Previous value: -"False when this link earns WEM nothing: the shop has no affiliate programme with WEM, or this link does not carry one. Still a real offer: list it with the others, and never call its link affiliate-tracked. Absent (or true) means the link is affiliate-tracked. `disclosure` already states whether all, some or none of the links are tracked; relay that rather than your own wording."New value: +"False when this link earns WEM nothing: the shop has no affiliate programme with WEM, or this link does not carry one. It is still a real offer, and its link is not affiliate-tracked. Absent (or true) means the link is affiliate-tracked. `disclosure` states whether all, some or none of the links are tracked."
    • changedOutput schema / properties / products / items / properties / channel / description
      Previous value: -"retailer is a named shop (Boots, Currys). marketplace is eBay/AliExpress/Temu-style parallel listings. Do not present a marketplace cluster as competing authorised retailers."New value: +"retailer is a named shop (Boots, Currys). marketplace is eBay/AliExpress/Temu-style parallel listings. A marketplace cluster is not a set of competing authorised retailers."
    • changedOutput schema / properties / products / items / properties / price / description
      Previous value: -"Indicative price. The retailer sets the final price at checkout. Null, with `priceNote`, when WEM may not show this listing’s price: show the note and the link, never a figure of your own."New value: +"Indicative price. The retailer sets the final price at checkout. Null, with `priceNote`, when WEM may not show this listing’s price: the note stands in place of a figure."
    • changedOutput schema / properties / products / items / properties / priceNote / description
      Previous value: -"Present only when `price` is null: the words to show in its place, e.g. \"See price at Amazon\". An Amazon price is shown only when Amazon’s API supplied it just now."New value: +"Present only when `price` is null: the words that stand in its place, e.g. \"See price at Amazon\". An Amazon price is shown only when Amazon’s API supplied it just now."
    • changedOutput schema / properties / products / items / properties / priceQualifier / description
      Previous value: -"Present when `price` is the OPENING price of a range, not the price of this item: a multi-variation listing where the seller advertises its cheapest variant and the shopper picks a size on the page. Quote it as \"from £X\", never as the price or the cheapest. Absent means the price is firm for the row as described."New value: +"Present when `price` is the OPENING price of a range, not the price of this item: a multi-variation listing where the seller advertises its cheapest variant and the shopper picks a size on the page. It reads as \"from £X\"; it is not the item's price or the cheapest. Absent means the price is firm for the row as described."
    • changedOutput schema / properties / products / items / properties / priceStatus / description
      Previous value: -"`current`: WEM read this price within the last 14 days; only a current price may lead, be called the cheapest or lowest, be compared or enter a saving. `stale`: read longer ago. `undated`: WEM cannot say when. Show those two as \"last seen £X on 8 Sep\" or \"price not dated\", after the current ones, and never compare them. Absent on a row a provider answered just now."New value: +"`current`: WEM read this price within the last 14 days; only a current price may lead, be called the cheapest or lowest, be compared or enter a saving. `stale`: read longer ago. `undated`: WEM cannot say when. Those two read as \"last seen £X on 8 Sep\" or \"price not dated\", come after the current ones, and are not compared. Absent on a row a provider answered just now."
    • changedOutput schema / properties / products / items / properties / unitPrice / description
      Previous value: -"Price per 100ml, 100g, litre, kg or item, from the size this row's title states. Present only when the rows differ in size, in one unit across all of them, and only for things bought by the amount (beauty, food, supplements), never an appliance's capacity. When comparing value across sizes, compare these, never the prices: a smaller size is not cheaper because its price is lower. Quote it beside the price (\"£29.95, £99.83 per 100ml\"). Absent does not mean the price is per unit."New value: +"Price per 100ml, 100g, litre, kg or item, from the size this row's title states. Present only when the rows differ in size, in one unit across all of them, and only for things bought by the amount (beauty, food, supplements), never an appliance's capacity. Across sizes, value is compared on these, not on the prices: a smaller size is not cheaper because its price is lower. It reads beside the price (\"£29.95, £99.83 per 100ml\"). Absent does not mean the price is per unit."
    • changedOutput schema / properties / products / items / properties / url / description
      Previous value: -"WEM tracked link to the retailer. Send the user here — WEM never takes payment."New value: +"WEM tracked link to the retailer page for this offer. WEM never takes payment."
    • changedOutput schema / properties / search / description
      Previous value: -"Present when this result names no offer. GIVE THE USER THIS LINK — it is the answer when WEM has nothing else to say, and `markdown` is ready to paste. WEM searches retailers live on that page, including shops it holds no affiliate programme with, so an empty or withheld result here is not evidence the product is unavailable or unpriced."New value: +"Present when this result names no offer. `url` is WEM's results page for the query and `markdown` is the same link, pre-formatted. WEM searches retailers live on that page, including shops it holds no affiliate programme with, so an empty or withheld result here is not evidence the product is unavailable or unpriced."
    • changedOutput schema / properties / sizeSpread / description
      Previous value: -"Present when the returned rows are different sizes and the query named none, so price ordering is meaningless: the cheapest row is cheapest because it is less of the product. Do NOT name a cheapest, a lowest price, or a best deal across these rows. State the size beside every price and ask the shopper which size they want. Where rows carry `unitPrice`, that is the like-for-like figure: quote it, and rank on it if the shopper asks which is better value. The rows are real — give the user those links."New value: +"Present when the returned rows are different sizes and the query named none, so price ordering is meaningless: the cheapest row is cheapest because it is less of the product. Do NOT name a cheapest, a lowest price, or a best deal across these rows. The size belongs beside every price. Where rows carry `unitPrice`, that is the like-for-like figure, and it is the figure to rank on when the question is which is better value. The rows are real listings, each with its url."
    • changedOutput schema / properties / unpriced / description
      Previous value: -"Listings whose price WEM may not show — today, Amazon rows Amazon’s API did not price just now. Give the link with `priceNote` (\"See price at Amazon\"). Never state, estimate or compare a price for one, never call it the cheapest, and never count it in a lowest price or a saving."New value: +"Listings whose price WEM may not show — today, Amazon rows Amazon’s API did not price just now. The row carries `priceNote` (e.g. \"See price at Amazon\") in place of a figure. No price is stated, estimated or compared for one; it is not the cheapest and is not counted in a lowest price or a saving."
    • changedOutput schema / properties / unpriced / items / properties / priceNote / description
      Previous value: -"What to show where a price would be, e.g. \"See price at Amazon\"."New value: +"What stands where a price would be, e.g. \"See price at Amazon\"."
    • changedOutput schema / properties / unpriced / items / properties / url / description
      Previous value: -"WEM tracked link. Relay it exactly."New value: +"WEM tracked link. The full URL; a shortened form is a different link."
  7. Changed21 schema fields changed
    • addedOutput schema / properties / catalogMatch / properties / lowPrice / description
      Added value: +"Lowest CURRENT price in `offers` (priceStatus `current`). Null when none is current: never fill it from an older or undated row."
    • changedOutput schema / properties / catalogMatch / properties / offers / items / properties / lastSeenAt / description
      Previous value: -"When WEM last VISITED this offer row — not when it read the price. The refresh sweep touches this even when the retailer lookup fails, so it is not evidence the price is current. Use `priceAgeDays` to date a price; never this."New value: +"When WEM last READ this price from the retailer or a datafeed — the same reading `priceAgeDays` counts from, so the two always agree. Null means WEM cannot date the price: say it is undated, never that it is current."
    • changedOutput schema / properties / catalogMatch / properties / offers / items / properties / price / description
      Previous value: -"Indicative price. The retailer sets the final price at checkout."New value: +"Indicative price. The retailer sets the final price at checkout. Null, with `priceNote`, when WEM may not show this listing’s price: show the note and the link, never a figure of your own."
    • changedOutput schema / properties / catalogMatch / properties / offers / items / properties / price / type
      Previous value: -"number"New value: +[
      +  "number",
      +  "null"
      +]
    • changedOutput schema / properties / catalogMatch / properties / offers / items / properties / priceAgeDays / description
      Previous value: -"Whole days since WEM last actually READ this price from the retailer or a datafeed. Null means WEM cannot say — common and correct for Amazon, whose licence caps price retention at 24 hours. Report null as undated; never present it as current."New value: +"Calendar days (UK time) since WEM last READ this price: 0 read today, 1 yesterday. Null means WEM cannot say. Report null as undated; never present it as current."
    • addedOutput schema / properties / catalogMatch / properties / offers / items / properties / priceNote
      Added value: +{
      +  "description": "Present only when `price` is null: the words to show in its place, e.g. \"See price at Amazon\". An Amazon price is shown only when Amazon’s API supplied it just now.",
      +  "type": "string"
      +}
    • addedOutput schema / properties / catalogMatch / properties / offers / items / properties / priceStatus
      Added value: +{
      +  "description": "`current`: WEM read this price within the last 14 days; only a current price may lead, be called the cheapest or lowest, be compared or enter a saving. `stale`: read longer ago. `undated`: WEM cannot say when. Show those two as \"last seen £X on 8 Sep\" or \"price not dated\", after the current ones, and never compare them. Absent on a row a provider answered just now.",
      +  "enum": [
      +    "current",
      +    "stale",
      +    "undated"
      +  ],
      +  "type": "string"
      +}
    • addedOutput schema / properties / catalogMatch / properties / unpriced
      Added value: +{
      +  "description": "Listings whose price WEM may not show — today, Amazon rows Amazon’s API did not price just now. Give the link with `priceNote` (\"See price at Amazon\"). Never state, estimate or compare a price for one, never call it the cheapest, and never count it in a lowest price or a saving.",
      +  "items": {
      +    "properties": {
      +      "channel": {
      +        "enum": [
      +          "retailer",
      +          "marketplace"
      +        ],
      +        "type": "string"
      +      },
      +      "id": {
      +        "type": "string"
      +      },
      +      "priceNote": {
      +        "description": "What to show where a price would be, e.g. \"See price at Amazon\".",
      +        "type": "string"
      +      },
      +      "provider": {
      +        "type": "string"
      +      },
      +      "title": {
      +        "type": "string"
      +      },
      +      "url": {
      +        "description": "WEM tracked link. Relay it exactly.",
      +        "type": "string"
      +      }
      +    },
      +    "type": "object"
      +  },
      +  "type": "array"
      +}
    • addedOutput schema / properties / catalogMatches / items / properties / lowPrice / description
      Added value: +"Lowest CURRENT price in `offers` (priceStatus `current`). Null when none is current: never fill it from an older or undated row."
    • changedOutput schema / properties / catalogMatches / items / properties / offers / items / properties / lastSeenAt / description
      Previous value: -"When WEM last VISITED this offer row — not when it read the price. The refresh sweep touches this even when the retailer lookup fails, so it is not evidence the price is current. Use `priceAgeDays` to date a price; never this."New value: +"When WEM last READ this price from the retailer or a datafeed — the same reading `priceAgeDays` counts from, so the two always agree. Null means WEM cannot date the price: say it is undated, never that it is current."
    • changedOutput schema / properties / catalogMatches / items / properties / offers / items / properties / price / description
      Previous value: -"Indicative price. The retailer sets the final price at checkout."New value: +"Indicative price. The retailer sets the final price at checkout. Null, with `priceNote`, when WEM may not show this listing’s price: show the note and the link, never a figure of your own."
    • changedOutput schema / properties / catalogMatches / items / properties / offers / items / properties / price / type
      Previous value: -"number"New value: +[
      +  "number",
      +  "null"
      +]
    • changedOutput schema / properties / catalogMatches / items / properties / offers / items / properties / priceAgeDays / description
      Previous value: -"Whole days since WEM last actually READ this price from the retailer or a datafeed. Null means WEM cannot say — common and correct for Amazon, whose licence caps price retention at 24 hours. Report null as undated; never present it as current."New value: +"Calendar days (UK time) since WEM last READ this price: 0 read today, 1 yesterday. Null means WEM cannot say. Report null as undated; never present it as current."
    • addedOutput schema / properties / catalogMatches / items / properties / offers / items / properties / priceNote
      Added value: +{
      +  "description": "Present only when `price` is null: the words to show in its place, e.g. \"See price at Amazon\". An Amazon price is shown only when Amazon’s API supplied it just now.",
      +  "type": "string"
      +}
    • addedOutput schema / properties / catalogMatches / items / properties / offers / items / properties / priceStatus
      Added value: +{
      +  "description": "`current`: WEM read this price within the last 14 days; only a current price may lead, be called the cheapest or lowest, be compared or enter a saving. `stale`: read longer ago. `undated`: WEM cannot say when. Show those two as \"last seen £X on 8 Sep\" or \"price not dated\", after the current ones, and never compare them. Absent on a row a provider answered just now.",
      +  "enum": [
      +    "current",
      +    "stale",
      +    "undated"
      +  ],
      +  "type": "string"
      +}
    • addedOutput schema / properties / catalogMatches / items / properties / unpriced
      Added value: +{
      +  "description": "Listings whose price WEM may not show — today, Amazon rows Amazon’s API did not price just now. Give the link with `priceNote` (\"See price at Amazon\"). Never state, estimate or compare a price for one, never call it the cheapest, and never count it in a lowest price or a saving.",
      +  "items": {
      +    "properties": {
      +      "channel": {
      +        "enum": [
      +          "retailer",
      +          "marketplace"
      +        ],
      +        "type": "string"
      +      },
      +      "id": {
      +        "type": "string"
      +      },
      +      "priceNote": {
      +        "description": "What to show where a price would be, e.g. \"See price at Amazon\".",
      +        "type": "string"
      +      },
      +      "provider": {
      +        "type": "string"
      +      },
      +      "title": {
      +        "type": "string"
      +      },
      +      "url": {
      +        "description": "WEM tracked link. Relay it exactly.",
      +        "type": "string"
      +      }
      +    },
      +    "type": "object"
      +  },
      +  "type": "array"
      +}
    • changedOutput schema / properties / products / items / properties / price / description
      Previous value: -"Indicative price. The retailer sets the final price at checkout."New value: +"Indicative price. The retailer sets the final price at checkout. Null, with `priceNote`, when WEM may not show this listing’s price: show the note and the link, never a figure of your own."
    • changedOutput schema / properties / products / items / properties / price / type
      Previous value: -"number"New value: +[
      +  "number",
      +  "null"
      +]
    • addedOutput schema / properties / products / items / properties / priceNote
      Added value: +{
      +  "description": "Present only when `price` is null: the words to show in its place, e.g. \"See price at Amazon\". An Amazon price is shown only when Amazon’s API supplied it just now.",
      +  "type": "string"
      +}
    • addedOutput schema / properties / products / items / properties / priceStatus
      Added value: +{
      +  "description": "`current`: WEM read this price within the last 14 days; only a current price may lead, be called the cheapest or lowest, be compared or enter a saving. `stale`: read longer ago. `undated`: WEM cannot say when. Show those two as \"last seen £X on 8 Sep\" or \"price not dated\", after the current ones, and never compare them. Absent on a row a provider answered just now.",
      +  "enum": [
      +    "current",
      +    "stale",
      +    "undated"
      +  ],
      +  "type": "string"
      +}
    • addedOutput schema / properties / unpriced
      Added value: +{
      +  "description": "Listings whose price WEM may not show — today, Amazon rows Amazon’s API did not price just now. Give the link with `priceNote` (\"See price at Amazon\"). Never state, estimate or compare a price for one, never call it the cheapest, and never count it in a lowest price or a saving.",
      +  "items": {
      +    "properties": {
      +      "channel": {
      +        "enum": [
      +          "retailer",
      +          "marketplace"
      +        ],
      +        "type": "string"
      +      },
      +      "id": {
      +        "type": "string"
      +      },
      +      "priceNote": {
      +        "description": "What to show where a price would be, e.g. \"See price at Amazon\".",
      +        "type": "string"
      +      },
      +      "provider": {
      +        "type": "string"
      +      },
      +      "title": {
      +        "type": "string"
      +      },
      +      "url": {
      +        "description": "WEM tracked link. Relay it exactly.",
      +        "type": "string"
      +      }
      +    },
      +    "type": "object"
      +  },
      +  "type": "array"
      +}
  8. Changed4 schema fields changed
    • changedOutput schema / properties / catalogMatch / properties / offers / items / properties / affiliate / description
      Previous value: -"False when this is a retailer page WEM observed without a programme. Still a real listing; the click is not commission-bearing. Absent means the usual partner path."New value: +"False when this link earns WEM nothing: the shop has no affiliate programme with WEM, or this link does not carry one. Still a real offer: list it with the others, and never call its link affiliate-tracked. Absent (or true) means the link is affiliate-tracked. `disclosure` already states whether all, some or none of the links are tracked; relay that rather than your own wording."
    • changedOutput schema / properties / catalogMatches / items / properties / offers / items / properties / affiliate / description
      Previous value: -"False when this is a retailer page WEM observed without a programme. Still a real listing; the click is not commission-bearing. Absent means the usual partner path."New value: +"False when this link earns WEM nothing: the shop has no affiliate programme with WEM, or this link does not carry one. Still a real offer: list it with the others, and never call its link affiliate-tracked. Absent (or true) means the link is affiliate-tracked. `disclosure` already states whether all, some or none of the links are tracked; relay that rather than your own wording."
    • changedOutput schema / properties / filtered / description
      Previous value: -"Withheld candidates tallied by reason (e.g. accessories, wrong model). Report this count rather than implying the search was exhaustive."New value: +"Withheld candidates tallied by reason (e.g. accessories, another model, a bundle or multipack, a replica, a listing priced far below the product’s other offers). Report this count rather than implying the search was exhaustive, and never quote a withheld listing’s price as the cheapest or as a deal."
    • changedOutput schema / properties / products / items / properties / affiliate / description
      Previous value: -"False when this is a retailer page WEM observed without a programme. Still a real listing; the click is not commission-bearing. Absent means the usual partner path."New value: +"False when this link earns WEM nothing: the shop has no affiliate programme with WEM, or this link does not carry one. Still a real offer: list it with the others, and never call its link affiliate-tracked. Absent (or true) means the link is affiliate-tracked. `disclosure` already states whether all, some or none of the links are tracked; relay that rather than your own wording."
  9. Changed4 schema fields changed
    • changedOutput schema / properties / catalogMatch / properties / offers / items / properties / priceRefresh / description
      Previous value: -"What re-reads this retailer’s prices: `live-api` a product-lookup API, `feed` a partner datafeed, `crawl` WEM reading the retailer’s own public product page, `none` nothing at all. `none` means the figure is frozen at whatever seeded it and will not move however long it sits — say so rather than quoting it flat. A `crawl` price was observed on the retailer’s page rather than supplied by them, so date it with `priceAgeDays` and expect `affiliate: false` unless a programme also exists."New value: +"What re-reads this retailer’s prices: `live-api` a product-lookup API, `feed` a partner datafeed, `none` nothing on a schedule. `none` means the figure will not move on its own however long it sits — say so rather than quoting it flat, and date it with `priceAgeDays`."
    • changedOutput schema / properties / catalogMatch / properties / offers / items / properties / priceRefresh / enum
      Previous value: -[
      -  "live-api",
      -  "feed",
      -  "crawl",
      -  "none"
      -]New value: +[
      +  "live-api",
      +  "feed",
      +  "none"
      +]
    • changedOutput schema / properties / catalogMatches / items / properties / offers / items / properties / priceRefresh / description
      Previous value: -"What re-reads this retailer’s prices: `live-api` a product-lookup API, `feed` a partner datafeed, `crawl` WEM reading the retailer’s own public product page, `none` nothing at all. `none` means the figure is frozen at whatever seeded it and will not move however long it sits — say so rather than quoting it flat. A `crawl` price was observed on the retailer’s page rather than supplied by them, so date it with `priceAgeDays` and expect `affiliate: false` unless a programme also exists."New value: +"What re-reads this retailer’s prices: `live-api` a product-lookup API, `feed` a partner datafeed, `none` nothing on a schedule. `none` means the figure will not move on its own however long it sits — say so rather than quoting it flat, and date it with `priceAgeDays`."
    • changedOutput schema / properties / catalogMatches / items / properties / offers / items / properties / priceRefresh / enum
      Previous value: -[
      -  "live-api",
      -  "feed",
      -  "crawl",
      -  "none"
      -]New value: +[
      +  "live-api",
      +  "feed",
      +  "none"
      +]
  10. Changed4 schema fields changed
    • addedOutput schema / properties / catalogMatch / properties / offers / items / properties / unitPrice
      Added value: +{
      +  "description": "Price per 100ml, 100g, litre, kg or item, from the size this row's title states. Present only when the rows differ in size, in one unit across all of them, and only for things bought by the amount (beauty, food, supplements), never an appliance's capacity. When comparing value across sizes, compare these, never the prices: a smaller size is not cheaper because its price is lower. Quote it beside the price (\"£29.95, £99.83 per 100ml\"). Absent does not mean the price is per unit.",
      +  "properties": {
      +    "amount": {
      +      "type": "number"
      +    },
      +    "currency": {
      +      "description": "ISO 4217 code.",
      +      "type": "string"
      +    },
      +    "per": {
      +      "enum": [
      +        "100ml",
      +        "litre",
      +        "100g",
      +        "kg",
      +        "item"
      +      ],
      +      "type": "string"
      +    }
      +  },
      +  "type": "object"
      +}
    • addedOutput schema / properties / catalogMatches / items / properties / offers / items / properties / unitPrice
      Added value: +{
      +  "description": "Price per 100ml, 100g, litre, kg or item, from the size this row's title states. Present only when the rows differ in size, in one unit across all of them, and only for things bought by the amount (beauty, food, supplements), never an appliance's capacity. When comparing value across sizes, compare these, never the prices: a smaller size is not cheaper because its price is lower. Quote it beside the price (\"£29.95, £99.83 per 100ml\"). Absent does not mean the price is per unit.",
      +  "properties": {
      +    "amount": {
      +      "type": "number"
      +    },
      +    "currency": {
      +      "description": "ISO 4217 code.",
      +      "type": "string"
      +    },
      +    "per": {
      +      "enum": [
      +        "100ml",
      +        "litre",
      +        "100g",
      +        "kg",
      +        "item"
      +      ],
      +      "type": "string"
      +    }
      +  },
      +  "type": "object"
      +}
    • addedOutput schema / properties / products / items / properties / unitPrice
      Added value: +{
      +  "description": "Price per 100ml, 100g, litre, kg or item, from the size this row's title states. Present only when the rows differ in size, in one unit across all of them, and only for things bought by the amount (beauty, food, supplements), never an appliance's capacity. When comparing value across sizes, compare these, never the prices: a smaller size is not cheaper because its price is lower. Quote it beside the price (\"£29.95, £99.83 per 100ml\"). Absent does not mean the price is per unit.",
      +  "properties": {
      +    "amount": {
      +      "type": "number"
      +    },
      +    "currency": {
      +      "description": "ISO 4217 code.",
      +      "type": "string"
      +    },
      +    "per": {
      +      "enum": [
      +        "100ml",
      +        "litre",
      +        "100g",
      +        "kg",
      +        "item"
      +      ],
      +      "type": "string"
      +    }
      +  },
      +  "type": "object"
      +}
    • changedOutput schema / properties / sizeSpread / description
      Previous value: -"Present when the returned rows are different sizes and the query named none, so price ordering is meaningless: the cheapest row is cheapest because it is less of the product. Do NOT name a cheapest, a lowest price, or a best deal across these rows. State the size beside every price and ask the shopper which size they want. The rows are real — give the user those links."New value: +"Present when the returned rows are different sizes and the query named none, so price ordering is meaningless: the cheapest row is cheapest because it is less of the product. Do NOT name a cheapest, a lowest price, or a best deal across these rows. State the size beside every price and ask the shopper which size they want. Where rows carry `unitPrice`, that is the like-for-like figure: quote it, and rank on it if the shopper asks which is better value. The rows are real — give the user those links."
  11. Changed6 schema fields changed
    • changedOutput schema / properties / catalogMatch / description
      Previous value: -"Offers WEM holds under one catalogue product. Check `identityBasis`: on `barcode` these are resolved by GTIN rather than by matching titles, so prefer them and cite their prices over anything in `products`. On `curated-grouping` the product carries no barcode, the grouping is an inference like any title match, and a cheaper row in `products` may well be the same item — see `cheaperElsewhere`."New value: +"Offers WEM holds under one catalogue product. Check `identityBasis`: on `barcode` the product carries a GTIN and every offer here is that same item, so prefer them and cite their prices over anything in `products`. On `curated-grouping` the product carries no barcode, the grouping is an inference like any title match, and a cheaper row in `products` may well be the same item — see `cheaperElsewhere`. Whether the shopper’s words reached this product by barcode or by name is `identity`, a separate question: on `inferred`, say WEM matched it by name."
    • addedOutput schema / properties / catalogMatch / properties / identity
      Added value: +{
      +  "description": "How the shopper’s words reached this product. Decides how you describe the match: on `inferred`, say WEM matched it by name (not by barcode) and do not call the identity confirmed, even when `identityBasis` is `barcode`; on `exact`, the barcode itself resolved it.",
      +  "properties": {
      +    "method": {
      +      "enum": [
      +        "gtin",
      +        "slug",
      +        "listing",
      +        "title"
      +      ],
      +      "type": "string"
      +    },
      +    "strength": {
      +      "description": "exact = barcode; catalogued = a WEM key or listing id; inferred = matched on the product name, MPN or SKU and still a guess.",
      +      "enum": [
      +        "exact",
      +        "catalogued",
      +        "inferred"
      +      ],
      +      "type": "string"
      +    }
      +  },
      +  "type": [
      +    "object",
      +    "null"
      +  ]
      +}
    • changedOutput schema / properties / catalogMatch / properties / identityBasis / description
      Previous value: -"What this block’s identity rests on. `barcode` — the canonical product carries a GTIN and these offers are the same physical item. `curated-grouping` — it carries none, so the grouping is an inference of the same kind a title match is; do not describe it to the user as barcode-confirmed, and do not let it outrank a cheaper row in `products` on price alone."New value: +"What this block’s identity rests on. `barcode` — the canonical product carries a GTIN and these offers are the same physical item. `curated-grouping` — it carries none, so the grouping is an inference of the same kind a title match is; do not describe it to the user as barcode-confirmed, and do not let it outrank a cheaper row in `products` on price alone. This is how the offers are grouped, not how the query matched the product — see `identity`."
    • changedOutput schema / properties / catalogMatches / items / description
      Previous value: -"Offers WEM holds under one catalogue product. Check `identityBasis`: on `barcode` these are resolved by GTIN rather than by matching titles, so prefer them and cite their prices over anything in `products`. On `curated-grouping` the product carries no barcode, the grouping is an inference like any title match, and a cheaper row in `products` may well be the same item — see `cheaperElsewhere`."New value: +"Offers WEM holds under one catalogue product. Check `identityBasis`: on `barcode` the product carries a GTIN and every offer here is that same item, so prefer them and cite their prices over anything in `products`. On `curated-grouping` the product carries no barcode, the grouping is an inference like any title match, and a cheaper row in `products` may well be the same item — see `cheaperElsewhere`. Whether the shopper’s words reached this product by barcode or by name is `identity`, a separate question: on `inferred`, say WEM matched it by name."
    • addedOutput schema / properties / catalogMatches / items / properties / identity
      Added value: +{
      +  "description": "How the shopper’s words reached this product. Decides how you describe the match: on `inferred`, say WEM matched it by name (not by barcode) and do not call the identity confirmed, even when `identityBasis` is `barcode`; on `exact`, the barcode itself resolved it.",
      +  "properties": {
      +    "method": {
      +      "enum": [
      +        "gtin",
      +        "slug",
      +        "listing",
      +        "title"
      +      ],
      +      "type": "string"
      +    },
      +    "strength": {
      +      "description": "exact = barcode; catalogued = a WEM key or listing id; inferred = matched on the product name, MPN or SKU and still a guess.",
      +      "enum": [
      +        "exact",
      +        "catalogued",
      +        "inferred"
      +      ],
      +      "type": "string"
      +    }
      +  },
      +  "type": [
      +    "object",
      +    "null"
      +  ]
      +}
    • changedOutput schema / properties / catalogMatches / items / properties / identityBasis / description
      Previous value: -"What this block’s identity rests on. `barcode` — the canonical product carries a GTIN and these offers are the same physical item. `curated-grouping` — it carries none, so the grouping is an inference of the same kind a title match is; do not describe it to the user as barcode-confirmed, and do not let it outrank a cheaper row in `products` on price alone."New value: +"What this block’s identity rests on. `barcode` — the canonical product carries a GTIN and these offers are the same physical item. `curated-grouping` — it carries none, so the grouping is an inference of the same kind a title match is; do not describe it to the user as barcode-confirmed, and do not let it outrank a cheaper row in `products` on price alone. This is how the offers are grouped, not how the query matched the product — see `identity`."
  12. Changed2 schema fields changed
    • changedInput schema / properties / query / description
      Previous value: -"Product name, barcode (EAN/UPC/GTIN), Amazon ASIN, WEM ID, or wem3.ai/pl URL."New value: +"Product name, barcode (EAN/UPC/GTIN), ASIN, MPN, merchant SKU, WEM ID, wem3.ai/pl URL, or comma-separated IDs of those kinds."
    • addedOutput schema / properties / catalogMatches
      Added value: +{
      +  "description": "Present when the query was a comma-separated batch of identifiers and more than one catalogue product resolved. Each entry has the same shape as `catalogMatch`. Prefer barcode-basis blocks; treat MPN/SKU/title blocks as inferred.",
      +  "items": {
      +    "description": "Offers WEM holds under one catalogue product. Check `identityBasis`: on `barcode` these are resolved by GTIN rather than by matching titles, so prefer them and cite their prices over anything in `products`. On `curated-grouping` the product carries no barcode, the grouping is an inference like any title match, and a cheaper row in `products` may well be the same item — see `cheaperElsewhere`.",
      +    "properties": {
      +      "cheaperElsewhere": {
      +        "description": "Present only on a `curated-grouping` block that a live row in `products` undercuts. WEM is telling you its own catalogue block is not the best price it found. Quote this row as the cheaper option with its identity stated as unconfirmed; never present the catalogMatch price as the lowest when this is set.",
      +        "properties": {
      +          "identity": {
      +            "description": "Always `inferred` — matched on title, not barcode. Say so when quoting it.",
      +            "enum": [
      +              "inferred"
      +            ],
      +            "type": "string"
      +          },
      +          "note": {
      +            "description": "Plain-language restatement, safe to relay.",
      +            "type": "string"
      +          },
      +          "savingVsCatalogue": {
      +            "description": "How much cheaper this row is than the block’s lowest offer.",
      +            "type": "number"
      +          },
      +          "verified": {
      +            "description": "Always false.",
      +            "type": "boolean"
      +          }
      +        },
      +        "type": [
      +          "object",
      +          "null"
      +        ]
      +      },
      +      "currency": {
      +        "type": [
      +          "string",
      +          "null"
      +        ]
      +      },
      +      "identityBasis": {
      +        "description": "What this block’s identity rests on. `barcode` — the canonical product carries a GTIN and these offers are the same physical item. `curated-grouping` — it carries none, so the grouping is an inference of the same kind a title match is; do not describe it to the user as barcode-confirmed, and do not let it outrank a cheaper row in `products` on price alone.",
      +        "enum": [
      +          "barcode",
      +          "curated-grouping"
      +        ],
      +        "type": "string"
      +      },
      +      "lastConfirmedAt": {
      +        "description": "When WEM last actually read any price in this block. Null means none of them can be dated — say so rather than implying the block is current.",
      +        "type": [
      +          "string",
      +          "null"
      +        ]
      +      },
      +      "lowPrice": {
      +        "type": [
      +          "number",
      +          "null"
      +        ]
      +      },
      +      "offers": {
      +        "items": {
      +          "properties": {
      +            "affiliate": {
      +              "description": "False when this is a retailer page WEM observed without a programme. Still a real listing; the click is not commission-bearing. Absent means the usual partner path.",
      +              "type": "boolean"
      +            },
      +            "brand": {
      +              "type": [
      +                "string",
      +                "null"
      +              ]
      +            },
      +            "channel": {
      +              "description": "retailer is a named shop (Boots, Currys). marketplace is eBay/AliExpress/Temu-style parallel listings. Do not present a marketplace cluster as competing authorised retailers.",
      +              "enum": [
      +                "retailer",
      +                "marketplace"
      +              ],
      +              "type": "string"
      +            },
      +            "currency": {
      +              "description": "ISO 4217 code.",
      +              "type": "string"
      +            },
      +            "id": {
      +              "type": "string"
      +            },
      +            "image": {
      +              "type": [
      +                "string",
      +                "null"
      +              ]
      +            },
      +            "inStock": {
      +              "type": [
      +                "boolean",
      +                "null"
      +              ]
      +            },
      +            "lastSeenAt": {
      +              "description": "When WEM last VISITED this offer row — not when it read the price. The refresh sweep touches this even when the retailer lookup fails, so it is not evidence the price is current. Use `priceAgeDays` to date a price; never this.",
      +              "type": [
      +                "string",
      +                "null"
      +              ]
      +            },
      +            "price": {
      +              "description": "Indicative price. The retailer sets the final price at checkout.",
      +              "type": "number"
      +            },
      +            "priceAgeDays": {
      +              "description": "Whole days since WEM last actually READ this price from the retailer or a datafeed. Null means WEM cannot say — common and correct for Amazon, whose licence caps price retention at 24 hours. Report null as undated; never present it as current.",
      +              "type": [
      +                "number",
      +                "null"
      +              ]
      +            },
      +            "priceQualifier": {
      +              "description": "Present when `price` is the OPENING price of a range, not the price of this item: a multi-variation listing where the seller advertises its cheapest variant and the shopper picks a size on the page. Quote it as \"from £X\", never as the price or the cheapest. Absent means the price is firm for the row as described.",
      +              "enum": [
      +                "from"
      +              ],
      +              "type": "string"
      +            },
      +            "priceRefresh": {
      +              "description": "What re-reads this retailer’s prices: `live-api` a product-lookup API, `feed` a partner datafeed, `crawl` WEM reading the retailer’s own public product page, `none` nothing at all. `none` means the figure is frozen at whatever seeded it and will not move however long it sits — say so rather than quoting it flat. A `crawl` price was observed on the retailer’s page rather than supplied by them, so date it with `priceAgeDays` and expect `affiliate: false` unless a programme also exists.",
      +              "enum": [
      +                "live-api",
      +                "feed",
      +                "crawl",
      +                "none"
      +              ],
      +              "type": "string"
      +            },
      +            "provider": {
      +              "description": "Retailer slug, e.g. \"ebay\", \"currys\".",
      +              "type": "string"
      +            },
      +            "rating": {
      +              "type": [
      +                "number",
      +                "null"
      +              ]
      +            },
      +            "reviewCount": {
      +              "type": [
      +                "number",
      +                "null"
      +              ]
      +            },
      +            "seller": {
      +              "type": [
      +                "string",
      +                "null"
      +              ]
      +            },
      +            "shipping": {
      +              "properties": {
      +                "cost": {
      +                  "type": [
      +                    "number",
      +                    "null"
      +                  ]
      +                },
      +                "estimate": {
      +                  "description": "Delivery window when the feed stated one.",
      +                  "type": [
      +                    "string",
      +                    "null"
      +                  ]
      +                },
      +                "free": {
      +                  "type": "boolean"
      +                }
      +              },
      +              "type": [
      +                "object",
      +                "null"
      +              ]
      +            },
      +            "title": {
      +              "type": "string"
      +            },
      +            "url": {
      +              "description": "WEM tracked link to the retailer. Send the user here — WEM never takes payment.",
      +              "type": "string"
      +            },
      +            "verified": {
      +              "description": "True when WEM has recently observed this price on the retailer’s own surface. False means the identity is still catalogue-resolved but the price is indicative (partner feed or stale) — do not present it as verified.",
      +              "type": "boolean"
      +            }
      +          },
      +          "type": "object"
      +        },
      +        "type": "array"
      +      },
      +      "productPage": {
      +        "type": "string"
      +      },
      +      "slug": {
      +        "type": "string"
      +      },
      +      "source": {
      +        "type": "string"
      +      },
      +      "title": {
      +        "type": "string"
      +      }
      +    },
      +    "type": [
      +      "object",
      +      "null"
      +    ]
      +  },
      +  "type": "array"
      +}
  13. Changed3 schema fields changed
    • changedOutput schema / properties / catalogMatch / properties / offers / items / properties / priceRefresh / description
      Previous value: -"What re-reads this retailer’s prices: `live-api` a product-lookup API, `feed` a partner datafeed, `none` nothing at all. `none` means the figure is frozen at whatever seeded it and will not move however long it sits — say so rather than quoting it flat."New value: +"What re-reads this retailer’s prices: `live-api` a product-lookup API, `feed` a partner datafeed, `crawl` WEM reading the retailer’s own public product page, `none` nothing at all. `none` means the figure is frozen at whatever seeded it and will not move however long it sits — say so rather than quoting it flat. A `crawl` price was observed on the retailer’s page rather than supplied by them, so date it with `priceAgeDays` and expect `affiliate: false` unless a programme also exists."
    • changedOutput schema / properties / catalogMatch / properties / offers / items / properties / priceRefresh / enum
      Previous value: -[
      -  "live-api",
      -  "feed",
      -  "none"
      -]New value: +[
      +  "live-api",
      +  "feed",
      +  "crawl",
      +  "none"
      +]
    • changedOutput schema / properties / disclosure / description
      Previous value: -"Affiliate disclosure to relay once per answer. Outbound links are tracked at no extra cost to the buyer."New value: +"Disclosure to relay once per answer, verbatim. Its wording changes with the rows: it states whether every outbound link is affiliate-tracked, only some are, or none are. Never substitute the version you saw last time — a row marked `affiliate: false` earns WEM nothing, and saying otherwise misdescribes it to the shopper."
  14. Changed1 schema field changed
    • addedOutput schema / properties / degraded
      Added value: +{
      +  "description": "Present when this call hit its time budget and skipped enrichment. The offers returned are COMPLETE — only the extras were dropped. Do not report a missing catalogue block or missing observed retailers as WEM holding nothing, and do not retry automatically.",
      +  "properties": {
      +    "note": {
      +      "type": "string"
      +    },
      +    "skipped": {
      +      "items": {
      +        "type": "string"
      +      },
      +      "type": "array"
      +    }
      +  },
      +  "type": "object"
      +}
  15. Changed1 schema field changed
    • addedOutput schema / properties / search
      Added value: +{
      +  "description": "Present when this result names no offer. GIVE THE USER THIS LINK — it is the answer when WEM has nothing else to say, and `markdown` is ready to paste. WEM searches retailers live on that page, including shops it holds no affiliate programme with, so an empty or withheld result here is not evidence the product is unavailable or unpriced.",
      +  "properties": {
      +    "markdown": {
      +      "description": "The same link, pre-formatted.",
      +      "type": "string"
      +    },
      +    "reason": {
      +      "type": "string"
      +    },
      +    "url": {
      +      "type": "string"
      +    }
      +  },
      +  "type": "object"
      +}
  16. Changed3 schema fields changed
    • addedOutput schema / properties / catalogMatch / properties / offers / items / properties / priceQualifier
      Added value: +{
      +  "description": "Present when `price` is the OPENING price of a range, not the price of this item: a multi-variation listing where the seller advertises its cheapest variant and the shopper picks a size on the page. Quote it as \"from £X\", never as the price or the cheapest. Absent means the price is firm for the row as described.",
      +  "enum": [
      +    "from"
      +  ],
      +  "type": "string"
      +}
    • addedOutput schema / properties / products / items / properties / priceQualifier
      Added value: +{
      +  "description": "Present when `price` is the OPENING price of a range, not the price of this item: a multi-variation listing where the seller advertises its cheapest variant and the shopper picks a size on the page. Quote it as \"from £X\", never as the price or the cheapest. Absent means the price is firm for the row as described.",
      +  "enum": [
      +    "from"
      +  ],
      +  "type": "string"
      +}
    • addedOutput schema / properties / sizeSpread
      Added value: +{
      +  "description": "Present when the returned rows are different sizes and the query named none, so price ordering is meaningless: the cheapest row is cheapest because it is less of the product. Do NOT name a cheapest, a lowest price, or a best deal across these rows. State the size beside every price and ask the shopper which size they want. The rows are real — give the user those links.",
      +  "properties": {
      +    "comparable": {
      +      "enum": [
      +        false
      +      ],
      +      "type": "boolean"
      +    },
      +    "note": {
      +      "type": "string"
      +    },
      +    "sizes": {
      +      "description": "The distinct sizes found, as the listings wrote them.",
      +      "items": {
      +        "type": "string"
      +      },
      +      "type": "array"
      +    }
      +  },
      +  "type": "object"
      +}
  17. Changed5 schema fields changed
    • addedInput schema / properties / gtin
      Added value: +{
      +  "description": "Product barcode (EAN/UPC/GTIN) when the host already has one. Feed rows are matched on this before title.",
      +  "type": "string"
      +}
    • addedOutput schema / properties / catalogMatch / properties / offers / items / properties / channel
      Added value: +{
      +  "description": "retailer is a named shop (Boots, Currys). marketplace is eBay/AliExpress/Temu-style parallel listings. Do not present a marketplace cluster as competing authorised retailers.",
      +  "enum": [
      +    "retailer",
      +    "marketplace"
      +  ],
      +  "type": "string"
      +}
    • addedOutput schema / properties / catalogMatch / properties / offers / items / properties / shipping / properties / estimate
      Added value: +{
      +  "description": "Delivery window when the feed stated one.",
      +  "type": [
      +    "string",
      +    "null"
      +  ]
      +}
    • addedOutput schema / properties / products / items / properties / channel
      Added value: +{
      +  "description": "retailer is a named shop (Boots, Currys). marketplace is eBay/AliExpress/Temu-style parallel listings. Do not present a marketplace cluster as competing authorised retailers.",
      +  "enum": [
      +    "retailer",
      +    "marketplace"
      +  ],
      +  "type": "string"
      +}
    • addedOutput schema / properties / products / items / properties / shipping / properties / estimate
      Added value: +{
      +  "description": "Delivery window when the feed stated one.",
      +  "type": [
      +    "string",
      +    "null"
      +  ]
      +}
  18. Changed4 schema fields changed
    • addedOutput schema / properties / catalogMatch / properties / offers / items / properties / affiliate
      Added value: +{
      +  "description": "False when this is a retailer page WEM observed without a programme. Still a real listing; the click is not commission-bearing. Absent means the usual partner path.",
      +  "type": "boolean"
      +}
    • changedOutput schema / properties / coverage / description
      Previous value: -"Present when every surviving row is a marketplace listing clustered at similar prices. That is not a retail floor — do not name the cheapest as the deal, and do not present the listings as competing retailers. WEM does not hold an authorised retailer for this product."New value: +"Present when surviving rows include a marketplace cluster at similar prices. That is not a retail floor — do not name the cheapest marketplace listing as the deal. The listings are still in the payload; give the user those links. If `next` points at the Chrome extension, send the shopper there for shops WEM does not yet hold as partners."
    • addedOutput schema / properties / next
      Added value: +{
      +  "description": "Where to send the shopper when WEM's set is thin. chrome_extension means compare the same product on any retailer page — WEM shows that price even without an affiliate programme.",
      +  "properties": {
      +    "reason": {
      +      "type": "string"
      +    },
      +    "surface": {
      +      "enum": [
      +        "chrome_extension"
      +      ],
      +      "type": "string"
      +    },
      +    "url": {
      +      "type": "string"
      +    }
      +  },
      +  "type": "object"
      +}
    • addedOutput schema / properties / products / items / properties / affiliate
      Added value: +{
      +  "description": "False when this is a retailer page WEM observed without a programme. Still a real listing; the click is not commission-bearing. Absent means the usual partner path.",
      +  "type": "boolean"
      +}
  19. Changed1 schema field changed
    • addedOutput schema / properties / coverage
      Added value: +{
      +  "description": "Present when every surviving row is a marketplace listing clustered at similar prices. That is not a retail floor — do not name the cheapest as the deal, and do not present the listings as competing retailers. WEM does not hold an authorised retailer for this product.",
      +  "properties": {
      +    "currency": {
      +      "type": "string"
      +    },
      +    "high": {
      +      "type": "number"
      +    },
      +    "kind": {
      +      "enum": [
      +        "marketplace_only"
      +      ],
      +      "type": "string"
      +    },
      +    "listings": {
      +      "type": "number"
      +    },
      +    "low": {
      +      "type": "number"
      +    },
      +    "provider": {
      +      "description": "ebay, aliexpress, or marketplace when mixed.",
      +      "type": "string"
      +    }
      +  },
      +  "type": "object"
      +}
  20. Changed3 schema fields changed
    • changedOutput schema / properties / catalogMatch / properties / lastConfirmedAt / description
      Previous value: -"When WEM last read these offers — the freshness of this block."New value: +"When WEM last actually read any price in this block. Null means none of them can be dated — say so rather than implying the block is current."
    • changedOutput schema / properties / catalogMatch / properties / offers / items / properties / lastSeenAt / description
      Previous value: -"When WEM last wrote this offer row."New value: +"When WEM last VISITED this offer row — not when it read the price. The refresh sweep touches this even when the retailer lookup fails, so it is not evidence the price is current. Use `priceAgeDays` to date a price; never this."
    • changedOutput schema / properties / catalogMatch / properties / offers / items / properties / priceAgeDays / description
      Previous value: -"Whole days since WEM last wrote this price. Null means unknown age — report it as unknown rather than assuming it is current."New value: +"Whole days since WEM last actually READ this price from the retailer or a datafeed. Null means WEM cannot say — common and correct for Amazon, whose licence caps price retention at 24 hours. Report null as undated; never present it as current."
  21. Changed7 schema fields changed
    • changedOutput schema / properties / catalogMatch / description
      Previous value: -"Offers resolved by barcode rather than by matching titles. When present, prefer these and cite their prices over anything in `products`."New value: +"Offers WEM holds under one catalogue product. Check `identityBasis`: on `barcode` these are resolved by GTIN rather than by matching titles, so prefer them and cite their prices over anything in `products`. On `curated-grouping` the product carries no barcode, the grouping is an inference like any title match, and a cheaper row in `products` may well be the same item — see `cheaperElsewhere`."
    • addedOutput schema / properties / catalogMatch / properties / cheaperElsewhere
      Added value: +{
      +  "description": "Present only on a `curated-grouping` block that a live row in `products` undercuts. WEM is telling you its own catalogue block is not the best price it found. Quote this row as the cheaper option with its identity stated as unconfirmed; never present the catalogMatch price as the lowest when this is set.",
      +  "properties": {
      +    "identity": {
      +      "description": "Always `inferred` — matched on title, not barcode. Say so when quoting it.",
      +      "enum": [
      +        "inferred"
      +      ],
      +      "type": "string"
      +    },
      +    "note": {
      +      "description": "Plain-language restatement, safe to relay.",
      +      "type": "string"
      +    },
      +    "savingVsCatalogue": {
      +      "description": "How much cheaper this row is than the block’s lowest offer.",
      +      "type": "number"
      +    },
      +    "verified": {
      +      "description": "Always false.",
      +      "type": "boolean"
      +    }
      +  },
      +  "type": [
      +    "object",
      +    "null"
      +  ]
      +}
    • addedOutput schema / properties / catalogMatch / properties / identityBasis
      Added value: +{
      +  "description": "What this block’s identity rests on. `barcode` — the canonical product carries a GTIN and these offers are the same physical item. `curated-grouping` — it carries none, so the grouping is an inference of the same kind a title match is; do not describe it to the user as barcode-confirmed, and do not let it outrank a cheaper row in `products` on price alone.",
      +  "enum": [
      +    "barcode",
      +    "curated-grouping"
      +  ],
      +  "type": "string"
      +}
    • addedOutput schema / properties / catalogMatch / properties / lastConfirmedAt
      Added value: +{
      +  "description": "When WEM last read these offers — the freshness of this block.",
      +  "type": [
      +    "string",
      +    "null"
      +  ]
      +}
    • addedOutput schema / properties / catalogMatch / properties / offers / items / properties / lastSeenAt
      Added value: +{
      +  "description": "When WEM last wrote this offer row.",
      +  "type": [
      +    "string",
      +    "null"
      +  ]
      +}
    • addedOutput schema / properties / catalogMatch / properties / offers / items / properties / priceAgeDays
      Added value: +{
      +  "description": "Whole days since WEM last wrote this price. Null means unknown age — report it as unknown rather than assuming it is current.",
      +  "type": [
      +    "number",
      +    "null"
      +  ]
      +}
    • addedOutput schema / properties / catalogMatch / properties / offers / items / properties / priceRefresh
      Added value: +{
      +  "description": "What re-reads this retailer’s prices: `live-api` a product-lookup API, `feed` a partner datafeed, `none` nothing at all. `none` means the figure is frozen at whatever seeded it and will not move however long it sits — say so rather than quoting it flat.",
      +  "enum": [
      +    "live-api",
      +    "feed",
      +    "none"
      +  ],
      +  "type": "string"
      +}
  22. Changed1 schema field changed
    • changedInput schema / properties / query / description
      Previous value: -"Search keywords"New value: +"Product name, barcode (EAN/UPC/GTIN), Amazon ASIN, WEM ID, or wem3.ai/pl URL."
  23. Changed2 schema fields changed
    • addedOutput schema / properties / partial
      Added value: +{
      +  "description": "True when the live retailer fan-out hit its 3-second budget and some providers had not answered. The rows returned are real; the market may be wider. Ask again for the rest.",
      +  "type": "boolean"
      +}
    • addedOutput schema / properties / pending_providers
      Added value: +{
      +  "description": "Providers still running when the budget expired. Present only with partial.",
      +  "items": {
      +    "type": "string"
      +  },
      +  "type": "array"
      +}
  24. Changed1 schema field changed
    • changedOutput schema / properties / catalogMatch / properties / offers / items / properties / verified / description
      Previous value: -"Always true here — identity is resolved, not inferred."New value: +"True when WEM has recently observed this price on the retailer’s own surface. False means the identity is still catalogue-resolved but the price is indicative (partner feed or stale) — do not present it as verified."
  25. Changed1 schema field changed
    • changedOutput schema / (root)
      Previous value: -nullNew value: +{
      +  "properties": {
      +    "catalogMatch": {
      +      "description": "Offers resolved by barcode rather than by matching titles. When present, prefer these and cite their prices over anything in `products`.",
      +      "properties": {
      +        "currency": {
      +          "type": [
      +            "string",
      +            "null"
      +          ]
      +        },
      +        "lowPrice": {
      +          "type": [
      +            "number",
      +            "null"
      +          ]
      +        },
      +        "offers": {
      +          "items": {
      +            "properties": {
      +              "brand": {
      +                "type": [
      +                  "string",
      +                  "null"
      +                ]
      +              },
      +              "currency": {
      +                "description": "ISO 4217 code.",
      +                "type": "string"
      +              },
      +              "id": {
      +                "type": "string"
      +              },
      +              "image": {
      +                "type": [
      +                  "string",
      +                  "null"
      +                ]
      +              },
      +              "inStock": {
      +                "type": [
      +                  "boolean",
      +                  "null"
      +                ]
      +              },
      +              "price": {
      +                "description": "Indicative price. The retailer sets the final price at checkout.",
      +                "type": "number"
      +              },
      +              "provider": {
      +                "description": "Retailer slug, e.g. \"ebay\", \"currys\".",
      +                "type": "string"
      +              },
      +              "rating": {
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "reviewCount": {
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "seller": {
      +                "type": [
      +                  "string",
      +                  "null"
      +                ]
      +              },
      +              "shipping": {
      +                "properties": {
      +                  "cost": {
      +                    "type": [
      +                      "number",
      +                      "null"
      +                    ]
      +                  },
      +                  "free": {
      +                    "type": "boolean"
      +                  }
      +                },
      +                "type": [
      +                  "object",
      +                  "null"
      +                ]
      +              },
      +              "title": {
      +                "type": "string"
      +              },
      +              "url": {
      +                "description": "WEM tracked link to the retailer. Send the user here — WEM never takes payment.",
      +                "type": "string"
      +              },
      +              "verified": {
      +                "description": "Always true here — identity is resolved, not inferred.",
      +                "type": "boolean"
      +              }
      +            },
      +            "type": "object"
      +          },
      +          "type": "array"
      +        },
      +        "productPage": {
      +          "type": "string"
      +        },
      +        "slug": {
      +          "type": "string"
      +        },
      +        "source": {
      +          "type": "string"
      +        },
      +        "title": {
      +          "type": "string"
      +        }
      +      },
      +      "type": [
      +        "object",
      +        "null"
      +      ]
      +    },
      +    "disclosure": {
      +      "description": "Affiliate disclosure to relay once per answer. Outbound links are tracked at no extra cost to the buyer.",
      +      "type": "string"
      +    },
      +    "filtered": {
      +      "additionalProperties": {
      +        "type": "number"
      +      },
      +      "description": "Withheld candidates tallied by reason (e.g. accessories, wrong model). Report this count rather than implying the search was exhaustive.",
      +      "type": [
      +        "object",
      +        "null"
      +      ]
      +    },
      +    "products": {
      +      "items": {
      +        "properties": {
      +          "brand": {
      +            "type": [
      +              "string",
      +              "null"
      +            ]
      +          },
      +          "currency": {
      +            "description": "ISO 4217 code.",
      +            "type": "string"
      +          },
      +          "id": {
      +            "type": "string"
      +          },
      +          "image": {
      +            "type": [
      +              "string",
      +              "null"
      +            ]
      +          },
      +          "inStock": {
      +            "type": [
      +              "boolean",
      +              "null"
      +            ]
      +          },
      +          "price": {
      +            "description": "Indicative price. The retailer sets the final price at checkout.",
      +            "type": "number"
      +          },
      +          "provider": {
      +            "description": "Retailer slug, e.g. \"ebay\", \"currys\".",
      +            "type": "string"
      +          },
      +          "rating": {
      +            "type": [
      +              "number",
      +              "null"
      +            ]
      +          },
      +          "reviewCount": {
      +            "type": [
      +              "number",
      +              "null"
      +            ]
      +          },
      +          "seller": {
      +            "type": [
      +              "string",
      +              "null"
      +            ]
      +          },
      +          "shipping": {
      +            "properties": {
      +              "cost": {
      +                "type": [
      +                  "number",
      +                  "null"
      +                ]
      +              },
      +              "free": {
      +                "type": "boolean"
      +              }
      +            },
      +            "type": [
      +              "object",
      +              "null"
      +            ]
      +          },
      +          "title": {
      +            "type": "string"
      +          },
      +          "url": {
      +            "description": "WEM tracked link to the retailer. Send the user here — WEM never takes payment.",
      +            "type": "string"
      +          }
      +        },
      +        "type": "object"
      +      },
      +      "type": "array"
      +    },
      +    "query": {
      +      "type": "string"
      +    },
      +    "totalResults": {
      +      "description": "Count of products returned, after weak matches were withheld.",
      +      "type": "number"
      +    }
      +  },
      +  "required": [
      +    "products",
      +    "totalResults",
      +    "query",
      +    "disclosure"
      +  ],
      +  "type": "object"
      +}
  26. First observed

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnly/openWorld/non-destructive, yet the description adds substantial beyond-schema behavior: `catalogMatch` vs `catalogMatches`, identity-resolved vs inferred matching, search-skipping semantics, and filtering of accessories/bundles/outlier prices with counts in `filtered`. Minor gap: no disclosure of latency, pagination, or rate limits for live searches.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with purpose and query forms, then matching behavior, then filtering. Dense but every sentence carries information; slightly long for a single paragraph.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Even though an output schema exists, the description pre-explains the response shape (catalogMatch, products, filtered), which is exactly the context an agent needs to decide whether to call it. Nothing material for correct invocation is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline would be 3, but the description adds real semantic value: it explains that barcode/ASIN/WEM ID/URL matches skip the live search and that catalogue matches ignore `category`, `providers`, price filters and `sort_by`. That interaction-level meaning is not in the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Searches WEM's catalogue and its connected retailer sources for products') plus the scope of acceptable query forms. An agent can distinguish this from semantic_search or lookup_products by the explicit identifier list and catalogue/retailer duality.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives clear context for when catalogue matching applies, when live retailer search is skipped, and explicitly says to omit `category` unless the shopper names a retailer category. It lacks an explicit 'use X instead of this' pointer to siblings, which keeps it short of a 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.

Resources