AINode.jsAutomation
Competitor Research Automation Tool
Build an AI competitor research tool with Node.js that scrapes competitor websites, analyzes their messaging, content strategy, and pricing, then generates a structured competitive intelligence report.
TT
Emily Ross
Competitor Research Automation Tool
Keeping up with competitors manually takes hours every week. This tool scrapes multiple competitor websites simultaneously and uses GPT-4o to generate a structured competitive intelligence report — positioning, pricing, features, content strategy, and your differentiation opportunities.
This is Tool 11 of the Build 50 AI Automation Tools course.
What You'll Build
POST /research— provide competitor URLs, receive a full competitive intelligence report- Analyzes homepage, pricing, and about pages for each competitor
- Generates a comparison matrix and differentiation recommendations
Setup
bash
mkdir competitor-research && cd competitor-research
npm init -y
npm install express axios cheerio openai dotenv p-limitbash
# .env
OPENAI_API_KEY=sk-your-key-here
PORT=3000Research Service
js
// src/services/researchService.js
import axios from 'axios';
import * as cheerio from 'cheerio';
import OpenAI from 'openai';
import pLimit from 'p-limit';
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
const limit = pLimit(3); // Max 3 concurrent requests
async function fetchPageText(url) {
const { data } = await axios.get(url, {
timeout: 15_000,
headers: { 'User-Agent': 'Mozilla/5.0 (compatible; ResearchBot/1.0)' },
});
const $ = cheerio.load(data);
$('script, style, nav, footer').remove();
return $('body').text().replace(/\s+/g, ' ').slice(0, 8000);
}
async function fetchCompetitorData(baseUrl) {
const urlObj = new URL(baseUrl);
const pages = [
{ type: 'homepage', url: baseUrl },
{ type: 'about', url: `${urlObj.origin}/about` },
{ type: 'pricing', url: `${urlObj.origin}/pricing` },
];
const pageData = await Promise.allSettled(
pages.map(p => fetchPageText(p.url).then(text => ({ ...p, text })))
);
return pageData
.filter(r => r.status === 'fulfilled')
.map(r => r.value);
}
async function analyzeCompetitor(baseUrl, pages) {
const combinedContent = pages.map(p => `[${p.type.toUpperCase()}]\n${p.text}`).join('\n\n---\n\n');
const response = await openai.chat.completions.create({
model: 'gpt-4o',
messages: [
{
role: 'system',
content: `You are a strategic business analyst. Analyze this competitor's website content.
Return ONLY a JSON object — no markdown:
{
"companyName": "string",
"website": "${baseUrl}",
"valueProposition": "1-2 sentence core value prop",
"targetAudience": "string — who they're targeting",
"mainFeatures": ["top 5-8 features/capabilities"],
"pricing": {
"model": "freemium | subscription | usage-based | one-time | contact-us | unknown",
"tiers": [{"name": "string", "price": "string", "highlights": ["string"]}],
"notes": "string"
},
"messaging": {
"primaryTagline": "string",
"keyBenefits": ["top selling points they emphasize"],
"toneOfVoice": "professional | casual | technical | enterprise | startup"
},
"contentStrategy": {
"blogTopics": ["main topics if blog found"],
"contentFrequency": "string or null"
},
"trustSignals": ["customer logos mentioned, testimonials, awards, certifications"],
"technicalIndicators": ["any tech stack clues — e.g. 'Built with React', 'Powered by AWS'"],
"weaknesses": ["apparent gaps or weaknesses you can identify"],
"strengths": ["clear competitive strengths"]
}`,
},
{ role: 'user', content: combinedContent },
],
temperature: 0.3,
response_format: { type: 'json_object' },
});
return JSON.parse(response.choices[0].message.content);
}
async function synthesizeReport(yourProduct, competitorAnalyses) {
const competitorSummaries = competitorAnalyses.map(c =>
`${c.companyName}: ${c.valueProposition} | Features: ${c.mainFeatures.slice(0, 3).join(', ')} | Pricing: ${c.pricing.model}`
).join('\n');
const response = await openai.chat.completions.create({
model: 'gpt-4o',
messages: [
{
role: 'system',
content: `You are a strategic consultant. Synthesize competitor data into actionable insights.
Return ONLY a JSON object:
{
"marketLandscape": "2-3 sentence overview of the competitive landscape",
"yourDifferentiators": ["unique angles your product should emphasize given these competitors"],
"marketGaps": ["opportunities none of the competitors are addressing"],
"positioningRecommendation": "string — how to position against this competitive set",
"battlecards": [
{
"competitor": "string",
"keyStrengths": ["string"],
"vulnerabilities": ["string"],
"talkingPoints": ["what to say when competing against this company"]
}
],
"priorityActions": ["top 3 things to do based on this competitive analysis"]
}`,
},
{
role: 'user',
content: `Your product: ${yourProduct || 'Not specified'}\n\nCompetitors:\n${competitorSummaries}`,
},
],
temperature: 0.4,
response_format: { type: 'json_object' },
});
return JSON.parse(response.choices[0].message.content);
}
export async function researchCompetitors(competitorUrls, yourProduct = '') {
const analyses = await Promise.all(
competitorUrls.map(url => limit(async () => {
const pages = await fetchCompetitorData(url);
return analyzeCompetitor(url, pages);
}))
);
const report = await synthesizeReport(yourProduct, analyses);
return {
generatedAt: new Date().toISOString(),
competitorCount: analyses.length,
competitors: analyses,
strategicReport: report,
};
}Server
js
// src/server.js
import 'dotenv/config';
import express from 'express';
import { researchCompetitors } from './services/researchService.js';
const app = express();
app.use(express.json());
app.post('/research', async (req, res, next) => {
try {
const { competitors, yourProduct } = req.body;
if (!competitors?.length) return res.status(400).json({ error: 'competitors array required' });
const report = await researchCompetitors(competitors, yourProduct);
res.json({ success: true, report });
} catch (err) { next(err); }
});
app.use((err, _req, res, _next) => res.status(500).json({ error: err.message }));
app.listen(process.env.PORT ?? 3000, () => console.log('Competitor Research running'));Testing
bash
curl -X POST http://localhost:3000/research \
-H "Content-Type: application/json" \
-d '{
"competitors": [
"https://competitor1.com",
"https://competitor2.com",
"https://competitor3.com"
],
"yourProduct": "AI-powered project management tool for remote engineering teams"
}'Build 50 AI Automation Tools — Tool 11 of 50
Competitive research is live. Continue to Phase 3 with Tool 12: AI Email Reply Generator.
Summary
- p-limit controls concurrency — scrapes multiple competitors in parallel without overwhelming servers
- Multi-page scraping (homepage, about, pricing) gives a comprehensive picture of each competitor
- Two-stage AI analysis — individual competitor analysis + strategic synthesis = actionable report
- Battlecards give sales teams specific talking points for each competitor
- Schedule weekly runs and store results in a database to track competitor changes over time
Continue to Tool 12: AI Email Reply Generator →
