Read the Method · what the capture reads, in order, and what it misses
How a capture reads a page
A capture is one fetch of one public product page, at 05:00 UTC for the ledger or on request for the free capture on the home page. Both run the same code. The page is fetched as a stranger's browser would fetch it, with no login and no cookies, and read for a price it publishes as data.
The order
Five methods, tried in this order. The first that finds a price answers; the rest are listed as not needed. Every reading records where on the page it came from and the SHA-256 of the matched fragment, so anyone holding the same HTML can find the same bytes.
- JSON-LD Product.offersThe structured data most themes publish. One reading per offer when a product lists variants, including ProductGroup.hasVariant; the offer's own priceCurrency and priceValidUntil are kept.
- microdata itemprop="price"Read from the content attribute where there is one, else from the element's text.
- Open Graph product:price:amountWith product:price:currency when the page states it.
- Shopify /products/<handle>.jsonA second fetch, made only when the page itself published nothing and its address is a Shopify /products/ page. The currency comes from the storefront's own markup.
- WooCommerce price markupThe amount inside p.price; when a sale is shown, the amount inside its <ins>, never the struck price and never a related product's.
Why from outside
The authorities read prices from outside too. In the 2025 Black Friday sweep, the Commission's EU eLab “allowed authorities to monitor the price history of the selected products and establish whether the prior price stated in the discount announcement was actually the lowest price applied to the product in the 30 days preceding the price reduction announcement.” A tool inside the shop writes down what the database says, and a database can be wrong in ways the page is not.
On the WordPress.org forum, a merchant reported that when a sale starts, “the plugin immediately records the new sale price as the "lowest price in the last 30 days"”. A capture from outside cannot make that mistake: the reduction day is not inside its own 30-day window, so the sale price never sets the prior price it is compared against.
What it misses
- A page behind a bot wall cannot be read from outside. That morning is a hole in the ledger, printed with the status the page answered, and the receipt prints the hole.
- A price drawn only by a script, or shown only in an image, is reported unread. Visible text is read only where the markup labels it as the price: an itemprop="price" element with no content attribute, or the WooCommerce markup above.
- One capture a day, at 05:00 UTC. A price that changed at noon and back by evening is not in the ledger.
- The product page only. Article 6a covers every announcement of a reduction, in every channel; banners, emails and shop windows are not read.
- The currency is the one the page's own markup states. Prices for other markets are captured only where the page lists them.
- The EU rule only. The United Kingdom is not covered.
Nothing until thirty
The receipt for a reduction starting on a given date looks at the 30 calendar days before it. A day counts when a price was read that morning; a missing day does not. With fewer than 30 days read before the reduction date, the receipt prints “no prior price yet: N of 30 days captured” and no number, because a number from a short history is the error an inspector is looking for.
With 30 or more, the prior price is the lowest price read in the window, and every missing day inside the window is printed in the same table with its reason. A missing day might have hidden a lower price. The receipt cannot rule that out, so it says how many there were.
The percentage is counted from the prior price, as the Court of Justice held in C-330/23, and rounded down, so it never says more than the two prices allow: (prior − sale) × 100 ÷ prior, in whole percent. Prices are compared in thousandths of the unit, in integers.
The receipt
A receipt holds the 30 rows, the prior price, the sale price read that morning, the percentage, the page's address, the SHA-256 of the ledger file it was derived from, the time it was issued and the rule it applied. That body is written as canonical JSON, keys sorted at every depth, and the receipt is named by the SHA-256 of those bytes. When the operator has set a signing key, the same bytes are signed with Ed25519 and the public key is published at /api/receipt?key=1. The verifier recomputes both in the browser.
How a page is fetched
https only, on the first request and on every redirect; at most 3 redirects; 10 seconds for the whole page; the first 3 MB read. Addresses that resolve to a private, loopback or link-local network are refused before the connection opens. The capture identifies itself as Mozilla/5.0 (compatible; Priorprice/1.0; +https://priorprice.vercel.app/method), and it fetches only the pages a store's owner listed or a visitor pasted. Ten free captures a minute from one address.
Other ways to get the number
Plugins inside the store record the store's own price history. They start with whatever history the store keeps and can fill the prior price into the theme automatically, which Priorprice does not do. Their published prices, quoted:
- Omnibus: 30-Day Lowest Price (DevShopHub), Shopify App Store: “Essential $9 / month”; “Growth $19 / month”. Read on 2026-10-10 at apps.shopify.com/omnibus-1.
- EU Omnibus Price Tracker (Meetanshi), Shopify App Store: “Omnibus Core $4.99 / month or $49.99/year and save 17%”. Read on 2026-10-10 at apps.shopify.com/eu-omnibus-price-tracker.
- WP Desk Omnibus for WooCommerce: “Professional € 69 /year 1 plugin PRO 1 site”. Read on 2026-10-10 at wpdesk.net/products/wp-desk-omnibus/.
Priorprice records from outside, which means it starts at zero on the morning of its first capture. For a reduction in late November, that first capture has to happen in October.