AINode.jsAutomation

Competitor Research Automation Tool

TT
TopicTrick Team
Competitor Research Automation Tool

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-limit
bash
# .env
OPENAI_API_KEY=sk-your-key-here
PORT=3000

Research 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 →