[{"data":1,"prerenderedAt":83},["ShallowReactive",2],{"$f5_qZvM4Vokv-DQBhk2sJ9gd0Wjx_0lmwoDooc2eGJUM":3},{"title":4,"date":5,"dateModified":6,"datePublished":7,"dateModifiedISO":7,"image":8,"content":9,"faq":10,"metaTitle":30,"metaDescription":31,"author":32,"authorBio":33,"authorLinkedin":33,"authorTitle":33,"authorPhoto":33,"lastReviewed":33,"researchBasis":33,"category":34,"readingTime":35,"related":36,"prev":53,"next":56,"toc":59,"takeaways":82},"Price per Unit: How to Compare Competitor Prices Across Pack Sizes","30 Sep 2026","30 SEP 2026","2026-09-30","/img/news/price-per-unit-comparison-pack-sizes-2026.png","\u003Cp>A yarn retailer sent us a spreadsheet of competitor product links, spread over several markets and currencies. Next to each link was the SKU of their own yarn that it competes with, and the market it sells in. The question was simple: are we cheaper or not?\u003C/p>\n\u003Cp>The sticker prices could not answer it. One competitor sells a 50 g ball, another a 100 g ball, a third a 226 g cake, and a US shop gives the weight in ounces with its own gram figure next to it. A price per unit comparison was the only honest way to line them up, and for yarn the unit the retailer asked for was price per 50 g.\u003C/p>\n\u003Cp>This post is how we built it, what broke, and what the data looks like now.\u003C/p>\n\u003Ch2 id=\"why-sticker-prices-mislead\">Why Sticker Prices Mislead\u003C/h2>\n\u003Cp>Take two rows from the final data. One shop sells a 100 g ball for 2.60 in its currency. Another sells a 50 g ball for less. The second looks cheaper on the shelf and is more expensive per gram.\u003C/p>\n\u003Cp>This is not a yarn problem. Grocery, pet food, cleaning products and building materials all have it, and the EU makes shops show a unit price next to the selling price for exactly this reason (the \u003Ca href=\"https://commission.europa.eu/law/law-topic/consumer-protection-law/unfair-commercial-practices-and-price-indication/price-indication-directive_en\">Price Indication Directive\u003C/a>). The shop&#39;s own printed unit price does not help much for monitoring though. Few shops in our set published one, and those that did used different bases: per kg, per 100 g, per 50 g.\u003C/p>\n\u003Cp>So you need two raw numbers from every page, the price and the pack weight, and one calculation you control.\u003C/p>\n\u003Caside class=\"article__usecase-card\">\u003Cdiv class=\"article__usecase-label\">Related use case\u003C/div>\u003Ch3 class=\"article__usecase-title\">Competitor price tracking\u003C/h3>\u003Cp class=\"article__usecase-blurb\">Automated price monitoring across marketplaces. 97% accuracy, no per-SKU fees.\u003C/p>\u003Ca class=\"article__usecase-link\" href=\"/use-cases/competitor-price-tracking\">See how it works →\u003C/a>\u003C/aside>\u003Ch2 id=\"only-the-customer39s-links-nothing-else\">Only the Customer&#39;s Links, Nothing Else\u003C/h2>\n\u003Cp>This setup was the opposite of our \u003Ca href=\"/blogs/competitor-price-monitoring-case-study-2026\">multi-site competitor monitoring case study\u003C/a>, where we scraped whole catalogues and matched them afterwards. Here the customer had already done the matching by hand. Each link was a product they had chosen as the competitor to their SKU.\u003C/p>\n\u003Cp>So there was no link discovery at all. We scraped exactly the links in the file. The product match came from the customer, which is the most reliable matching there is.\u003C/p>\n\u003Cp>We put the market and the SKU into each link&#39;s title, in the form \u003Ccode>MARKET | SKU | shop domain\u003C/code>. That title travels with every row, so filtering by market or by the customer&#39;s SKU is a text filter in the portal or in Excel. Their own prices came from their price file, uploaded as an upload-file scraper into the same group, so their row and the competitor rows sit side by side.\u003C/p>\n\u003Ch2 id=\"first-attempt-let-the-ai-do-it\">First Attempt: Let the AI Do It\u003C/h2>\n\u003Cp>Our first version used AI extraction on every link. The schema asked for name, price, sale price, pack weight, and the price per 50 g.\u003C/p>\n\u003Cp>The price and currency came back fine. The price per 50 g did not. It was right on 18 of 69 yarn rows, about 26%, and all 18 were 50 g balls, where the right answer is the price itself. We checked every row. Not one held a real calculation. The model copied numbers from the page.\u003C/p>\n\u003Cp>The pack weight was worse than we expected too. It was wrong on 25 of 67 links, more than a third. It read a free-shipping threshold as the weight. It took &quot;about 450 g for a sweater&quot; from a pattern text. It wrote 0.05 where the shop gave kilograms. On pages that list several colourways with different weights, it took the wrong colourway.\u003C/p>\n\u003Cp>The full breakdown is in \u003Ca href=\"/blogs/llm-data-extraction-rules-do-the-maths-2026\">why AI should read the raw values and rules should do the maths\u003C/a>. The short lesson: an extraction model reads, and it reads well. It does not calculate, and a schema that asks it to will get a number that looks right.\u003C/p>\n\u003Caside class=\"article__inline-cta\">\u003Cp class=\"article__inline-cta-text\">Try ScrapeWise on your own URL. \u003Cstrong>Your first 5 requests are free.\u003C/strong>\u003C/p>\u003Ca class=\"article__inline-cta-btn\" href=\"https://portal.scrapewise.ai/login\" target=\"_blank\" rel=\"noopener\">Start Free →\u003C/a>\u003C/aside>\u003Ch2 id=\"second-attempt-structured-sources-then-rules\">Second Attempt: Structured Sources, Then Rules\u003C/h2>\n\u003Cp>We changed two things.\u003C/p>\n\u003Cp>\u003Cstrong>The source.\u003C/strong> For each shop we looked for a structured source before falling back to AI. Many of the shops turned out to run on Shopify, and Shopify has a public storefront JSON for every variant with the price, the before-price, the weight in grams, the SKU, the barcode and the stock state. Some ran WooCommerce, which has a public Store API. Others had \u003Ccode>itemprop\u003C/code> microdata in the page, which is a stable structured layer even when the design changes.\u003C/p>\n\u003Cp>Final mix on the links that return data:\u003C/p>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>Source type\u003C/th>\n\u003Cth>Share of links\u003C/th>\n\u003C/tr>\n\u003C/thead>\n\u003Ctbody>\u003Ctr>\n\u003Ctd>Shopify variant JSON\u003C/td>\n\u003Ctd>57%\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>HTML microdata or selectors\u003C/td>\n\u003Ctd>30%\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>WooCommerce Store API\u003C/td>\n\u003Ctd>2%\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>JSON-LD\u003C/td>\n\u003Ctd>1%\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>AI extraction (no structured weight found)\u003C/td>\n\u003Ctd>9%\u003C/td>\n\u003C/tr>\n\u003C/tbody>\u003C/table>\n\u003Cp>The AI links are the ones where no structured source had the weight. We kept AI there, with raw fields only.\u003C/p>\n\u003Cp>\u003Cstrong>The maths.\u003C/strong> The price per 50 g is now an after-scrape rule, not an AI field. Scrapewise has a rule called &quot;Price per unit&quot; that takes a price column and a pack-size column and rescales the price to a fixed pack size. The formula is the one you would write on paper:\u003C/p>\n\u003Cp>\u003Ccode>price_per_50g = price ÷ pack_grams × 50\u003C/code>\u003C/p>\n\u003Cp>Example: 2.60 for a 100 g ball gives 2.60 ÷ 100 × 50 = 1.30. On sale at 1.69 it gives 0.845.\u003C/p>\n\u003Cp>A second rule, &quot;Take a number from text&quot;, reads the number in front of a unit word, for example &quot;100 g&quot; in a product name, and can convert kg to g on the way. We used it where the weight only appears in text. A third rule, &quot;Convert currency&quot;, turns every per-50 g value into EUR with the European Central Bank \u003Ca href=\"https://www.ecb.europa.eu/stats/policy_and_exchange_rates/euro_reference_exchange_rates/html/index.en.html\">daily reference rate\u003C/a>, and writes the rate and its date next to it.\u003C/p>\n\u003Cp>After the switch the per-50 g value was right on 100% of yarn rows. Right here means it equals price ÷ pack × 50 to four decimals, and the pack matches the page.\u003C/p>\n\u003Ch2 id=\"details-that-matter-more-than-they-look\">Details That Matter More Than They Look\u003C/h2>\n\u003Cp>\u003Cstrong>Regular price and sale price.\u003C/strong> We use \u003Ccode>price\u003C/code> for the regular price and \u003Ccode>sale_price\u003C/code> for the price paid today, and \u003Ccode>sale_price\u003C/code> is filled only when it is lower than \u003Ccode>price\u003C/code>. Some shops show a &quot;before&quot; price equal to today&#39;s price. That is not a sale, so it stays empty. The reader&#39;s rule is short: today&#39;s price is \u003Ccode>sale_price\u003C/code> if it is filled, otherwise \u003Ccode>price\u003C/code>.\u003C/p>\n\u003Cp>\u003Cstrong>Empty means unknown.\u003C/strong> If the price or the weight is missing, the per-50 g value is left empty, not 0. A zero looks like a free product in a chart.\u003C/p>\n\u003Cp>\u003Cstrong>Accessories need their own unit.\u003C/strong> Stuffing is sold per 300 g bag, blocking mats per set of nine, a neck light per piece. Each accessory row has exactly one unit filled: per 100 g or per piece. Our first version let one row carry both, because the AI read &quot;40+ pcs in stock&quot; as a piece count.\u003C/p>\n\u003Cp>\u003Cstrong>The page can disagree with the customer&#39;s file.\u003C/strong> One cotton yarn is 226 g on the shop and 250 g in the customer&#39;s file. We use the shop&#39;s weight and flagged it.\u003C/p>\n\u003Cp>\u003Cstrong>Weights in ounces.\u003C/strong> US shops give their own gram figure, so a ball labelled 100 g can show as 99 g. The difference is under 1%, and we left the shop&#39;s number.\u003C/p>\n\u003Ch2 id=\"what-it-costs-to-run\">What It Costs to Run\u003C/h2>\n\u003Cp>A full run of the group costs about €0.015 at our \u003Ca href=\"/pricing\">page prices\u003C/a>, because every link is one plain page or one JSON call. It is scheduled weekly. Each run adds a new set of rows and keeps the old ones, so you get a price history per link.\u003C/p>\n\u003Cp>The structured sources were also the cheap ones. On the first Shopify links we moved, the JSON run cost about a ninth of the AI run on the same links.\u003C/p>\n\u003Cp>The same setup scales past a hand-picked list. The links sit in one link list per scraper, with the market and SKU in each title, and a bulk upload takes up to 100,000 URLs as JSON Lines in one go. A retailer with 50 competitor shops and tens of thousands of product links would use the same scrapers, the same titles and the same rules.\u003C/p>\n\u003Ch2 id=\"links-we-could-not-fix\">Links We Could Not Fix\u003C/h2>\n\u003Cp>About 5% of the links in the file give no data. The shop removed the product, or in one case the whole shop closed. No scraper setting fixes a product that no longer exists, so those go back to the customer for new links.\u003C/p>\n\u003Cp>If you want to build the same kind of comparison, the parts are all standard: an upload-file scraper for your own prices, one scraper per source type with your links in its link list, and after-scrape rules for per-unit, EUR and stock. You can also have an AI agent do the build for you through our MCP server, as described in \u003Ca href=\"/blogs/build-web-scraper-with-claude-mcp-2026\">build a web scraper with Claude and MCP\u003C/a>. For the managed route, see \u003Ca href=\"/use-cases/competitor-price-tracking\">competitor price tracking\u003C/a>.\u003C/p>\n",{"title":11,"description":12,"badge":13,"benefits":14},"Frequently asked questions","Price per unit comparison: questions answered","FAQ",[15,18,21,24,27],{"title":16,"description":17},"How do you compare prices when pack sizes differ?","Convert every price to the same unit. For yarn we used price per 50 g, calculated as price divided by pack weight in grams times 50. A 2.60 ball of 100 g is 1.30 per 50 g.",{"title":19,"description":20},"Can an AI scraper calculate the price per unit?","Not reliably. In our test the AI value was right on 18 of 69 rows, and only where the pack was already 50 g. Moving the calculation into an after-scrape rule made it right on every row.",{"title":22,"description":23},"What if the pack weight is missing?","Leave the price per unit empty. An empty value means unknown. A zero would look like a free product in any chart or average.",{"title":25,"description":26},"How do you handle sale prices?","Keep the regular price and the sale price in separate columns, and fill the sale price only when it is lower than the regular price. Today's price is the sale price if it is filled, otherwise the regular price.",{"title":28,"description":29},"How are prices in different currencies compared?","Each value is also converted to EUR with the European Central Bank daily reference rate, and the rate and its date are stored next to the value, so every conversion can be checked.","Price per Unit: Compare Prices Across Pack Sizes","A yarn retailer's competitors sell 25 g, 50 g, 100 g and 226 g balls. How we turned their competitor links into one comparable price per 50 g.","Raivo Kartau",null,"Pricing",7,[37,42,48],{"slug":38,"title":39,"image":40,"date":5,"category":34,"excerpt":41},"competitor-price-monitoring-case-study-2026","Competitor Price Monitoring Case Study: 100,000 SKUs, 88% Fewer Requests","/img/news/competitor-price-monitoring-case-study-2026.png","How we set up daily competitor price monitoring for a retailer in two EU markets: API first, HTML last, and 88% fewer requests per day.",{"slug":43,"title":44,"image":45,"date":46,"category":34,"excerpt":47},"price2spy-vs-wiser-price-monitoring-2026","Price2Spy vs Wiser: Which Price Monitoring Tool Should You Choose in 2026?","/img/news/price2spy-vs-wiser-price-monitoring-2026.png","24 Sep 2026","Price2Spy vs Wiser in 2026: which has the more comprehensive feature set, why refresh speed is worthless without match accuracy, and the 50-SKU test to run.",{"slug":49,"title":50,"image":51,"date":46,"category":34,"excerpt":52},"prisync-vs-competera-price-monitoring-2026","Prisync vs Competera: Which Pricing Tool Should You Choose in 2026?","/img/news/prisync-vs-competera-price-monitoring-2026.png","Prisync vs Competera in 2026: $99–$399/month published plans against a quoted enterprise contract, and the one question that decides it — rules or recommendations.",{"slug":54,"title":55},"web-scraping-mistakes-price-scrapers-2026","14 Web Scraping Mistakes We Made Building Price Scrapers",{"slug":57,"title":58},"llm-data-extraction-rules-do-the-maths-2026","LLM Data Extraction: Let the AI Read, Let Rules Do the Maths",[60,64,67,70,73,76,79],{"level":61,"text":62,"id":63},2,"Why Sticker Prices Mislead","why-sticker-prices-mislead",{"level":61,"text":65,"id":66},"Only the Customer&#39;s Links, Nothing Else","only-the-customer39s-links-nothing-else",{"level":61,"text":68,"id":69},"First Attempt: Let the AI Do It","first-attempt-let-the-ai-do-it",{"level":61,"text":71,"id":72},"Second Attempt: Structured Sources, Then Rules","second-attempt-structured-sources-then-rules",{"level":61,"text":74,"id":75},"Details That Matter More Than They Look","details-that-matter-more-than-they-look",{"level":61,"text":77,"id":78},"What It Costs to Run","what-it-costs-to-run",{"level":61,"text":80,"id":81},"Links We Could Not Fix","links-we-could-not-fix",[],1790769637106]