Claude Skills Guide

Open box deals represent one of the best ways to save money on electronics, but manually checking multiple retailers for price drops quickly becomes tedious. A custom chrome extension deal tracker automates this process, notifying you the moment prices drop on items you’re watching. This guide walks through building a functional open box deal tracker extension from scratch.

Understanding the Deal Tracker Architecture

A deal tracker chrome extension consists of several interconnected components working together. The popup interface allows users to add products and configure alerts. Background scripts periodically fetch prices and check for changes. Storage synchronization keeps your watchlist accessible across devices through Chrome’s sync storage API.

The core workflow follows a simple pattern: you add a product URL, the extension periodically visits that page to extract current pricing, compares it against your target price, and sends a notification when conditions are met. This requires understanding HTML parsing, scheduled tasks, and the Chrome notifications API.

Setting Up the Manifest

Every Chrome extension begins with the manifest file. For a deal tracker using Manifest V3, you’ll configure permissions for storage, notifications, and the ability to run in the background:

// manifest.json
{
  "manifest_version": 3,
  "name": "Open Box Deal Tracker",
  "version": "1.0",
  "description": "Track open box deals and price drops across retailers",
  "permissions": [
    "storage",
    "notifications",
    "activeTab",
    "scripting"
  ],
  "background": {
    "service_worker": "background.js"
  },
  "action": {
    "default_popup": "popup.html",
    "default_icon": {
      "16": "icons/icon16.png",
      "48": "icons/icon48.png",
      "128": "icons/icon128.png"
    }
  },
  "host_permissions": [
    "*://*.amazon.com/*",
    "*://*.bestbuy.com/*",
    "*://*.walmart.com/*",
    "*://*.newegg.com/*"
  ]
}

The host_permissions array specifies which domains your extension can actively scrape for pricing data. Each retailer requires specific pattern matching to handle their unique URL structures.

Building the Popup Interface

The popup provides the user interface for managing your watchlist. Users need to add product URLs, set target prices, and see current status at a glance:

