Build an E-commerce Price Monitoring System with Zapserp
Stay competitive in the e-commerce landscape by monitoring competitor prices automatically. This guide shows you how to build a comprehensive price tracking system that monitors products across multiple platforms and sends alerts when opportunities arise.
What We're Building
A smart price monitoring system that:
- Tracks competitor prices across multiple sites
- Monitors product availability and stock status
- Sends alerts when prices drop below thresholds
- Generates competitive analysis reports
- Supports multiple product categories
Core Price Monitoring Engine
const { Zapserp } = require('zapserp')
class PriceMonitor {
constructor(apiKey) {
this.zapserp = new Zapserp({ apiKey })
this.priceHistory = new Map()
this.alertThresholds = new Map()
}
async trackProduct(productName, targetPrice = null) {
const searchQueries = [
`${productName} buy online price`,
`${productName} shop compare prices`,
`${productName} best deal discount`
]
const allPrices = []
for (const query of searchQueries) {
try {
const searchResults = await this.zapserp.search({
query,
engines: ['google', 'bing'],
limit: 10,
country: 'us'
})
// Filter for e-commerce sites
const ecommerceResults = this.filterEcommerceSites(searchResults.results)
if (ecommerceResults.length > 0) {
const urls = ecommerceResults.slice(0, 5).map(result => result.url)
const contentResults = await this.zapserp.readerBatch({ urls })
contentResults.results.forEach(content => {
if (content && content.content) {
const priceData = this.extractPriceData(content)
if (priceData.price > 0) {
allPrices.push({
...priceData,
source: this.extractStoreName(content.url),
url: content.url,
timestamp: new Date().toISOString(),
productName
})
}
}
})
}
} catch (error) {
console.error(`Price tracking failed for query: ${query}`, error)
}
}
// Update price history
const currentPrices = this.deduplicateByStore(allPrices)
this.updatePriceHistory(productName, currentPrices)
// Check for alerts
if (targetPrice) {
this.checkPriceAlerts(productName, currentPrices, targetPrice)
}
return {
product: productName,
currentPrices: currentPrices.sort((a, b) => a.price - b.price),
lowestPrice: Math.min(...currentPrices.map(p => p.price)),
averagePrice: this.calculateAverage(currentPrices.map(p => p.price)),
priceRange: {
min: Math.min(...currentPrices.map(p => p.price)),
max: Math.max(...currentPrices.map(p => p.price))
},
totalStores: currentPrices.length
}
}
filterEcommerceSites(results) {
const ecommerceDomains = [
'amazon.com', 'ebay.com', 'walmart.com', 'target.com',
'bestbuy.com', 'costco.com', 'homedepot.com', 'lowes.com',
'newegg.com', 'bhphotovideo.com', 'adorama.com', 'etsy.com',
'aliexpress.com', 'shopify.com', 'overstock.com'
]
return results.filter(result =>
ecommerceDomains.some(domain => result.url.includes(domain)) ||
this.containsPriceIndicators(result.snippet)
)
}
containsPriceIndicators(text) {
const priceIndicators = ['$', 'price:', 'buy now', 'add to cart', 'in stock', 'free shipping']
return priceIndicators.some(indicator =>
text.toLowerCase().includes(indicator.toLowerCase())
)
}
extractPriceData(content) {
const text = content.content
const title = content.title
// Multiple price extraction patterns
const pricePatterns = [
/\$(\d{1,3}(?:,\d{3})*(?:\.\d{2})?)/g,
/USD\s*(\d{1,3}(?:,\d{3})*(?:\.\d{2})?)/gi,
/Price[:\s]*\$?(\d{1,3}(?:,\d{3})*(?:\.\d{2})?)/gi
]
const prices = []
pricePatterns.forEach(pattern => {
let match
while ((match = pattern.exec(text)) !== null) {
const price = parseFloat(match[1].replace(/,/g, ''))
if (price > 0 && price < 10000) { // Reasonable price range
prices.push(price)
}
}
})
// Extract additional product info
const stockStatus = this.extractStockStatus(text)
const rating = this.extractRating(text)
const reviewCount = this.extractReviewCount(text)
return {
price: prices.length > 0 ? Math.min(...prices) : 0,
originalPrice: this.extractOriginalPrice(text),
currency: 'USD',
inStock: stockStatus.inStock,
stockText: stockStatus.text,
rating,
reviewCount,
title: title || 'Unknown Product'
}
}
extractStockStatus(text) {
const inStockIndicators = ['in stock', 'available', 'ships today', 'ready to ship']
const outOfStockIndicators = ['out of stock', 'unavailable', 'sold out', 'temporarily unavailable']
const lowerText = text.toLowerCase()
if (outOfStockIndicators.some(indicator => lowerText.includes(indicator))) {
return { inStock: false, text: 'Out of Stock' }
}
if (inStockIndicators.some(indicator => lowerText.includes(indicator))) {
return { inStock: true, text: 'In Stock' }
}
return { inStock: null, text: 'Unknown' }
}
extractRating(text) {
const ratingPattern = /(\d\.\d)\s*(?:out of|\/)\s*5|(\d\.\d)\s*star/gi
const match = ratingPattern.exec(text)
return match ? parseFloat(match[1] || match[2]) : null
}
extractReviewCount(text) {
const reviewPattern = /(\d{1,3}(?:,\d{3})*)\s*reviews?/gi
const match = reviewPattern.exec(text)
return match ? parseInt(match[1].replace(/,/g, '')) : null
}
extractOriginalPrice(text) {
const originalPricePattern = /(?:was|originally|list price)[:\s]*\$?(\d{1,3}(?:,\d{3})*(?:\.\d{2})?)/gi
const match = originalPricePattern.exec(text)
return match ? parseFloat(match[1].replace(/,/g, '')) : null
}
extractStoreName(url) {
try {
const hostname = new URL(url).hostname.replace('www.', '')
const storeName = hostname.split('.')[0]
return storeName.charAt(0).toUpperCase() + storeName.slice(1)
} catch {
return 'Unknown Store'
}
}
deduplicateByStore(prices) {
const storeMap = new Map()
prices.forEach(price => {
const existing = storeMap.get(price.source)
if (!existing || price.price < existing.price) {
storeMap.set(price.source, price)
}
})
return Array.from(storeMap.values())
}
updatePriceHistory(productName, currentPrices) {
if (!this.priceHistory.has(productName)) {
this.priceHistory.set(productName, [])
}
const history = this.priceHistory.get(productName)
history.push({
timestamp: new Date().toISOString(),
prices: currentPrices,
lowestPrice: Math.min(...currentPrices.map(p => p.price))
})
// Keep only last 30 entries
if (history.length > 30) {
history.splice(0, history.length - 30)
}
}
checkPriceAlerts(productName, currentPrices, targetPrice) {
const lowestPrice = Math.min(...currentPrices.map(p => p.price))
if (lowestPrice <= targetPrice) {
const bestDeal = currentPrices.find(p => p.price === lowestPrice)
this.sendPriceAlert(productName, bestDeal, targetPrice)
}
}
sendPriceAlert(productName, deal, targetPrice) {
console.log(`🚨 PRICE ALERT: ${productName}`)
console.log(`Target: $${targetPrice} | Current: $${deal.price} | Store: ${deal.source}`)
console.log(`URL: ${deal.url}`)
// Here you would integrate with email, SMS, or webhook notifications
// Example: await this.sendEmail({ productName, deal, targetPrice })
}
calculateAverage(prices) {
return prices.length > 0 ?
Math.round((prices.reduce((sum, price) => sum + price, 0) / prices.length) * 100) / 100 : 0
}
// Get price trend analysis
getPriceTrend(productName, days = 7) {
const history = this.priceHistory.get(productName) || []
const recent = history.slice(-days)
if (recent.length < 2) return { trend: 'insufficient_data' }
const firstPrice = recent[0].lowestPrice
const lastPrice = recent[recent.length - 1].lowestPrice
const change = lastPrice - firstPrice
const changePercent = (change / firstPrice) * 100
return {
trend: change > 0 ? 'increasing' : change < 0 ? 'decreasing' : 'stable',
change,
changePercent: Math.round(changePercent * 100) / 100,
dataPoints: recent.length
}
}
}
module.exports = PriceMonitor
Usage Examples
Basic Price Tracking
const monitor = new PriceMonitor('your-zapserp-api-key')
// Track a specific product
const results = await monitor.trackProduct('iPhone 15 Pro 256GB', 999)
console.log(`Found ${results.totalStores} stores`)
console.log(`Lowest price: $${results.lowestPrice}`)
console.log(`Average price: $${results.averagePrice}`)
results.currentPrices.forEach(price => {
console.log(`${price.source}: $${price.price} (${price.inStock ? 'In Stock' : 'Out of Stock'})`)
})
Bulk Product Monitoring
const products = [
{ name: 'MacBook Pro M3', targetPrice: 1800 },
{ name: 'Sony WH-1000XM5', targetPrice: 300 },
{ name: 'Nintendo Switch OLED', targetPrice: 320 }
]
for (const product of products) {
const results = await monitor.trackProduct(product.name, product.targetPrice)
console.log(`${product.name}: Best price $${results.lowestPrice}`)
// Wait between requests to be respectful
await new Promise(resolve => setTimeout(resolve, 2000))
}
Price Trend Analysis
// Check price trends over time
const trend = monitor.getPriceTrend('iPhone 15 Pro 256GB', 14)
if (trend.trend === 'decreasing') {
console.log(`📉 Price dropped ${Math.abs(trend.changePercent)}% in last 14 days`)
} else if (trend.trend === 'increasing') {
console.log(`📈 Price increased ${trend.changePercent}% in last 14 days`)
}
Advanced Features
Automated Monitoring Schedule
// Set up automated monitoring every 6 hours
setInterval(async () => {
const watchlist = [
'iPhone 15 Pro', 'Samsung Galaxy S24', 'MacBook Air M3'
]
for (const product of watchlist) {
try {
await monitor.trackProduct(product)
console.log(`✅ Updated prices for ${product}`)
} catch (error) {
console.error(`❌ Failed to update ${product}:`, error.message)
}
}
}, 6 * 60 * 60 * 1000) // 6 hours
Custom Alerts Integration
// Extend the monitor with webhook alerts
class AlertingPriceMonitor extends PriceMonitor {
async sendPriceAlert(productName, deal, targetPrice) {
const alertData = {
product: productName,
currentPrice: deal.price,
targetPrice,
store: deal.source,
url: deal.url,
savings: targetPrice - deal.price,
timestamp: new Date().toISOString()
}
// Send to webhook, email, or Slack
await fetch('https://your-webhook-url.com/price-alert', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(alertData)
})
}
}
Deployment Tips
- Scheduling: Use cron jobs or cloud functions for automated monitoring
- Rate Limiting: Add delays between requests to respect website policies
- Data Storage: Store price history in a database for long-term analysis
- Notifications: Integrate with email services or messaging platforms
- Error Handling: Implement retry logic for failed price extractions
Conclusion
You now have a powerful price monitoring system that can track products across multiple e-commerce sites, detect price changes, and alert you to deals. This system is perfect for dropshippers, retail businesses, or anyone who wants to stay on top of pricing trends.
Ready to expand? Consider adding features like price prediction algorithms, competitor product discovery, or integration with inventory management systems.