Price Comparison Chrome (2026)

Building a Price Comparison Chrome Extension: A Developer’s Guide

Price tracking and comparison tools have become essential for online shoppers and developers building e-commerce platforms. Chrome extensions offer a powerful way to automate price comparisons directly in the browser, providing real-time alerts and historical data without requiring users to visit separate websites.

This guide walks through building a price comparison Chrome extension using modern APIs and best practices.

Understanding the Architecture

A price comparison extension typically consists of three main components:

  1. Content scripts that run on e-commerce pages and extract product information
  2. Background scripts that handle data storage, API calls, and cross-tab coordination
  3. Popup UI for quick access to tracked products and settings

With Chrome’s Manifest V3, background scripts have shifted to service workers, which introduces some architectural considerations around persistence and state management.

Project Setup

Create your extension directory structure:

price-tracker/
 manifest.json
 background.js
 content.js
 popup/
 popup.html
 popup.js
 styles.css
 icons/
 icon16.png
 icon48.png
 icon128.png

Manifest Configuration

The manifest.json defines your extension’s capabilities:

{
 "manifest_version": 3,
 "name": "Price Tracker",
 "version": "1.0",
 "description": "Track and compare prices across e-commerce sites",
 "permissions": [
 "storage",
 "activeTab",
 "scripting"
 ],
 "host_permissions": [
 "https://*.amazon.com/*",
 "https://*.ebay.com/*",
 "https://*.walmart.com/*"
 ],
 "background": {
 "service_worker": "background.js"
 },
 "content_scripts": [{
 "matches": ["https://*.amazon.com/*", "https://*.ebay.com/*"],
 "js": ["content.js"]
 }],
 "action": {
 "default_popup": "popup/popup.html",
 "default_icon": {
 "48": "icons/icon48.png"
 }
 }
}

Note the host_permissions array. In Manifest V3, you must explicitly declare which domains your extension can access. This is crucial for price comparison extensions that need to read product pages from multiple retailers.

Extracting Product Data with Content Scripts

Content scripts run in the context of web pages, giving you access to the DOM. Here’s how to extract product information from a typical e-commerce page:

// content.js
function extractProductData() {
 const selectors = {
 title: '[data-feature-name="title"], .product-title, h1[itemprop="name"]',
 price: '[data-feature-name="priceblock_ourprice"], .price-current, [itemprop="price"]',
 currency: '[itemprop="priceCurrency"]',
 availability: '[data-feature-name="availability"], .availability-status'
 };
 const getText = (selector) => {
 const el = document.querySelector(selector);
 return el ? el.textContent.trim() : null;
 };
 const getAttribute = (selector, attr) => {
 const el = document.querySelector(selector);
 return el ? el.getAttribute(attr) : null;
 };
 return {
 url: window.location.href,
 title: getText(selectors.title),
 price: getText(selectors.price),
 currency: getAttribute(selectors.currency, 'content') || 'USD',
 availability: getText(selectors.availability),
 timestamp: new Date().toISOString()
 };
}
// Listen for messages from popup or background
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
 if (request.action === 'getProductData') {
 const data = extractProductData();
 sendResponse(data);
 }
});

This script handles multiple selectors since different retailers use varying class names and data attributes.

Background Service Worker

The background service worker manages the extension’s core logic:

// background.js
const PRICE_HISTORY_KEY = 'price_history';
// Store price data
async function storePriceData(productUrl, productData) {
 const result = await chrome.storage.local.get(PRICE_HISTORY_KEY);
 const history = result[PRICE_HISTORY_KEY] || {};
 
 if (!history[productUrl]) {
 history[productUrl] = {
 currentPrice: productData.price,
 lowestPrice: productData.price,
 highestPrice: productData.price,
 prices: [],
 title: productData.title,
 lastUpdated: productData.timestamp
 };
 } else {
 const product = history[productUrl];
 product.prices.push({
 price: productData.price,
 timestamp: productData.timestamp
 });
 product.currentPrice = productData.price;
 product.lowestPrice = Math.min(product.lowestPrice, parsePrice(productData.price));
 product.highestPrice = Math.max(product.highestPrice, parsePrice(productData.price));
 product.lastUpdated = productData.timestamp;
 }
 
 await chrome.storage.local.set({ [PRICE_HISTORY_KEY]: history });
}
function parsePrice(priceString) {
 const numericMatch = priceString.match(/[\d,]+\.?\d*/);
 return numericMatch ? parseFloat(numericMatch[0].replace(',', '')) : 0;
}
// Handle messages from content scripts and popup
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
 if (request.action === 'saveProduct') {
 storePriceData(request.url, request.productData)
 .then(() => sendResponse({ success: true }))
 .catch(err => sendResponse({ success: false, error: err.message }));
 return true;
 }
 
 if (request.action === 'getHistory') {
 chrome.storage.local.get(PRICE_HISTORY_KEY)
 .then(result => sendResponse(result[PRICE_HISTORY_KEY] || {}))
 .catch(err => sendResponse({ error: err.message }));
 return true;
 }
});

The popup provides quick access to tracked products:

<!-- popup/popup.html -->
<!DOCTYPE html>
<html>
<head>
 <link rel="stylesheet" href="../styles.css">
</head>
<body>
 <div class="popup-container">
 <h2>Price Tracker</h2>
 <div id="tracked-products"></div>
 <button id="track-current">Track This Product</button>
 </div>
 <script src="popup.js"></script>
