Skip to main content

Recipes

Common patterns and use cases for Need2Watch API integration.

Price Drop Alerts

Monitor product prices and get notified when they drop.

Use Case

E-commerce app that tracks competitor prices and alerts users when items go on sale.

Implementation

// Create price monitors for multiple products
async function monitorPrices(products) {
const monitors = [];

for (const product of products) {
const response = await fetch('https://api.need2.watch/v1/monitors', {
method: 'POST',
headers: {
'X-API-Key': process.env.N2W_API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify({
mode: 'nlp',
intent: `Monitor ${product.name} price on ${product.retailers.join(' and ')}`,
sensitivity: 0.3, // More sensitive to price changes
checkIntervalSeconds: 3600 // Check every hour
})
});

const monitor = await response.json();
monitors.push(monitor);
}

return monitors;
}

// Example usage
const products = [
{
name: 'iPhone 16 Pro',
retailers: ['Amazon', 'Best Buy', 'Apple.com']
},
{
name: 'PlayStation 5',
retailers: ['GameStop', 'Target', 'Walmart']
}
];

const monitors = await monitorPrices(products);
console.log(`Created ${monitors.length} price monitors`);

Webhook Handler

app.post('/webhooks/need2watch', async (req, res) => {
const event = req.body;

if (event.type === 'change.detected') {
const change = event.data.change;

// Calculate price drop percentage
const priceDrop = change.before.price - change.after.price;
const dropPercentage = (priceDrop / change.before.price) * 100;

// Only notify if price dropped by 10% or more
if (dropPercentage >= 10) {
await notifyUsers({
product: change.source.title,
oldPrice: change.before.price,
newPrice: change.after.price,
savings: priceDrop,
url: change.source.url
});
}
}

res.status(200).send('OK');
});

async function notifyUsers(priceAlert) {
// Send push notification
await sendPushNotification({
title: `Price Drop: ${priceAlert.product}`,
body: `Now $${priceAlert.newPrice} (was $${priceAlert.oldPrice})`,
url: priceAlert.url,
data: priceAlert
});

// Send email to interested users
const users = await db.users.findByProduct(priceAlert.product);
for (const user of users) {
await sendEmail(user.email, {
subject: `Price Alert: ${priceAlert.product}`,
template: 'price-drop',
data: priceAlert
});
}
}

Competitor Monitoring

Track competitor websites for new products, pricing changes, or content updates.

Use Case

SaaS company monitoring competitor websites for feature announcements and pricing changes.

Implementation

// Monitor competitor pages
async function monitorCompetitors() {
const competitors = [
{
name: 'Competitor A',
url: 'https://competitor-a.com/pricing',
focus: 'pricing changes'
},
{
name: 'Competitor B',
url: 'https://competitor-b.com/blog',
focus: 'new blog posts and feature announcements'
},
{
name: 'Competitor C',
url: 'https://competitor-c.com/features',
focus: 'new features'
}
];

for (const competitor of competitors) {
await fetch('https://api.need2.watch/v1/monitors', {
method: 'POST',
headers: {
'X-API-Key': process.env.N2W_API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify({
mode: 'url',
name: `${competitor.name} - ${competitor.focus}`,
url: competitor.url,
extractionPrompt: `Extract ${competitor.focus}. Focus on dates, prices, and feature descriptions.`,
sensitivity: 0.7, // Less sensitive, only major changes
checkIntervalSeconds: 14400 // Check every 4 hours
})
});
}
}

// Webhook handler
app.post('/webhooks/need2watch', async (req, res) => {
const event = req.body;

if (event.type === 'change.detected') {
const change = event.data.change;

// Log to competitive intelligence database
await db.competitorChanges.create({
competitor: extractCompetitorName(change.monitor_name),
url: change.source.url,
changeType: change.type,
before: change.before,
after: change.after,
detectedAt: change.detected_at
});

// Notify competitive intelligence team via Slack
await postToSlack({
channel: '#competitive-intel',
text: `🚨 Competitor Update: ${change.monitor_name}`,
blocks: [
{
type: 'section',
text: {
type: 'mrkdwn',
text: `*${change.source.title}*\n\nChange detected on ${new Date(change.detected_at).toLocaleDateString()}`
}
},
{
type: 'section',
fields: [
{ type: 'mrkdwn', text: `*Before:*\n${JSON.stringify(change.before, null, 2)}` },
{ type: 'mrkdwn', text: `*After:*\n${JSON.stringify(change.after, null, 2)}` }
]
},
{
type: 'actions',
elements: [
{
type: 'button',
text: { type: 'plain_text', text: 'View Page' },
url: change.source.url
}
]
}
]
});
}

res.status(200).send('OK');
});

Job Posting Tracker

Monitor career pages for new job openings.

Use Case

Recruitment tool that tracks competitor hiring and notifies recruiters about talent acquisition trends.

Implementation

// Monitor multiple company career pages
async function monitorJobPostings(companies) {
for (const company of companies) {
await fetch('https://api.need2.watch/v1/monitors', {
method: 'POST',
headers: {
'X-API-Key': process.env.N2W_API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify({
mode: 'url',
name: `${company.name} - Job Postings`,
url: company.careersUrl,
extractionPrompt: 'Extract all job titles, locations, and posting dates. Focus on engineering and product roles.',
sensitivity: 0.5,
checkIntervalSeconds: 43200 // Check twice daily
})
});
}
}

// Example companies
const companies = [
{ name: 'Google', careersUrl: 'https://careers.google.com/jobs/results' },
{ name: 'Meta', careersUrl: 'https://www.metacareers.com/jobs' },
{ name: 'Apple', careersUrl: 'https://jobs.apple.com/en-us/search' }
];

await monitorJobPostings(companies);

// Webhook handler
app.post('/webhooks/need2watch', async (req, res) => {
const event = req.body;

if (event.type === 'change.detected') {
const change = event.data.change;

// Detect new job postings (additions to the list)
const newJobs = detectNewJobs(change.before, change.after);

for (const job of newJobs) {
// Store in database
await db.jobPostings.create({
company: extractCompanyName(change.monitor_name),
title: job.title,
location: job.location,
postedDate: job.postedDate,
url: change.source.url,
detectedAt: change.detected_at
});

// Notify recruiters via email
await notifyRecruiters({
subject: `New Job: ${job.title} at ${extractCompanyName(change.monitor_name)}`,
job,
url: change.source.url
});
}
}

res.status(200).send('OK');
});

function detectNewJobs(before, after) {
// Extract job titles from before/after snapshots
const beforeTitles = new Set(before.jobs?.map(j => j.title) || []);
const afterJobs = after.jobs || [];

// Find jobs that weren't in the before snapshot
return afterJobs.filter(job => !beforeTitles.has(job.title));
}

Regulatory Compliance Monitoring

Track regulatory websites for compliance updates and policy changes.

Use Case

Financial services company monitoring SEC, FINRA, and other regulatory bodies for rule changes.

Implementation

// Monitor regulatory websites
async function monitorRegulatory() {
const sources = [
{
name: 'SEC Rule Filings',
url: 'https://www.sec.gov/rules',
extractionPrompt: 'Extract new rule filings, effective dates, and rule numbers'
},
{
name: 'FINRA Notices',
url: 'https://www.finra.org/rules-guidance/notices',
extractionPrompt: 'Extract regulatory notices, implementation dates, and affected rules'
},
{
name: 'CFPB Updates',
url: 'https://www.consumerfinance.gov/rules-policy',
extractionPrompt: 'Extract policy updates and enforcement actions'
}
];

for (const source of sources) {
await fetch('https://api.need2.watch/v1/monitors', {
method: 'POST',
headers: {
'X-API-Key': process.env.N2W_API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify({
mode: 'url',
name: source.name,
url: source.url,
extractionPrompt: source.extractionPrompt,
sensitivity: 0.8, // High threshold, only significant changes
checkIntervalSeconds: 3600 // Check hourly
})
});
}
}

// Webhook handler with compliance workflow
app.post('/webhooks/need2watch', async (req, res) => {
const event = req.body;

if (event.type === 'change.detected') {
const change = event.data.change;

// Create compliance task
const task = await db.complianceTasks.create({
source: change.monitor_name,
changeType: change.type,
detectedAt: change.detected_at,
url: change.source.url,
status: 'pending_review',
priority: calculatePriority(change),
assignedTo: null
});

// Notify compliance team
await notifyComplianceTeam({
taskId: task.id,
source: change.monitor_name,
url: change.source.url,
priority: task.priority,
summary: generateChangeSummary(change)
});

// Auto-assign based on keywords
const assignee = autoAssignTask(change);
if (assignee) {
await db.complianceTasks.update(task.id, {
assignedTo: assignee.id
});

await notifyAssignee(assignee, task);
}
}

res.status(200).send('OK');
});

function calculatePriority(change) {
// High priority keywords
const highPriorityKeywords = ['enforcement', 'deadline', 'violation', 'penalty'];

const text = JSON.stringify(change.after).toLowerCase();
const hasHighPriorityKeyword = highPriorityKeywords.some(kw => text.includes(kw));

return hasHighPriorityKeyword ? 'high' : 'normal';
}

Stock Availability Alerts

Monitor product stock status and alert users when items are back in stock.

Use Case

Notification service for hard-to-find items (GPUs, game consoles, limited edition products).

Implementation

// User subscribes to stock alerts
async function subscribeToStockAlert(userId, product) {
// Create monitor for the product
const response = await fetch('https://api.need2.watch/v1/monitors', {
method: 'POST',
headers: {
'X-API-Key': process.env.N2W_API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify({
mode: 'nlp',
intent: `Monitor ${product.name} availability on ${product.retailers.join(' and ')}`,
sensitivity: 0.3, // Sensitive to stock changes
checkIntervalSeconds: 900 // Check every 15 minutes
})
});

const monitor = await response.json();

// Store user subscription
await db.stockAlerts.create({
userId,
productName: product.name,
monitorId: monitor.id,
retailers: product.retailers,
active: true
});

return monitor;
}

// Webhook handler for stock changes
app.post('/webhooks/need2watch', async (req, res) => {
const event = req.body;

if (event.type === 'change.detected') {
const change = event.data.change;

// Check if availability changed from "Out of Stock" to "In Stock"
const wasOutOfStock = change.before.availability?.includes('Out of Stock');
const nowInStock = change.after.availability?.includes('In Stock');

if (wasOutOfStock && nowInStock) {
// Find subscribed users
const subscriptions = await db.stockAlerts.findByMonitor(change.monitor_id);

for (const sub of subscriptions) {
// Send immediate notification
await sendStockAlert(sub.userId, {
product: change.source.title,
retailer: extractRetailer(change.source.url),
price: change.after.price,
url: change.source.url
});

// Mark subscription as notified
await db.stockAlerts.update(sub.id, {
lastNotified: Date.now(),
active: false // One-time alert, deactivate after notification
});
}
}
}

res.status(200).send('OK');
});

async function sendStockAlert(userId, alert) {
const user = await db.users.find(userId);

// Multi-channel notification
await Promise.all([
// Push notification
sendPushNotification(user.deviceTokens, {
title: '🎉 Back in Stock!',
body: `${alert.product} is available at ${alert.retailer}`,
url: alert.url
}),

// Email
sendEmail(user.email, {
subject: `${alert.product} - Back in Stock`,
template: 'stock-alert',
data: alert
}),

// SMS (for premium users)
user.tier === 'premium' && sendSMS(user.phone,
`${alert.product} is back in stock at ${alert.retailer}! ${alert.url}`
)
]);
}

Content Change Monitoring

Monitor blog posts, documentation, or news sites for updates.

Use Case

News aggregator tracking breaking news from multiple sources.

Implementation

// Monitor news sources
async function monitorNewsSources() {
const sources = [
{ name: 'TechCrunch', url: 'https://techcrunch.com', category: 'tech' },
{ name: 'The Verge', url: 'https://theverge.com', category: 'tech' },
{ name: 'Bloomberg', url: 'https://bloomberg.com', category: 'finance' }
];

for (const source of sources) {
await fetch('https://api.need2.watch/v1/monitors', {
method: 'POST',
headers: {
'X-API-Key': process.env.N2W_API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify({
mode: 'url',
name: `${source.name} - Latest Articles`,
url: source.url,
extractionPrompt: 'Extract article headlines, publication times, and article summaries. Focus on the top 10 most recent articles.',
sensitivity: 0.5,
checkIntervalSeconds: 300 // Check every 5 minutes
})
});
}
}

// Webhook handler
app.post('/webhooks/need2watch', async (req, res) => {
const event = req.body;

if (event.type === 'change.detected') {
const change = event.data.change;

// Detect new articles
const newArticles = detectNewArticles(change.before, change.after);

for (const article of newArticles) {
// Store article
await db.articles.create({
source: extractSourceName(change.monitor_name),
title: article.title,
summary: article.summary,
publishedAt: article.publishedAt,
url: article.url,
detectedAt: change.detected_at
});

// Publish to real-time feed
await publishToFeed({
type: 'new_article',
article,
source: extractSourceName(change.monitor_name)
});
}
}

res.status(200).send('OK');
});

function detectNewArticles(before, after) {
const beforeTitles = new Set(before.articles?.map(a => a.title) || []);
const afterArticles = after.articles || [];

return afterArticles.filter(article => !beforeTitles.has(article.title));
}

Best Practices

Error Handling

async function createMonitorWithRetry(monitorConfig, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const response = await fetch('https://api.need2.watch/v1/monitors', {
method: 'POST',
headers: {
'X-API-Key': process.env.N2W_API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify(monitorConfig)
});

if (!response.ok) {
const error = await response.json();

if (error.code === 'ERR_LIMIT_EXCEEDED') {
throw new Error('Monitor limit exceeded. Please upgrade or delete unused monitors.');
}

if (response.status >= 500) {
// Server error - retry
await sleep(Math.pow(2, attempt) * 1000);
continue;
}

throw new Error(error.message || error.error);
}

return await response.json();

} catch (err) {
if (attempt === maxRetries - 1) throw err;
await sleep(Math.pow(2, attempt) * 1000);
}
}
}

Webhook Idempotency

const processedEvents = new Map();

app.post('/webhooks/need2watch', async (req, res) => {
const event = req.body;

// Check if already processed
if (processedEvents.has(event.id)) {
return res.status(200).send('Already processed');
}

try {
await processWebhook(event);

// Mark as processed (with TTL cleanup)
processedEvents.set(event.id, Date.now());
setTimeout(() => processedEvents.delete(event.id), 24 * 60 * 60 * 1000); // 24h

res.status(200).send('OK');
} catch (err) {
console.error('Webhook processing failed:', err);
res.status(500).send('Processing failed');
}
});

Rate Limit Handling

class RateLimiter {
constructor(maxRequests, windowMs) {
this.maxRequests = maxRequests;
this.windowMs = windowMs;
this.requests = [];
}

async checkLimit() {
const now = Date.now();
this.requests = this.requests.filter(t => now - t < this.windowMs);

if (this.requests.length >= this.maxRequests) {
const oldestRequest = this.requests[0];
const waitTime = this.windowMs - (now - oldestRequest);
await sleep(waitTime);
return this.checkLimit();
}

this.requests.push(now);
}
}

const limiter = new RateLimiter(100, 24 * 60 * 60 * 1000); // 100 req/day

async function createMonitorWithRateLimit(config) {
await limiter.checkLimit();
return createMonitor(config);
}

Next Steps