DEV Community

Ozor
Ozor

Posted on

How to Build a Competitive Analysis Tool in JavaScript (Free APIs)

Want to know what tech stack your competitors use, where their servers are located, and what their site looks like — all from a single script? Let's build it.

We'll combine four free APIs to create a competitive intelligence tool that runs from the command line:

  • Web Scraper — extract meta tags, headings, and links
  • Screenshots — capture visual snapshots
  • DNS Lookup — find hosting provider and infrastructure
  • IP Geolocation — locate their servers

Get a Free API Key

All four APIs use the same key. Grab one (200 free credits, no credit card):

curl -X POST "https://api.frostbyte.world/api/keys/create"
Enter fullscreen mode Exit fullscreen mode

Save the key from the response.

The Full Script

Create competitor-analysis.js:

const API_KEY = process.env.FROSTBYTE_KEY;
const BASE = 'https://api.frostbyte.world';

async function analyzeCompetitor(url) {
  const domain = new URL(url).hostname;
  console.log(`\n🔍 Analyzing ${domain}...\n`);

  // Run all API calls in parallel
  const [scrape, screenshot, dns, geo] = await Promise.all([
    fetch(`${BASE}/api/scraper/scrape?url=${encodeURIComponent(url)}&format=json`, {
      headers: { 'x-api-key': API_KEY }
    }).then(r => r.json()),

    fetch(`${BASE}/api/screenshot?url=${encodeURIComponent(url)}&width=1280&height=800`, {
      headers: { 'x-api-key': API_KEY }
    }).then(r => r.json()),

    fetch(`${BASE}/api/dns/lookup?domain=${domain}&type=A`, {
      headers: { 'x-api-key': API_KEY }
    }).then(r => r.json()),

    // We'll get geo data after DNS resolves
    null
  ]);

  // Get server IP and geolocate it
  const serverIp = dns?.records?.[0]?.value;
  let geoData = null;
  if (serverIp) {
    const geoRes = await fetch(`${BASE}/api/geo?ip=${serverIp}`, {
      headers: { 'x-api-key': API_KEY }
    });
    geoData = await geoRes.json();
  }

  // Extract tech signals from scraped content
  const techStack = detectTechStack(scrape);

  return {
    domain,
    title: scrape?.title || 'N/A',
    description: scrape?.meta?.description || 'N/A',
    techStack,
    server: {
      ip: serverIp,
      location: geoData ? `${geoData.city}, ${geoData.country}` : 'Unknown',
      isp: geoData?.isp || 'Unknown',
      org: geoData?.org || 'Unknown',
    },
    screenshot: screenshot?.screenshot_url || null,
    links: {
      internal: scrape?.links?.filter(l => l.includes(domain)).length || 0,
      external: scrape?.links?.filter(l => !l.includes(domain)).length || 0,
    }
  };
}

function detectTechStack(scrapeData) {
  const html = JSON.stringify(scrapeData).toLowerCase();
  const techs = [];

  const signatures = {
    'React': ['react', 'reactdom', '__next', '_next/static'],
    'Next.js': ['_next/', '__next', 'next/image'],
    'Vue.js': ['vue.js', 'vuejs', '__vue'],
    'Angular': ['ng-version', 'angular'],
    'Svelte': ['svelte', '__svelte'],
    'Tailwind CSS': ['tailwind', 'tw-'],
    'Bootstrap': ['bootstrap'],
    'WordPress': ['wp-content', 'wordpress'],
    'Shopify': ['shopify', 'cdn.shopify'],
    'Vercel': ['vercel', '_vercel'],
    'Cloudflare': ['cloudflare', 'cf-ray'],
    'Google Analytics': ['google-analytics', 'gtag', 'ga.js'],
    'Google Tag Manager': ['googletagmanager', 'gtm.js'],
    'Stripe': ['stripe.com', 'stripe.js'],
    'Intercom': ['intercom', 'intercomcdn'],
    'HubSpot': ['hubspot', 'hs-scripts'],
    'Segment': ['segment.com', 'analytics.js'],
    'Sentry': ['sentry.io', 'sentry'],
  };

  for (const [tech, patterns] of Object.entries(signatures)) {
    if (patterns.some(p => html.includes(p))) {
      techs.push(tech);
    }
  }

  return techs;
}

