Price History Chrome Extension Guide (2026)

Building a price history Chrome extension gives you the power to track and visualize price changes across any e-commerce site. Unlike basic price trackers that only show current prices, a price history extension stores historical data, allowing users to identify trends, spot seasonal discounts, and make informed purchasing decisions.

This guide covers the technical implementation for developers and power users who want to build custom price tracking solutions.

Core Architecture

A price history extension consists of three main components:

  1. Content Script - Extracts price data from web pages
  2. Background Service Worker - Handles data storage and notifications
  3. Popup UI - Displays price history charts and configuration

The extension uses Chrome’s storage API to persist price data locally. For larger datasets, consider IndexedDB for better query performance.

Extracting Price Data

Price extraction varies significantly across e-commerce platforms. A solid implementation handles multiple price formats and selectors. Here is a content script pattern for extracting prices:

// content-script.js
class PriceExtractor {
 constructor() {
 this.selectors = {
 mainPrice: [
 '.price', 
 '[data-price]', 
 '.product-price',
 '.price-current'
 ],
 originalPrice: [
 '.price-was',
 '.price-original',
 '.strike-through'
 ],
 salePrice: [
 '.price-sale',
 '.sale-price'
 ]
 };
 }
 extract() {
 const data = {
 url: window.location.href,
 productName: this.extractProductName(),
 currentPrice: this.extractPrice(this.selectors.mainPrice),
 originalPrice: this.extractPrice(this.selectors.originalPrice),
 salePrice: this.extractPrice(this.selectors.salePrice),
 currency: this.detectCurrency(),
 timestamp: Date.now()
 };
 
 return this.validateData(data) ? data : null;
 }
 extractPrice(selectors) {
 for (const selector of selectors) {
 const element = document.querySelector(selector);
 if (element) {
 const text = element.textContent.trim();
 const price = this.parsePrice(text);
 if (price !== null) return price;
 }
 }
 return null;
 }
 parsePrice(text) {
 // Handle various price formats: $19.99, €19,99, £19.99, etc.
 const match = text.match(/[\d,]+\.?\d*/);
 if (!match) return null;
 
 let price = match[0].replace(/,/g, '');
 return parseFloat(price);
 }
 detectCurrency() {
 const currencyEl = document.querySelector('[data-currency], .currency');
 if (currencyEl) return currencyEl.dataset.currency || currencyEl.textContent;
 
 // Fallback: detect from page locale or body class
 return 'USD';
 }
 extractProductName() {
 const titleEl = document.querySelector('h1, [data-product-title]');
 return titleEl?.textContent.trim() || 
 document.title.split('|')[0].trim();
 }
 validateData(data) {
 return data.currentPrice !== null && 
 data.currentPrice > 0 &&
 data.productName.length > 0;
 }
}

Data Storage Strategy

Chrome Extensions offer multiple storage options. For price history, you need to balance storage limits with query performance:

Storage Type Capacity Best For
chrome.storage.local 5MB Small to medium datasets
chrome.storage.sync 100KB User preferences only
IndexedDB 50%+ of disk Large price histories

A practical storage implementation:

