I already wrote about the EU 30-day rule: when a shop announces a discount, the crossed-out "old" price is supposed to be the lowest price it charged in the previous 30 days. This post is a walk through the places where shops actually put that number, and the lookalike figures next to it.

I built truediscount, a free open-source browser extension and userscript, to read those numbers on the product page. What follows comes from the shop adapters and the saved product pages I test against, not from today's live prices.

A generic sweep, then a dedicated reader

Many EU shops already print a sentence such as "lowest price in the last 30 days", "niedrigster Preis der letzten 30 Tage", "più basso ultimi 30gg" or "le plus bas". truediscount walks visible text for a "lowest" word plus a mention of 30 days, then reads the price next to that label.

That sweep is enough when the disclosure is ordinary text. Some shops need a dedicated reader, because the 30-day figure is in page state, in a labelled buy box, or next to a recommended retail price that looks similar.

Amazon: the was-price in the buy box

On Amazon EU stores, the current price is the one in the buy box. The crossed-out "was" price lives in the same block, often as a basisPrice or a struck a-price.

That "was" figure is not automatically the 30-day low. If Amazon prints a separate "lowest price in the last 30 days" label, the generic sweep picks it up. If it does not, the crossed-out price is just a was-price until you have your own history from a later visit.

One parsing trap I fixed in truediscount 0.13.3: amounts of 1,000 or more must be read in full. A price written as €1,599.00, 1.299,99 or a Swiss 1'299.00 used to get cut after the first digits, so the comparison never saw the real reference price.

Zalando: a struck price in the price container

Zalando's old price, when it is shown, is a strikethrough in the product price container. The current price often comes from JSON-LD. On one live product page I saved for tests, there was no struck old price at all, so there was nothing to compare until a 30-day label or a later visit provided a reference.

MediaMarkt, Saturn and MediaWorld: the type of the struck price matters

These three share a layout. The struck figure's data-test type is the whole story:

  • rrp is a recommended retail price (in Germany, the "UVP" label). It is not the shop's 30-day low.
  • previous is a claimed earlier shop price.
  • lop or lowest (and the Italian "Più basso ultimi 30gg") is the 30-day disclosure. It must not be treated as a "was" price.

On a saved MediaMarkt.de page the struck amount was labelled UVP, so it is an RRP. On a saved MediaWorld Italy page a "30gg" label was the 30-day disclosure, with no claimed old price. Swiss pages group thousands with an apostrophe, which has to be read as a whole, not as 1.

Lidl: look at the word next to the strike

Lidl prints the current price in one element and the struck amount in another. If the label next to the strike is "UVP", it is a manufacturer's recommended price. If the strike is bare, it is Lidl's own earlier price.

I have checked that on saved pages from lidl.de and lidl.fr. Other Lidl country sites share the same layout, but I have not live-tested each of them yet.

Notino: the selected size, not the JSON-LD offer

Notino is the shop that made me stop trusting the first JSON-LD offer on the page. That block lists every size, and the first offer is often another size or the price before a voucher code.

The shelf price is the selected size. The 30-day low is recentMinPrice on that same variant in the page state, also printed as "Mindestpreis" or "Letzter niedrigster Preis". If the page state is missing, the printed label is enough. A recommended price in that state is not crossed out, so it is not treated as a previous shop price.

This reader is in v0.13.3, for 25 Notino country sites. I checked it on saved pages from notino.de, and the same price state on notino.fr.

Euronics Italy: "prezzo precedente" versus "prezzo consigliato"

This is the RRP trap I want people to watch for this Black Friday. On euronics.it, the buy box holds the current price and two labels:

  • prezzo precedente, the barred previous price, is defined on the page as the lowest price charged in the 30 days before the promotion. That is the 30-day low.
  • prezzo consigliato is the manufacturer's recommended price. It is only an RRP. The extension uses it as a reference only when there is no previous price, and flags it as an RRP.

A later compare column repeats other prices, including figures that do not belong to this product, so it is ignored. On a saved promotion page the current price was under the barred previous price, so the discount looked real against the shop's own 30-day figure. On a saved page with only a recommended price, there was no 30-day number, so tracking starts and that figure is flagged as an RRP.

Euronics Italy landed on the main branch after 0.13.3. Other country sites are separate storefronts and are not included.

Otto and bol.com: what I have not checked yet

For otto.de I look for a retail-style old price. For bol.com I look for the list price in the buy block. Neither selector is, by itself, a 30-day disclosure. I have not yet checked those shops against a saved live product page, so I am not going to describe a disclosure I have not seen.

What to do with this

If you only remember one thing before Black Friday: a crossed-out number is not enough. Check the label. UVP, "prezzo consigliato", "retail price" and "adviesprijs" are recommended prices. The 30-day low may be in a quieter line, in a "30gg" or "Mindestpreis" label, or in the page's own definition of "prezzo precedente".

truediscount does that comparison on the product page. Price history stays in your browser. No account, no tracking, nothing is sent anywhere.

The current release is truediscount 0.13.3. A live demo is on the site.

Install it before you trust a "-50%" label this November.