🔍 Competitor Analysis

Analytics src
168 lines by @nichxbt

Competitor Analysis on X - by nichxbt

How to Use

  1. Navigate to x.com and log in
  2. Open DevTools Console (F12 or Cmd+Option+I)
  3. Paste the script below and press Enter

Default Configuration

const CONFIG = {
    accounts: [
      // Add usernames to compare (without @)
      // 'elonmusk',
      // 'nichxbt',
      // 'openai',
    ],
    postsToAnalyze: 20,
    scrollDelay: 2000,
    exportResults: true,
  };

Full Script

Copy and paste this entire script into your browser DevTools console on x.com.

// Competitor Analysis on X - by nichxbt
// https://github.com/nirholas/xactions
// Compare engagement, follower counts, and posting patterns of multiple accounts
// 1. Go to x.com (any page)
// 2. Open the Developer Console (F12)
// 3. Edit the accounts array below
// 4. Paste this into the Developer Console and run it
//
// Last Updated: 24 February 2026
(() => {
  const CONFIG = {
    accounts: [
      // Add usernames to compare (without @)
      // 'elonmusk',
      // 'nichxbt',
      // 'openai',
    ],
    postsToAnalyze: 20,
    scrollDelay: 2000,
    exportResults: true,
  };

  const $tweet = 'article[data-testid="tweet"]';
  const $tweetText = '[data-testid="tweetText"]';
  const $like = '[data-testid="like"], [data-testid="unlike"]';
  const $retweet = '[data-testid="retweet"], [data-testid="unretweet"]';
  const $reply = '[data-testid="reply"]';

  const sleep = (ms) => new Promise(r => setTimeout(r, ms));

  const parseCount = (str) => {
    if (!str) return 0;
    str = str.replace(/,/g, '').trim();
    const match = str.match(/([\d.]+)([KMB])?/i);
    if (!match) return 0;
    let num = parseFloat(match[1]);
    if (match[2]) num *= { K: 1000, M: 1000000, B: 1000000000 }[match[2].toUpperCase()];
    return Math.round(num);
  };

  const analyzeProfile = async (username) => {
    console.log(`\n📊 Analyzing @${username}...`);

    // Navigate to profile
    window.location.href = `https://x.com/${username}`;
    await sleep(3000);

    // Extract profile info
    const followingEl = document.querySelector(`a[href="/${username}/following"]`);
    const followersEl = document.querySelector(`a[href="/${username}/followers"]`);
    const bio = document.querySelector('[data-testid="UserDescription"]')?.textContent || '';
    const displayName = document.querySelector('[data-testid="UserName"]')?.textContent || '';

    const following = parseCount(followingEl?.textContent || '0');
    const followers = parseCount(followersEl?.textContent || '0');

    // Scroll and collect posts
    const posts = new Map();
    let noNewPosts = 0;

    while (posts.size < CONFIG.postsToAnalyze && noNewPosts < 3) {
      const prevSize = posts.size;

      document.querySelectorAll($tweet).forEach(tweet => {
        const linkEl = tweet.querySelector('a[href*="/status/"]');
        if (!linkEl || posts.has(linkEl.href)) return;

        const textEl = tweet.querySelector($tweetText);
        const likeEl = tweet.querySelector($like);
        const rtEl = tweet.querySelector($retweet);
        const replyEl = tweet.querySelector($reply);
        const timeEl = tweet.querySelector('time');

        posts.set(linkEl.href, {
          text: textEl?.textContent?.substring(0, 100) || '',
          likes: parseCount(likeEl?.getAttribute('aria-label') || '0'),
          retweets: parseCount(rtEl?.getAttribute('aria-label') || '0'),
          replies: parseCount(replyEl?.getAttribute('aria-label') || '0'),
          timestamp: timeEl?.dateTime || '',
        });
      });

      if (posts.size === prevSize) noNewPosts++;
      else noNewPosts = 0;

      window.scrollTo(0, document.body.scrollHeight);
      await sleep(CONFIG.scrollDelay);
    }

    const postsArray = [...posts.values()];
    const totalLikes = postsArray.reduce((s, p) => s + p.likes, 0);
    const totalRts = postsArray.reduce((s, p) => s + p.retweets, 0);
    const totalReplies = postsArray.reduce((s, p) => s + p.replies, 0);
    const totalEngagement = totalLikes + totalRts + totalReplies;

    return {
      username,
      displayName,
      bio: bio.substring(0, 200),
      followers,
      following,
      followRatio: following > 0 ? (followers / following).toFixed(2) : 'N/A',
      postsAnalyzed: postsArray.length,
      totalLikes,
      totalRetweets: totalRts,
      totalReplies,
      totalEngagement,
      avgLikesPerPost: postsArray.length > 0 ? (totalLikes / postsArray.length).toFixed(1) : 0,
      avgEngagementPerPost: postsArray.length > 0 ? (totalEngagement / postsArray.length).toFixed(1) : 0,
      engagementRate: followers > 0 ? ((totalEngagement / postsArray.length / followers) * 100).toFixed(3) : '0',
    };
  };

  const run = async () => {
    console.log('🏆 COMPETITOR ANALYSIS - XActions by nichxbt');

    if (CONFIG.accounts.length === 0) {
      console.error('❌ No accounts to analyze! Edit CONFIG.accounts array.');
      return;
    }

    console.log(`📋 Accounts to compare: ${CONFIG.accounts.join(', ')}`);

    const results = [];

    for (const account of CONFIG.accounts) {
      try {
        const data = await analyzeProfile(account);
        results.push(data);
        console.log(`   ✅ @${account}: ${data.followers.toLocaleString()} followers, ${data.avgLikesPerPost} avg likes`);
      } catch (e) {
        console.error(`   ❌ @${account}: ${e.message}`);
      }
    }

    // Sort by engagement
    results.sort((a, b) => parseFloat(b.avgEngagementPerPost) - parseFloat(a.avgEngagementPerPost));

    console.log('\n╔════════════════════════════════════════════════════════════╗');
    console.log('║  🏆 COMPETITOR COMPARISON                                   ║');
    console.log('╚════════════════════════════════════════════════════════════╝\n');

    results.forEach((r, i) => {
      console.log(`${i + 1}. @${r.username}`);
      console.log(`   👥 Followers: ${r.followers.toLocaleString()} | Following: ${r.following.toLocaleString()} | Ratio: ${r.followRatio}`);
      console.log(`   📊 Avg likes: ${r.avgLikesPerPost} | Avg engagement: ${r.avgEngagementPerPost}`);
      console.log(`   📈 Engagement rate: ${r.engagementRate}%`);
      console.log('');
    });

    if (CONFIG.exportResults) {
      const report = {
        analyzedAt: new Date().toISOString(),
        accounts: results,
      };
      const blob = new Blob([JSON.stringify(report, null, 2)], { type: 'application/json' });
      const url = URL.createObjectURL(blob);
      const a = document.createElement('a');
      a.href = url;
      a.download = `xactions-competitor-analysis-${new Date().toISOString().slice(0, 10)}.json`;
      a.click();
      console.log('📥 Full report downloaded as JSON');
    }
  };

  run();
})();

⚡ More XActions Scripts

Browse 300+ free browser scripts for X/Twitter automation. No API keys, no fees.

Browse All Scripts