// Compare multiple competitors
async function compareCompetitors(urls) {
  console.log('='.repeat(60));
  console.log('  COMPETITIVE ANALYSIS REPORT');
  console.log('='.repeat(60));

  const results = [];
  for (const url of urls) {
    try {
      const result = await analyzeCompetitor(url);
      results.push(result);

      console.log(`\n📊 ${result.domain}`);
      console.log(`   Title: ${result.title}`);
      console.log(`   Tech: ${result.techStack.join(', ') || 'None detected'}`);
      console.log(`   Server: ${result.server.ip} (${result.server.location})`);
      console.log(`   Hosting: ${result.server.org}`);
      console.log(`   Links: ${result.links.internal} internal, ${result.links.external} external`);
      if (result.screenshot) {
        console.log(`   Screenshot: ${result.screenshot}`);
      }
    } catch (err) {
      console.error(`   ❌ Error analyzing ${url}: ${err.message}`);
    }
  }

  // Print comparison table
  if (results.length > 1) {
    console.log('\n' + '='.repeat(60));
    console.log('  COMPARISON MATRIX');
    console.log('='.repeat(60));

    // Collect all unique techs
    const allTechs = [...new Set(results.flatMap(r => r.techStack))];

    console.log('\nTech Stack Overlap:');
    for (const tech of allTechs) {
      const users = results
        .filter(r => r.techStack.includes(tech))
        .map(r => r.domain);
      console.log(`  ${tech}: ${users.join(', ')}`);
    }
  }

  return results;
}

// Run it
const competitors = process.argv.slice(2);
if (competitors.length === 0) {
  console.log('Usage: node competitor-analysis.js https://site1.com https://site2.com');
  process.exit(1);
}

compareCompetitors(competitors);
Enter fullscreen mode Exit fullscreen mode

Run It

export FROSTBYTE_KEY="your-api-key"
node competitor-analysis.js https://stripe.com https://paddle.com https://lemonsqueezy.com
Enter fullscreen mode Exit fullscreen mode

Example output:

============================================================
  COMPETITIVE ANALYSIS REPORT
============================================================

📊 stripe.com
   Title: Stripe | Financial Infrastructure for the Internet
   Tech: React, Next.js, Google Analytics, Stripe
   Server: 52.54.140.218 (Ashburn, United States)
   Hosting: Amazon Technologies Inc.
   Links: 142 internal, 8 external
   Screenshot: https://api.frostbyte.world/screenshots/abc123.png

📊 paddle.com
   Title: Paddle | The complete payments platform
   Tech: Next.js, Tailwind CSS, Google Tag Manager, HubSpot
   Server: 76.76.21.21 (San Francisco, United States)
   Hosting: Vercel Inc
   Links: 87 internal, 12 external

📊 lemonsqueezy.com
   Title: Lemon Squeezy | Payments, tax & subscriptions
   Tech: Vue.js, Tailwind CSS, Google Analytics, Stripe
   Server: 172.67.182.31 (San Francisco, United States)
   Hosting: Cloudflare Inc
   Links: 64 internal, 5 external

============================================================
  COMPARISON MATRIX
============================================================

Tech Stack Overlap:
  React: stripe.com
  Next.js: stripe.com, paddle.com
  Vue.js: lemonsqueezy.com
  Tailwind CSS: paddle.com, lemonsqueezy.com
  Google Analytics: stripe.com, lemonsqueezy.com
  Stripe: stripe.com, lemonsqueezy.com
  Google Tag Manager: paddle.com
  HubSpot: paddle.com
Enter fullscreen mode Exit fullscreen mode

What This Tells You

From one command, you now know:

  1. Tech choices — Who uses React vs Vue? Who's on Next.js?
  2. Infrastructure — AWS vs Vercel vs Cloudflare. Where are servers located?
  3. Third-party services — Who uses HubSpot vs Intercom? Stripe vs custom payments?
  4. Content scale — Link counts hint at content strategy depth
  5. Visual design — Screenshots for side-by-side comparison

Extend It

Add more signals by combining with other endpoints:

// Check if they use CDN
const cdnCheck = await fetch(
  `${BASE}/api/dns/lookup?domain=${domain}&type=CNAME`,
  { headers: { 'x-api-key': API_KEY } }
).then(r => r.json());

// Check email infrastructure (MX records)
const emailSetup = await fetch(
  `${BASE}/api/dns/lookup?domain=${domain}&type=MX`,
  { headers: { 'x-api-key': API_KEY } }
).then(r => r.json());
console.log('Email provider:', emailSetup?.records?.[0]?.value);
// google.com → Google Workspace, outlook.com → Microsoft 365
Enter fullscreen mode Exit fullscreen mode

Cost

Each competitor analysis uses 4 API credits (scrape + screenshot + DNS + geo). With 200 free credits, you can analyze 50 competitors at zero cost.


The full source code uses Node.js 18+ (native fetch). No external dependencies required.

Try the APIs: Frostbyte API | Get API Key

Top comments (0)