👥 Following Scraping
Scrape the complete list of accounts that any public X/Twitter user follows — with mutual follow detection.
Author: nich (@nichxbt)
📦 What You Get
When you scrape a user's following list, you get:
| Field | Description |
|---|---|
username |
The @handle of the followed account |
name |
Display name |
bio |
Profile bio/description |
verified |
Blue checkmark status |
avatar |
Profile image URL |
followsBack |
Whether this account follows the target user back |
The followsBack indicator is powerful — it lets you instantly identify:
- ✅ Mutual follows (they follow each other)
- ❌ One-way follows (target follows them, but they don't follow back)
🖥️ Example 1: Browser Console (Quick)
Best for: Scraping up to ~500 following quickly from your browser
Instructions
- Go to
x.com/USERNAME/following(replace USERNAME with target) - Open DevTools (F12 or Cmd+Opt+I)
- Go to Console tab
- Paste the code below and press Enter
// ============================================
// XActions - Following Scraper (Browser Console)
// Author: nich (@nichxbt)
// Go to: x.com/USERNAME/following
// Open console (F12), paste this entire script
// ============================================
(async () => {
const TARGET_COUNT = 500; // Adjust as needed
const SCROLL_DELAY = 1500; // ms between scrolls
console.log('🔍 Starting following scrape...');
console.log(`📊 Target: ${TARGET_COUNT} accounts`);
const following = new Map();
let retries = 0;
const maxRetries = 10;
// Helper to extract user data from cells
const extractUsers = () => {
const cells = document.querySelectorAll('[data-testid="UserCell"]');
const users = [];
cells.forEach(cell => {
try {
// Get username from link
const link = cell.querySelector('a[href^="/"]');
const href = link?.getAttribute('href') || '';
const username = href.split('/')[1];
// Skip if invalid or query params
if (!username || username.includes('?')) return;
// Get display name
const nameEl = cell.querySelector('[dir="ltr"] > span');
const name = nameEl?.textContent?.trim() || null;
// Get bio
const bioEl = cell.querySelector('[data-testid="UserDescription"]');
const bio = bioEl?.textContent?.trim() || null;
// Check verified status
const verified = !!cell.querySelector('svg[aria-label*="Verified"]');
// Get avatar
const avatarEl = cell.querySelector('img[src*="profile_images"]');
const avatar = avatarEl?.src || null;
// Check if they follow back (look for "Follows you" badge)
const followsBackEl = cell.querySelector('[data-testid="userFollowIndicator"]');
const followsBackText = cell.textContent?.includes('Follows you');
const followsBack = !!(followsBackEl || followsBackText);
users.push({
username,
name,
bio,
verified,
avatar,
followsBack,
});
} catch (e) {
// Skip malformed cells
}
});
return users;
};
// Sleep helper
const sleep = (ms) => new Promise(r => setTimeout(r, ms));
// Main scraping loop
while (following.size < TARGET_COUNT && retries < maxRetries) {
// Extract visible users
const users = extractUsers();
const prevSize = following.size;
// Add to map (dedupes automatically)
users.forEach(user => {
if (!following.has(user.username)) {
following.set(user.username, user);
}
});
// Progress update
const mutualCount = Array.from(following.values()).filter(u => u.followsBack).length;
console.log(`📈 Scraped: ${following.size} following (${mutualCount} mutual)`);
// Check if we're stuck
if (following.size === prevSize) {
retries++;
console.log(`⏳ No new users found (retry ${retries}/${maxRetries})`);
} else {
retries = 0;
}
// Scroll to load more
window.scrollTo(0, document.body.scrollHeight);
await sleep(SCROLL_DELAY);
}
// Convert to array
const result = Array.from(following.values());
// Calculate stats
const mutualFollows = result.filter(u => u.followsBack);
const oneWayFollows = result.filter(u => !u.followsBack);
const verifiedCount = result.filter(u => u.verified).length;
const withBioCount = result.filter(u => u.bio).length;
// Summary
console.log('\n✅ Scraping complete!');
console.log('━'.repeat(40));
console.log(`📊 Total following scraped: ${result.length}`);
console.log(`🤝 Mutual follows: ${mutualFollows.length} (${Math.round(mutualFollows.length / result.length * 100)}%)`);
console.log(`➡️ One-way follows: ${oneWayFollows.length} (${Math.round(oneWayFollows.length / result.length * 100)}%)`);
console.log(`✓ Verified: ${verifiedCount}`);
console.log(`✓ With bio: ${withBioCount}`);
console.log('━'.repeat(40));
// Copy to clipboard
const json = JSON.stringify(result, null, 2);
await navigator.clipboard.writeText(json);
console.log('\n📋 Full data copied to clipboard!');
// Also expose data for inspection
console.log('\n💾 Data objects (right-click → Copy object):');
console.log('All following:', result);
console.log('Mutual follows:', mutualFollows);
console.log('One-way follows:', oneWayFollows);
// Create downloadable file
const blob = new Blob([json], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `following-${new Date().toISOString().split('T')[0]}.json`;
a.click();
console.log('\n📥 Download started!');
// Return for further use in console
return {
all: result,
mutual: mutualFollows,
oneWay: oneWayFollows,
stats: {
total: result.length,
mutual: mutualFollows.length,
oneWay: oneWayFollows.length,
verified: verifiedCount,
withBio: withBioCount,
}
};
})();
What Happens
- 🔄 Script scrolls through the following list automatically
- 📊 Extracts data from each user card
- 🔍 Detects "Follows you" badge for each account
- 🔁 Deduplicates automatically
- 📈 Shows live progress with mutual follow count
- 📥 Downloads JSON file automatically
- 📋 Copies data to clipboard
Sample Output
🔍 Starting following scrape...
📊 Target: 500 accounts
📈 Scraped: 47 following (12 mutual)
📈 Scraped: 89 following (31 mutual)
📈 Scraped: 156 following (52 mutual)
...
📈 Scraped: 500 following (187 mutual)
✅ Scraping complete!
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📊 Total following scraped: 500
🤝 Mutual follows: 187 (37%)
➡️ One-way follows: 313 (63%)
✓ Verified: 45
✓ With bio: 412
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📋 Full data copied to clipboard!
📥 Download started!
🚀 Example 2: Node.js with Puppeteer (Production-Ready)
Best for: Large following lists, automation, scheduled jobs, exporting to JSON/CSV
Installation
npm install puppeteer puppeteer-extra puppeteer-extra-plugin-stealth
Full Script
Save as scrape-following.js:
// ============================================
// XActions - Following Scraper (Node.js)
// Author: nich (@nichxbt)
//
// Save as: scrape-following.js
// Run: node scrape-following.js <username> [limit]
// Example: node scrape-following.js elonmusk 1000
// ============================================
import puppeteer from 'puppeteer-extra';
import StealthPlugin from 'puppeteer-extra-plugin-stealth';
import fs from 'fs/promises';
import path from 'path';
// Apply stealth plugin to avoid detection
puppeteer.use(StealthPlugin());
/**
* Scrape the accounts that a Twitter/X user follows
* @param {string} username - Account to scrape following from
* @param {Object} options - Configuration options
* @returns {Promise<Array>} Array of following user objects
*/
async function scrapeFollowing(username, options = {}) {
const {
limit = 1000,
headless = true,
authToken = null,
onProgress = null,
scrollDelay = 1500,
maxRetries = 10,
timeout = 30000,
} = options;
console.log(`\n🔍 Scraping following for @${username}`);
console.log(`📊 Limit: ${limit}`);
console.log(`🕐 Started: ${new Date().toLocaleTimeString()}\n`);
const browser = await puppeteer.launch({
headless: headless ? 'new' : false,
args: [
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-blink-features=AutomationControlled',
'--disable-web-security',
'--disable-features=IsolateOrigins,site-per-process',
],
});
try {
const page = await browser.newPage();
// Set realistic viewport with slight randomization
await page.setViewport({
width: 1280 + Math.floor(Math.random() * 100),
height: 800 + Math.floor(Math.random() * 100),
});
// Set realistic user agent
await page.setUserAgent(
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
);
// Set auth cookie if provided (for private accounts or rate limit bypass)
if (authToken) {
await page.setCookie({
name: 'auth_token',
value: authToken,
domain: '.x.com',
path: '/',
httpOnly: true,
secure: true,
});
console.log('🔐 Auth token set');
}
// Navigate to following page
const url = `https://x.com/${username}/following`;
console.log(`🌐 Navigating to ${url}`);
await page.goto(url, {
waitUntil: 'networkidle2',
timeout,
});
// Wait for user cells to appear
try {
await page.waitForSelector('[data-testid="UserCell"]', { timeout: 10000 });
} catch (e) {
// Check if account exists or is private
const pageContent = await page.content();
if (pageContent.includes('This account doesn't exist')) {
throw new Error(`Account @${username} does not exist`);
}
if (pageContent.includes('These posts are protected')) {
throw new Error(`Account @${username} is private. Provide authToken to access.`);
}
throw new Error('Could not load following list. Page may have changed or be rate limited.');
}
// Small random delay to mimic human behavior
await new Promise(r => setTimeout(r, 1000 + Math.random() * 1000));
const following = new Map();
let retries = 0;
// Main scraping loop
while (following.size < limit && retries < maxRetries) {
// Extract users from page
const users = await page.evaluate(() => {
const cells = document.querySelectorAll('[data-testid="UserCell"]');
return Array.from(cells).map(cell => {
try {
// Get username from profile link
const link = cell.querySelector('a[href^="/"]');
const href = link?.getAttribute('href') || '';
const username = href.split('/')[1];
// Skip if invalid username
if (!username || username.includes('?') || username.includes('/')) return null;
// Get display name
const nameEl = cell.querySelector('[dir="ltr"] > span');
const name = nameEl?.textContent?.trim() || null;
// Get bio
const bioEl = cell.querySelector('[data-testid="UserDescription"]');
const bio = bioEl?.textContent?.trim() || null;
// Get avatar URL
const avatarEl = cell.querySelector('img[src*="profile_images"]');
const avatar = avatarEl?.src || null;
// Check verified status
const verifiedEl = cell.querySelector('svg[aria-label*="Verified"]');
const verified = !!verifiedEl;
// Check if they follow back
// Look for "Follows you" indicator
const followsBackIndicator = cell.querySelector('[data-testid="userFollowIndicator"]');
const cellText = cell.textContent || '';
const followsBack = !!(followsBackIndicator || cellText.includes('Follows you'));
return {
username,
name,
bio,
verified,
avatar,
followsBack,
scrapedAt: new Date().toISOString(),
};
} catch {
return null;
}
}).filter(Boolean);
});
const prevSize = following.size;
// Add to map (automatically dedupes by username)
users.forEach(user => {
if (!following.has(user.username)) {
following.set(user.username, user);
}
});
// Fire progress callback if provided
if (onProgress) {
const current = Array.from(following.values());
const mutualCount = current.filter(u => u.followsBack).length;
onProgress({
scraped: following.size,
limit,
percent: Math.round((following.size / limit) * 100),
mutual: mutualCount,
});
}
// Check if we're stuck (no new users found)
if (following.size === prevSize) {
retries++;
// Longer wait when stuck
await new Promise(r => setTimeout(r, scrollDelay + 1000));
} else {
retries = 0;
}
// Scroll down to trigger lazy loading
await page.evaluate(() => {
window.scrollTo(0, document.body.scrollHeight);
});
// Random delay between scrolls
await new Promise(r => setTimeout(r, scrollDelay + Math.random() * 500));
}
// Convert to array and limit
const result = Array.from(following.values()).slice(0, limit);
// Calculate final stats
const mutualCount = result.filter(u => u.followsBack).length;
const oneWayCount = result.length - mutualCount;
console.log(`\n✅ Scraped ${result.length} following`);
console.log(`🤝 Mutual: ${mutualCount} | ➡️ One-way: ${oneWayCount}`);
return result;
} finally {
await browser.close();
}
}
/**
* Export data to JSON file
* @param {Array} data - Data to export
* @param {string} filename - Output filename
*/
async function exportJSON(data, filename) {
const output = {
exportedAt: new Date().toISOString(),
count: data.length,
mutualCount: data.filter(u => u.followsBack).length,
oneWayCount: data.filter(u => !u.followsBack).length,
data,
};
await fs.writeFile(filename, JSON.stringify(output, null, 2));
console.log(`💾 Saved JSON to ${filename}`);
}
/**
* Export data to CSV file
* @param {Array} data - Data to export
* @param {string} filename - Output filename
*/
async function exportCSV(data, filename) {
const headers = ['username', 'name', 'bio', 'verified', 'followsBack', 'avatar', 'scrapedAt'];
const escapeCSV = (val) => {
if (val === null || val === undefined) return '';
if (typeof val === 'boolean') return val ? 'true' : 'false';
if (typeof val === 'string') {
// Escape quotes and wrap in quotes if contains comma, quote, or newline
const escaped = val.replace(/"/g, '""').replace(/\n/g, ' ');
if (escaped.includes(',') || escaped.includes('"') || escaped.includes('\n')) {
return `"${escaped}"`;
}
return escaped;
}
return String(val);
};
const rows = data.map(row =>
headers.map(h => escapeCSV(row[h])).join(',')
);
const csv = [headers.join(','), ...rows].join('\n');
await fs.writeFile(filename, csv);
console.log(`💾 Saved CSV to ${filename}`);
}
/**
* Export only mutual follows
*/
async function exportMutualOnly(data, baseFilename) {
const mutuals = data.filter(u => u.followsBack);
const jsonFile = `${baseFilename}-mutuals.json`;
const csvFile = `${baseFilename}-mutuals.csv`;
await exportJSON(mutuals, jsonFile);
await exportCSV(mutuals, csvFile);
console.log(`\n🤝 Exported ${mutuals.length} mutual follows separately`);
}
/**
* Export only one-way follows (they don't follow back)
*/
async function exportOneWayOnly(data, baseFilename) {
const oneWay = data.filter(u => !u.followsBack);
const jsonFile = `${baseFilename}-oneway.json`;
const csvFile = `${baseFilename}-oneway.csv`;
await exportJSON(oneWay, jsonFile);
await exportCSV(oneWay, csvFile);
console.log(`\n➡️ Exported ${oneWay.length} one-way follows separately`);
}
// ============================================
// CLI Execution
// ============================================
const args = process.argv.slice(2);
const username = args[0];
const limit = parseInt(args[1]) || 500;
const exportType = args[2] || 'all'; // 'all', 'mutual', 'oneway'
if (!username) {
console.log(`
╔══════════════════════════════════════════════════════════════╗
║ XActions - Following Scraper ║
║ Author: nich (@nichxbt) ║
╚══════════════════════════════════════════════════════════════╝
Usage: node scrape-following.js <username> [limit] [export-type]
Arguments:
username Twitter/X username (without @)
limit Maximum accounts to scrape (default: 500)
export-type What to export: 'all', 'mutual', 'oneway' (default: all)
Examples:
node scrape-following.js elonmusk 1000
node scrape-following.js naval 500 mutual
node scrape-following.js pmarca 200 oneway
node scrape-following.js vitalikbuterin
Output Files:
{username}-following-{date}.json Full data as JSON
{username}-following-{date}.csv Full data as CSV
{username}-following-{date}-mutuals.json Mutual follows only
{username}-following-{date}-oneway.json One-way follows only
Environment Variables:
X_AUTH_TOKEN Your X auth token for private accounts
`);
process.exit(1);
}
// Get auth token from environment if available
const authToken = process.env.X_AUTH_TOKEN || null;
// Run the scraper with progress indicator
console.log('');
console.log('═'.repeat(60));
scrapeFollowing(username, {
limit,
authToken,
onProgress: ({ scraped, limit, percent, mutual }) => {
process.stdout.write(`\r📈 Progress: ${scraped}/${limit} (${percent}%) | 🤝 Mutual: ${mutual}`);
},
})
.then(async (following) => {
console.log('\n');
console.log('═'.repeat(60));
// Calculate detailed stats
const mutualFollows = following.filter(u => u.followsBack);
const oneWayFollows = following.filter(u => !u.followsBack);
const verified = following.filter(u => u.verified);
const withBio = following.filter(u => u.bio);
// Print summary
console.log('\n📊 Summary:');
console.log('─'.repeat(40));
console.log(` Total scraped: ${following.length}`);
console.log(` 🤝 Mutual follows: ${mutualFollows.length} (${Math.round(mutualFollows.length / following.length * 100)}%)`);
console.log(` ➡️ One-way: ${oneWayFollows.length} (${Math.round(oneWayFollows.length / following.length * 100)}%)`);
console.log(` ✓ Verified: ${verified.length}`);
console.log(` ✓ With bio: ${withBio.length}`);
console.log('─'.repeat(40));
// Generate base filename
const date = new Date().toISOString().split('T')[0];
const baseFilename = `${username}-following-${date}`;
// Export based on type
console.log('\n📁 Exporting files...\n');
await exportJSON(following, `${baseFilename}.json`);
await exportCSV(following, `${baseFilename}.csv`);
if (exportType === 'all' || exportType === 'mutual') {
await exportMutualOnly(following, baseFilename);
}
if (exportType === 'all' || exportType === 'oneway') {
await exportOneWayOnly(following, baseFilename);
}
// Show sample data
console.log('\n📋 Sample mutual follows (first 5):');
mutualFollows.slice(0, 5).forEach(f => {
console.log(` @${f.username} ${f.verified ? '✓' : ''} - ${f.name || 'No name'}`);
});
console.log('\n📋 Sample one-way follows (first 5):');
oneWayFollows.slice(0, 5).forEach(f => {
console.log(` @${f.username} ${f.verified ? '✓' : ''} - ${f.name || 'No name'}`);
});
console.log('\n✨ Done!\n');
})
.catch((error) => {
console.error('\n❌ Error:', error.message);
process.exit(1);
});
Usage
# Basic usage - scrape 500 following
node scrape-following.js elonmusk
# Scrape 1000 following
node scrape-following.js elonmusk 1000
# Scrape and export only mutual follows
node scrape-following.js naval 500 mutual
# Scrape and export only one-way follows
node scrape-following.js pmarca 200 oneway
# Use with auth token for private accounts
X_AUTH_TOKEN="your_token_here" node scrape-following.js username 500
Output
═══════════════════════════════════════════════════════════════
🔍 Scraping following for @elonmusk
📊 Limit: 1000
🕐 Started: 2:30:45 PM
🌐 Navigating to https://x.com/elonmusk/following
📈 Progress: 1000/1000 (100%) | 🤝 Mutual: 342
═══════════════════════════════════════════════════════════════
✅ Scraped 1000 following
🤝 Mutual: 342 | ➡️ One-way: 658
📊 Summary:
────────────────────────────────────────
Total scraped: 1000
🤝 Mutual follows: 342 (34%)
➡️ One-way: 658 (66%)
✓ Verified: 156
✓ With bio: 891
────────────────────────────────────────
📁 Exporting files...
💾 Saved JSON to elonmusk-following-2026-01-01.json
💾 Saved CSV to elonmusk-following-2026-01-01.csv
🤝 Exported 342 mutual follows separately
💾 Saved JSON to elonmusk-following-2026-01-01-mutuals.json
💾 Saved CSV to elonmusk-following-2026-01-01-mutuals.csv
➡️ Exported 658 one-way follows separately
💾 Saved JSON to elonmusk-following-2026-01-01-oneway.json
💾 Saved CSV to elonmusk-following-2026-01-01-oneway.csv
📋 Sample mutual follows (first 5):
@kloris ✓ - Kloris Wong
@jason ✓ - Jason Calacanis
@pmarca ✓ - Marc Andreessen
@naval - Naval Ravikant
@balajis ✓ - Balaji Srinivasan
📋 Sample one-way follows (first 5):
@SpaceX ✓ - SpaceX
@Tesla ✓ - Tesla
@boring_company - The Boring Company
@neuralink ✓ - Neuralink
@xAI ✓ - xAI
✨ Done!
💡 Use Cases
1. Finding Mutual Follows
Identify who has a reciprocal relationship:
// After scraping, filter mutual follows
const mutuals = following.filter(u => u.followsBack);
console.log(`Found ${mutuals.length} mutual follows!`);
// Export just the usernames
const mutualUsernames = mutuals.map(u => `@${u.username}`);
2. Analyzing Someone's Network
Understand who an influencer or competitor follows:
// Analyze verified accounts they follow
const verifiedFollowing = following.filter(u => u.verified);
console.log(`They follow ${verifiedFollowing.length} verified accounts`);
// Find accounts with specific keywords in bio
const cryptoAccounts = following.filter(u =>
u.bio?.toLowerCase().includes('crypto') ||
u.bio?.toLowerCase().includes('bitcoin') ||
u.bio?.toLowerCase().includes('web3')
);
3. Find Potential Unfollows
Identify accounts that don't follow back (candidates for cleanup):
// Get accounts that don't follow back
const notFollowingBack = following.filter(u => !u.followsBack);
console.log(`${notFollowingBack.length} accounts don't follow back`);
console.log('Consider unfollowing:');
notFollowingBack.forEach(u => console.log(` @${u.username}`));
4. Compare Following Lists
Track changes over time:
// compare-following.js
import fs from 'fs/promises';
async function compareFollowing(oldFile, newFile) {
const oldData = JSON.parse(await fs.readFile(oldFile, 'utf-8'));
const newData = JSON.parse(await fs.readFile(newFile, 'utf-8'));
const oldSet = new Set(oldData.data.map(u => u.username));
const newSet = new Set(newData.data.map(u => u.username));
const unfollowed = oldData.data.filter(u => !newSet.has(u.username));
const newlyFollowed = newData.data.filter(u => !oldSet.has(u.username));
console.log(`\n🔴 Unfollowed ${unfollowed.length} accounts:`);
unfollowed.forEach(u => console.log(` @${u.username}`));
console.log(`\n🟢 Newly followed ${newlyFollowed.length} accounts:`);
newlyFollowed.forEach(u => console.log(` @${u.username}`));
// Check for mutual status changes
const bothHave = newData.data.filter(u => oldSet.has(u.username));
const lostMutual = bothHave.filter(newU => {
const oldU = oldData.data.find(o => o.username === newU.username);
return oldU?.followsBack && !newU.followsBack;
});
if (lostMutual.length > 0) {
console.log(`\n⚠️ ${lostMutual.length} accounts stopped following back:`);
lostMutual.forEach(u => console.log(` @${u.username}`));
}
}
// Usage: node compare-following.js old.json new.json
5. Build a Follow Recommendation Engine
Find accounts followed by multiple people you track:
// Scrape following for multiple accounts, then find overlap
const followingLists = {
'naval': navalFollowing,
'balajis': balajiFollowing,
'pmarca': pmarcaFollowing,
};
const followCounts = new Map();
Object.values(followingLists).forEach(list => {
list.forEach(user => {
const count = followCounts.get(user.username) || 0;
followCounts.set(user.username, count + 1);
});
});
// Find accounts followed by all three
const followedByAll = Array.from(followCounts.entries())
.filter(([_, count]) => count === 3)
.map(([username]) => username);
console.log('Accounts followed by all three:', followedByAll);
📊 Performance Tips
| Following Count | Estimated Time |
|---|---|
| 100 | ~30 seconds |
| 500 | ~2-3 minutes |
| 1,000 | ~5-6 minutes |
| 5,000 | ~20-25 minutes |
| 10,000+ | ~45+ minutes |
Optimizations
// For large accounts, increase scroll delay to avoid rate limits
const following = await scrapeFollowing('largAccount', {
limit: 10000,
scrollDelay: 2500, // Slower, safer
});
// For faster scraping of smaller accounts
const following = await scrapeFollowing('smallAccount', {
limit: 200,
scrollDelay: 1000, // Faster
});
Rate Limit Tips
- ⏱️ Don't scrape the same account more than once per hour
- 🔄 Add 5-10 second delays between different account scrapes
- 🔐 Use auth token for higher limits
- 🌙 Run large scrapes during off-peak hours
🌐 Website Alternative
Don't want to code? Use xactions.app:
- 🔐 Login with your X account
- 👤 Enter any public username
- 📋 Click "Scrape Following"
- ⏳ Wait for completion
- 📥 Download as CSV or JSON
- 🤝 Filter by mutual/one-way directly in the UI
Features on xactions.app:
- ✅ No coding required
- ✅ Cloud-based processing
- ✅ Automatic mutual detection
- ✅ Export to multiple formats
- ✅ Schedule recurring scrapes
- ✅ Compare historical data
🔗 Related Guides
- Followers Scraping - Scrape who follows an account
- Unfollow Non-Followers - Clean up your following list
- Profile Scraping - Get detailed profile information
Built with ❤️ by @nichxbt
Part of the XActions toolkit
⚡ Ready to try Following Scraping?
XActions is 100% free and open-source. No API keys, no fees, no signup.
Browse All Scripts