<!-- popup.html -->
<!DOCTYPE html>
<html>
<head>
  <style>
    body { width: 320px; padding: 16px; font-family: system-ui; }
    .input-group { margin-bottom: 12px; }
    input { width: 100%; padding: 8px; margin-top: 4px; box-sizing: border-box; }
    button { background: #4CAF50; color: white; border: none; padding: 10px; width: 100%; cursor: pointer; }
    button:hover { background: #45a049; }
    .deal-item { border: 1px solid #ddd; padding: 10px; margin: 8px 0; border-radius: 4px; }
    .deal-item .price { font-weight: bold; color: #d32f2f; }
    .deal-item .target { color: #388e3c; }
    .remove-btn { background: #f44336; margin-top: 4px; font-size: 12px; padding: 4px; }
  </style>
</head>
<body>
  <h3>Open Box Deal Tracker</h3>
  <div class="input-group">
    <label>Product URL</label>
    <input type="url" id="productUrl" placeholder="https://...">
  </div>
  <div class="input-group">
    <label>Target Price ($)</label>
    <input type="number" id="targetPrice" placeholder="299.99">
  </div>
  <button id="addDeal">Add to Watchlist</button>
  <div id="watchlist"></div>
  <script src="popup.js"></script>
</body>
</html>

The popup JavaScript handles adding deals to storage and rendering the current watchlist:

// popup.js
document.addEventListener('DOMContentLoaded', () => {
  loadWatchlist();
  
  document.getElementById('addDeal').addEventListener('click', async () => {
    const url = document.getElementById('productUrl').value;
    const targetPrice = parseFloat(document.getElementById('targetPrice').value);
    
    if (!url || !targetPrice) {
      alert('Please enter both URL and target price');
      return;
    }
    
    const deal = {
      id: Date.now(),
      url,
      targetPrice,
      addedAt: new Date().toISOString(),
      retailer: detectRetailer(url)
    };
    
    const { deals = [] } = await chrome.storage.sync.get('deals');
    deals.push(deal);
    await chrome.storage.sync.set({ deals });
    
    document.getElementById('productUrl').value = '';
    document.getElementById('targetPrice').value = '';
    loadWatchlist();
  });
});

function detectRetailer(url) {
  const retailers = {
    'amazon.com': 'Amazon',
    'bestbuy.com': 'Best Buy',
    'walmart.com': 'Walmart',
    'newegg.com': 'Newegg'
  };
  
  for (const [domain, name] of Object.entries(retailers)) {
    if (url.includes(domain)) return name;
  }
  return 'Unknown';
}

async function loadWatchlist() {
  const { deals = [] } = await chrome.storage.sync.get('deals');
  const container = document.getElementById('watchlist');
  
  container.innerHTML = deals.map(deal => `
    <div class="deal-item">
      <div><strong>${deal.retailer}</strong></div>
      <div class="target">Target: $${deal.targetPrice}</div>
      <button class="remove-btn" data-id="${deal.id}">Remove</button>
    </div>
  `).join('');
  
  container.querySelectorAll('.remove-btn').forEach(btn => {
    btn.addEventListener('click', async (e) => {
      const id = parseInt(e.target.dataset.id);
      const updated = deals.filter(d => d.id !== id);
      await chrome.storage.sync.set({ deals: updated });
      loadWatchlist();
    });
  });
}

Implementing Price Scraping

The background service worker handles periodic price checks. This requires retailer-specific parsing logic since each site structures their pricing data differently:

// background.js
const CHECK_INTERVAL = 30 * 60 * 1000; // 30 minutes

chrome.runtime.installed.addEventListener(() => {
  startPriceChecker();
  setInterval(startPriceChecker, CHECK_INTERVAL);
});

async function startPriceChecker() {
  const { deals = [] } = await chrome.storage.sync.get('deals');
  
  for (const deal of deals) {
    try {
      const currentPrice = await scrapePrice(deal.url, deal.retailer);
      
      if (currentPrice && currentPrice <= deal.targetPrice) {
        sendNotification(deal, currentPrice);
      }
      
      // Update stored price
      deal.lastChecked = new Date().toISOString();
      deal.lastPrice = currentPrice;
    } catch (error) {
      console.error(`Failed to check ${deal.url}:`, error);
    }
  }
  
  await chrome.storage.sync.set({ deals });
}

async function scrapePrice(url, retailer) {
  // Inject content script to extract price
  const [tab] = await chrome.tabs.create({ url, active: false });
  
  await new Promise(resolve => setTimeout(resolve, 2000)); // Wait for page load
  
  const results = await chrome.tabs.executeScript(tab.id, {
    code: `(${getPriceExtractor('${retailer}')})()`
  });
  
  chrome.tabs.remove(tab.id);
  return results[0];
}

function getPriceExtractor(retailer) {
  const extractors = {
    'Amazon': () => {
      const el = document.querySelector('.a-price-whole') || 
                 document.querySelector('#priceblock_ourprice') ||
                 document.querySelector('.a-offscreen');
      return el ? el.textContent.replace(/[^0-9.]/g, '') : null;
    },
    'Best Buy': () => {
      const el = document.querySelector('.priceView-customer-price span') ||
                 document.querySelector('[data-price]');
      return el ? el.textContent.replace(/[^0-9.]/g, '') : null;
    },
    'Walmart': () => {
      const el = document.querySelector('[itemprop="price"]') ||
                 document.querySelector('.price-characteristic');
      return el ? el.textContent.replace(/[^0-9.]/g, '') : null;
    },
    'Newegg': () => {
      const el = document.querySelector('.price-current') ||
                 document.querySelector('.price');
      return el ? el.textContent.replace(/[^0-9.]/g, '') : null;
    }
  };
  
  return extractors[retailer] || (() => null);
}

function sendNotification(deal, currentPrice) {
  chrome.notifications.create({
    type: 'basic',
    iconUrl: 'icons/icon128.png',
    title: 'Price Drop Alert!',
    message: `$${currentPrice} - Below your target of $${deal.targetPrice}`
  });
}

Handling Rate Limiting and Errors

Production extensions need robust error handling. Retailers frequently block automated requests, requiring you to implement backoff strategies and respect robots.txt guidelines. Consider adding random delays between checks and rotating user agents to avoid detection.

For more reliable scraping, you might integrate a scraping API service that handles the complexity of bypassing anti-bot measures. Services like ScraperAPI or ScrapingBee provide proxy rotation and JavaScript rendering, though they introduce ongoing costs.

Advanced Features to Consider

Once you have the basics working, several enhancements improve the user experience. Price history tracking shows graphs of price changes over time, helping users identify the best buying moments. Multiple alert thresholds let you track both “buy now” and “watch for better” prices.

Open box specific filters help identify merchandise conditions. Many retailers tag open box items differently, allowing you to focus specifically on discounted returns and display models. Adding condition badges to your watchlist display keeps this information visible.

Email notifications extend reach beyond browser notifications. Using the Chrome identity API with OAuth, you can integrate with email services to send alerts when you’re away from your computer.

Extension Deployment

Before publishing to the Chrome Web Store, test thoroughly across multiple retailers and price scenarios. Ensure your extension handles missing pricing data gracefully and doesn’t spam notifications when prices fluctuate slightly around your target threshold.

Prepare store listing assets including screenshots showing the popup interface and notification examples. The review process typically takes a few days, though extensions with broad permissions may require additional review time.

Building your own open box deal tracker gives you complete control over which retailers to monitor, how often to check, and exactly when to receive alerts. The investment in building this tool pays dividends through years of automated savings on electronics purchases.

Built by theluckystrike — More at zovo.one