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:
- Content Script - Extracts price data from web pages
- Background Service Worker - Handles data storage and notifications
- 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.
Related Reading
- Price Tracker Chrome Extension for Amazon: A Developer Guide
- Chrome Extension Open Graph Preview: Implementation Guide
- Chrome Extension Price Per Unit Calculator: A Practical.
Built by theluckystrike. More at zovo.one
Step-by-Step: Tracking Your First Product
- Navigate to any product page on a supported retailer
- Click the extension icon. the popup detects the product from URL and page metadata
- Click “Track Price” to save the current price to local storage
- The background alarm checks prices every 6-12 hours and adds new data points
- When the price drops below your alert threshold, a Chrome notification fires
- 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.