// background/priceStore.js
const DB_NAME = 'PriceHistoryDB';
const STORE_NAME = 'prices';
class PriceStore {
 constructor() {
 this.db = null;
 }
 async init() {
 return new Promise((resolve, reject) => {
 const request = indexedDB.open(DB_NAME, 1);
 
 request.onerror = () => reject(request.error);
 request.onsuccess = () => {
 this.db = request.result;
 resolve();
 };
 
 request.onupgradeneeded = (event) => {
 const db = event.target.result;
 if (!db.objectStoreNames.contains(STORE_NAME)) {
 const store = db.createObjectStore(STORE_NAME, { 
 keyPath: 'id', 
 autoIncrement: true 
 });
 store.createIndex('url', 'url', { unique: false });
 store.createIndex('timestamp', 'timestamp', { unique: false });
 }
 };
 });
 }
 async savePrice(priceData) {
 return new Promise((resolve, reject) => {
 const transaction = this.db.transaction([STORE_NAME], 'readwrite');
 const store = transaction.objectStore(STORE_NAME);
 
 // Attach unique identifier based on URL
 priceData.urlHash = this.hashURL(priceData.url);
 priceData.id = `${priceData.urlHash}_${priceData.timestamp}`;
 
 const request = store.put(priceData);
 request.onsuccess = () => resolve(request.result);
 request.onerror = () => reject(request.error);
 });
 }
 async getPriceHistory(url, options = {}) {
 const { limit = 100, days = 90 } = options;
 const cutoff = Date.now() - (days * 24 * 60 * 60 * 1000);
 
 return new Promise((resolve, reject) => {
 const transaction = this.db.transaction([STORE_NAME], 'readonly');
 const store = transaction.objectStore(STORE_NAME);
 const index = store.index('url');
 
 const results = [];
 const request = index.openCursor(IDBKeyRange.only(url));
 
 request.onsuccess = (event) => {
 const cursor = event.target.result;
 if (cursor && results.length < limit) {
 if (cursor.value.timestamp >= cutoff) {
 results.push(cursor.value);
 }
 cursor.continue();
 } else {
 resolve(results.sort((a, b) => a.timestamp - b.timestamp));
 }
 };
 
 request.onerror = () => reject(request.error);
 });
 }
 hashURL(url) {
 // Simple hash for URL identification
 let hash = 0;
 for (let i = 0; i < url.length; i++) {
 const char = url.charCodeAt(i);
 hash = ((hash << 5) - hash) + char;
 hash = hash & hash;
 }
 return Math.abs(hash).toString(36);
 }
}

Message Passing System

Content scripts communicate with the background script using message passing:

// content-script.js - sending price data
async function reportPrice() {
 const extractor = new PriceExtractor();
 const priceData = extractor.extract();
 
 if (priceData) {
 try {
 await chrome.runtime.sendMessage({
 action: 'savePrice',
 data: priceData
 });
 console.log('Price saved:', priceData.currentPrice);
 } catch (error) {
 console.error('Failed to save price:', error);
 }
 }
}
// Run on page load and periodically for dynamic prices
reportPrice();
setInterval(reportPrice, 60000);
// background/service-worker.js
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
 if (message.action === 'savePrice') {
 handleSavePrice(message.data);
 } else if (message.action === 'getHistory') {
 handleGetHistory(message.url).then(sendResponse);
 return true; // async response
 }
});
async function handleSavePrice(priceData) {
 const store = new PriceStore();
 await store.init();
 await store.savePrice(priceData);
}
async function handleGetHistory(url) {
 const store = new PriceStore();
 await store.init();
 return await store.getPriceHistory(url);
}

Price History Visualization

The popup UI should display historical data as a chart. Using a lightweight charting library:

// popup/chart.js
function renderPriceChart(prices, containerId) {
 const ctx = document.getElementById(containerId);
 
 const labels = prices.map(p => new Date(p.timestamp).toLocaleDateString());
 const data = prices.map(p => p.currentPrice);
 
 new Chart(ctx, {
 type: 'line',
 data: {
 labels: labels,
 datasets: [{
 label: 'Price History',
 data: data,
 borderColor: '#4CAF50',
 backgroundColor: 'rgba(76, 175, 80, 0.1)',
 fill: true,
 tension: 0.3
 }]
 },
 options: {
 responsive: true,
 plugins: {
 legend: { display: false },
 tooltip: {
 callbacks: {
 label: (context) => `$${context.raw.toFixed(2)}`
 }
 }
 },
 scales: {
 y: {
 beginAtZero: false,
 title: { display: true, text: 'Price' }
 }
 }
 }
 });
}

Extension Manifest Configuration

Your manifest.json needs proper permissions:

{
 "manifest_version": 3,
 "name": "Price History Tracker",
 "version": "1.0.0",
 "permissions": [
 "storage",
 "activeTab",
 "scripting"
 ],
 "host_permissions": [
 "*://*.amazon.com/*",
 "*://*.walmart.com/*",
 "*://*.target.com/*"
 ],
 "background": {
 "service_worker": "background/service-worker.js"
 },
 "content_scripts": [{
 "matches": ["*://*/*"],
 "js": ["content-script.js"],
 "run_at": "document_idle"
 }],
 "action": {
 "default_popup": "popup/popup.html",
 "default_icon": "icons/icon.png"
 }
}

Privacy Considerations

When building price tracking extensions, respect user privacy:

  • Store only necessary price data, not personal information
  • Provide clear data export and deletion options
  • Avoid sending browsing data to external servers without consent
  • Use HTTPS for any network requests
  • Consider adding a “do not track” mode

Deployment and Testing

Test your extension thoroughly across different e-commerce sites. Use Chrome’s built-in testing features:

Load unpacked extension for testing
1. Navigate to chrome://extensions
2. Enable "Developer mode"
3. Click "Load unpacked"
4. Select your extension directory

Monitor console logs in both the popup and service worker for debugging. Use Chrome’s Storage Inspector to view persisted price data during development.

Building a price history extension requires handling diverse price formats, managing storage efficiently, and creating useful visualizations. The implementation above provides a solid foundation that you can customize for specific retailers or add features like price drop alerts and shopping lists.


Try it: Paste your error into our Error Diagnostic for an instant fix.

I'm a solo developer in Vietnam. 50K Chrome extension users. $500K+ on Upwork. 5 Claude Max subscriptions running agent fleets in parallel. These are my actual CLAUDE.md templates, orchestration configs, and prompts. Not a course. Not theory. The files I copy into every project before I write a line of code. **[See what's inside →](https://zovo.one/lifetime?utm_source=ccg&utm_medium=cta-default&utm_campaign=price-history-chrome-extension)** $99 once. Free forever. 47/500 founding spots left.

Related Reading

Built by theluckystrike. More at zovo.one

Step-by-Step: Tracking Your First Product

  1. Navigate to any product page on a supported retailer
  2. Click the extension icon. the popup detects the product from URL and page metadata
  3. Click “Track Price” to save the current price to local storage
  4. The background alarm checks prices every 6-12 hours and adds new data points
  5. When the price drops below your alert threshold, a Chrome notification fires
  6. View the price history chart in the popup at any time

Advanced: Cross-Retailer Price Comparison

Show prices from multiple retailers side by side:

async function findCrossRetailerPrices(productTitle) {
 const results = await Promise.allSettled([
 fetch(`https://www.cheapshark.com/api/1.0/games?title=${encodeURIComponent(productTitle)}`).then(r => r.json())
 ]);
 return results.filter(r => r.status === 'fulfilled').flatMap(r => r.value).sort((a, b) => a.price - b.price);
}

Comparison with Established Price Tools

Tool Retailers Price history Integration Cost
This extension Configurable Full control Deep (content script) Free to build
Honey 30,000+ Limited Excellent Free
CamelCamelCamel Amazon only Excellent charts Website only Free
Keepa Amazon only Excellent Extension available Free/Premium

Troubleshooting Common Issues

Price not extracting correctly: Build a solid parser for varied price formats:

function parsePrice(text) {
 if (!text) return null;
 const normalized = text.replace(/[^\d.,]/g, '');
 if (normalized.match(/,\d{2}$/)) return parseFloat(normalized.replace('.','').replace(',','.'));
 return parseFloat(normalized.replace(',',''));
}

Storage quota hit: Use IndexedDB for price history and keep only the last 90 days by default. Let users configure the retention period in settings.

Chart not rendering: Chart.js requires the canvas element to be in the DOM when instantiated. Verify the canvas exists before calling new Chart().

Building a price history extension requires handling diverse price formats, managing storage efficiently, and creating useful visualizations.

Know your costs → Use our Claude Code Cost Calculator to estimate your monthly spend.