</body>
</html>
// popup/popup.js
document.addEventListener('DOMContentLoaded', () => {
 loadTrackedProducts();
 
 document.getElementById('track-current').addEventListener('click', async () => {
 const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
 
 chrome.tabs.sendMessage(tab.id, { action: 'getProductData' }, async (productData) => {
 if (productData && productData.price) {
 await chrome.runtime.sendMessage({
 action: 'saveProduct',
 url: productData.url,
 productData: productData
 });
 loadTrackedProducts();
 }
 });
 });
});
async function loadTrackedProducts() {
 const result = await chrome.runtime.sendMessage({ action: 'getHistory' });
 const container = document.getElementById('tracked-products');
 
 if (!result || Object.keys(result).length === 0) {
 container.innerHTML = '<p>No products tracked yet</p>';
 return;
 }
 
 container.innerHTML = Object.entries(result).map(([url, data]) => `
 <div class="product-card">
 <h3>${data.title || 'Unknown Product'}</h3>
 <p class="current-price">Current: ${data.currentPrice}</p>
 <p class="price-range">
 Low: ${data.lowestPrice} | High: ${data.highestPrice}
 </p>
 </div>
 `).join('');
}

Advanced Features

Once you have the basics working, consider adding:

Price Drop Notifications: Use the Chrome Notifications API to alert users when prices fall below their target:

async function checkPriceAlert(productUrl, targetPrice) {
 const history = await chrome.storage.local.get(PRICE_HISTORY_KEY);
 const product = history[PRICE_HISTORY_KEY][productUrl];
 
 if (parsePrice(product.currentPrice) <= targetPrice) {
 chrome.notifications.create({
 type: 'basic',
 iconUrl: 'icons/icon48.png',
 title: 'Price Drop Alert!',
 message: `${product.title} is now ${product.currentPrice}`
 });
 }
}

Cross-Platform Sync: Implement cloud storage to sync price history across devices using your own backend or services like Firebase.

Analytics Dashboard: Create a dedicated extension page showing price trends with charts using a library like Chart.js.

Best Practices

When building production-ready price comparison extensions:

  • Respect rate limits: Implement throttling when fetching page data to avoid triggering anti-bot measures
  • Handle dynamic content: Many e-commerce sites use JavaScript to render prices, consider using MutationObserver or waiting for specific elements
  • Clean your selectors: Retailers frequently change their page structure, build in flexibility with fallback selectors
  • Storage management: Chrome storage has quotas, implement cleanup of old price history entries

Building a price comparison extension combines DOM manipulation, Chrome APIs, and data management into a practical project that demonstrates real-world extension development patterns.

Step-by-Step: Comparing Prices on a Product Page

  1. Navigate to any product page on Amazon, Walmart, Target, or another supported retailer
  2. Click the extension icon. the popup extracts the product name and current price
  3. The background script searches configured comparison APIs for the same product
  4. Results display in the popup sorted by price ascending with store names and links
  5. Click “Set Alert” to get notified when any retailer’s price drops further
  6. Click any result row to open that store’s product page directly

Advanced: Structured Data Extraction

Many retailers embed pricing in JSON-LD structured data, which is far more reliable than DOM selectors:

function extractFromStructuredData() {
 const scripts = document.querySelectorAll('script[type="application/ld+json"]');
 for (const script of scripts) {
 try {
 const data = JSON.parse(script.textContent);
 const product = Array.isArray(data) ? data.find(d => d['@type'] === 'Product') : data;
 if (product?.offers) {
 const offer = Array.isArray(product.offers) ? product.offers[0] : product.offers;
 return { price: parseFloat(offer.price), currency: offer.priceCurrency, name: product.name };
 }
 } catch {}
 }
 return null;
}

This approach survives most retailer DOM changes because structured data is part of the SEO contract. retailers update it more carefully than their visual HTML.

Comparison with Alternative Approaches

Approach Coverage Setup Maintenance Cost
This extension Customizable Build yourself Regular (DOM changes) Free
Google Shopping Massive Zero Google maintains it Free
PriceGrabber Many retailers Website only Service maintains it Free
Shopzilla Many retailers Website only Service maintains it Free

The extension wins for users who want comparisons inline while browsing a specific retailer, without navigating away to a comparison site.

Best Practices

  • Respect rate limits: Implement throttling when fetching price data to avoid triggering anti-bot measures
  • Handle dynamic content: Many e-commerce sites render prices via JavaScript. use MutationObserver or wait for specific elements before extracting
  • Clean your selectors: Retailers frequently change their page structure. build in fallback selectors and test regularly
  • Storage management: Chrome storage has quotas. implement cleanup of old price history entries

Troubleshooting Common Issues

Price shows wrong currency: Detect the page currency from the price element’s itemprop="priceCurrency" attribute or from the page’s <html lang> and accept-language patterns.

Comparison popup too wide or narrow: Constrain the popup width in CSS and use a scrollable list for results so the popup size does not depend on the number of results.

Multiple prices for the same product (variants): Extract the specific variant price, not the default. Look for selected state in size/color selectors and use the offers array in structured data to find the matching variant offer.

Building a price comparison extension demonstrates real-world extension development patterns: DOM manipulation, Chrome APIs, data management, and external API integration.


Try it: Estimate your monthly spend with our Cost Calculator.

I've tried them all. Claude Code wins — but only if you set it up right. The gap isn't the tool. It's the CLAUDE.md, the prompts, the workflow. I run 5 Claude Max subscriptions in parallel with autonomous agent fleets. These are my actual configs — the ones that let a solo dev outproduce a small team. **[See the full setup →](https://zovo.one/lifetime?utm_source=ccg&utm_medium=cta-compare&utm_campaign=price-comparison-chrome-extension)** $99. Once. Everything I use to ship.

Related Reading

Built by theluckystrike. More at zovo.one

Get started → Generate your project setup with our Project Starter.