Capture the current X trends (name, post count, category), highlight watched keywords, and optionally re-scan on an interval.
How to Use
- Go to x.com/explore/tabs/trending (or x.com/explore)
- Open the browser console (F12 or Cmd+Option+I -> Console)
- (Optional) set CONFIG.watchKeywords and CONFIG.repeatIntervalMs
- Paste this entire script and press Enter
Default Configuration
const CONFIG = {
// Highlight any trend whose name contains one of these words (case-insensitive).
watchKeywords: [],
// Re-scan interval in ms. Set to 0 for a single one-off scan.
// 300000 = every 5 minutes. Stop with window.stopTrendingMonitor().
repeatIntervalMs: 0,
// Scroll passes per scan to load the full trends list.
scrollRounds: 4,
// Delay between scrolls (ms).
scrollDelay: 1400,
// Auto-download a JSON snapshot after each scan.
exportResults: true
};
Full Script
Copy and paste this entire script into your browser DevTools console on x.com.
// Copyright (c) 2024-2026 nich (@nichxbt). Licensed under the Apache License, Version 2.0.
/**
* ============================================
* 📡 Trending Monitor - XActions
* ============================================
*
* @name trending-monitor
* @description Capture the current X trends (name, post count, category), highlight watched keywords, and optionally re-scan on an interval.
* @author nichxbt
* @version 1.0.0
* @date 2026-07-20
* @website https://xactions.app
*
* Usage:
* 1. Go to x.com/explore/tabs/trending (or x.com/explore)
* 2. Open the browser console (F12 or Cmd+Option+I -> Console)
* 3. (Optional) set CONFIG.watchKeywords and CONFIG.repeatIntervalMs
* 4. Paste this entire script and press Enter
*
* Example:
* Set watchKeywords: ['solana','ai'] and repeatIntervalMs: 300000 to scan
* the trends now, flag any trend containing those words, then re-scan every
* 5 minutes. Each scan prints a ranked table and downloads a JSON snapshot.
* To stop the repeating monitor: window.stopTrendingMonitor()
*
* ============================================
*/
(async function trendingMonitor() {
'use strict';
// ============================================
// 📝 CONFIGURATION - Customize these options
// ============================================
const CONFIG = {
// Highlight any trend whose name contains one of these words (case-insensitive).
watchKeywords: [],
// Re-scan interval in ms. Set to 0 for a single one-off scan.
// 300000 = every 5 minutes. Stop with window.stopTrendingMonitor().
repeatIntervalMs: 0,
// Scroll passes per scan to load the full trends list.
scrollRounds: 4,
// Delay between scrolls (ms).
scrollDelay: 1400,
// Auto-download a JSON snapshot after each scan.
exportResults: true
};
// ============================================
// 🔧 SELECTORS
// ============================================
const SELECTORS = {
trend: '[data-testid="trend"]'
};
// ============================================
// 🗂️ CATEGORY KEYWORDS
// ============================================
const CATEGORIES = {
Tech: ['ai', 'chatgpt', 'openai', 'google', 'apple', 'microsoft', 'nvidia', 'coding', 'crypto', 'bitcoin', 'solana', 'web3', 'ethereum'],
Politics: ['trump', 'biden', 'congress', 'election', 'democrat', 'republican', 'government', 'senate'],
Sports: ['nfl', 'nba', 'mlb', 'soccer', 'football', 'basketball', 'fifa', 'ufc', 'f1', 'olympics'],
Entertainment: ['movie', 'music', 'album', 'netflix', 'disney', 'grammy', 'oscar', 'tour', 'concert'],
Business: ['stock', 'market', 'earnings', 'ipo', 'startup', 'economy', 'fed', 'inflation'],
Gaming: ['gaming', 'playstation', 'xbox', 'nintendo', 'steam', 'esports', 'minecraft']
};
const classify = (text) => {
if (!text) return 'Other';
const lower = text.toLowerCase();
for (const [category, words] of Object.entries(CATEGORIES)) {
if (words.some((w) => lower.includes(w))) return category;
}
return 'Other';
};
// ============================================
// 🛠️ HELPERS
// ============================================
const STORAGE_KEY = 'xactions_trends_history';
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
const log = {
info: (msg) => console.log(`ℹ️ ${msg}`),
success: (msg) => console.log(`✅ ${msg}`),
warning: (msg) => console.log(`⚠️ ${msg}`),
error: (msg) => console.log(`❌ ${msg}`)
};
const download = (data, filename) => {
try {
const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
log.success(`Snapshot downloaded: ${filename}`);
} catch (e) {
log.error(`Could not download snapshot: ${e.message}`);
}
};
const parsePostCount = (text) => {
if (!text) return 0;
const match = text.match(/([\d,.]+)\s*([KMB])?/i);
if (!match) return 0;
let num = parseFloat(match[1].replace(/,/g, ''));
if (Number.isNaN(num)) return 0;
const mult = { K: 1e3, M: 1e6, B: 1e9 };
if (match[2]) num *= mult[match[2].toUpperCase()];
return Math.round(num);
};
const loadHistory = () => {
try {
return JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]');
} catch {
return [];
}
};
const saveSnapshot = (trends) => {
try {
const history = loadHistory();
history.push({
timestamp: new Date().toISOString(),
trends: trends.map((t) => ({ rank: t.rank, topic: t.topic, postCount: t.postCount, category: t.category }))
});
localStorage.setItem(STORAGE_KEY, JSON.stringify(history.slice(-50)));
} catch (e) {
log.warning(`Could not save history: ${e.message}`);
}
};
// Read one trends scan from the DOM. Each trend cell holds several spans;
// the topic is the longest non-metadata span and the post count is the
// span that mentions "posts"/"Tweets".
const scrapeTrends = async () => {
const trends = [];
const seen = new Set();
for (let round = 0; round < CONFIG.scrollRounds; round++) {
document.querySelectorAll(SELECTORS.trend).forEach((cell) => {
let topic = '';
let postCountRaw = '';
cell.querySelectorAll('span').forEach((span) => {
const text = span.textContent.trim();
if (/[\d,.]+[KMB]?\s*(posts|tweets)/i.test(text)) {
postCountRaw = text;
return;
}
const isMeta = text.includes('·') || /Trending|Only on X|Promoted/i.test(text) || /posts|tweets/i.test(text) || /^\d+$/.test(text);
if (!isMeta && text.length > 1 && text.length < 80 && text.length > topic.length) {
topic = text;
}
});
if (!topic || seen.has(topic.toLowerCase())) return;
seen.add(topic.toLowerCase());
trends.push({
rank: trends.length + 1,
topic,
category: classify(topic),
postCount: parsePostCount(postCountRaw),
postCountRaw: postCountRaw || null
});
});
window.scrollTo(0, document.body.scrollHeight);
await sleep(CONFIG.scrollDelay);
}
return trends;
};
const compareWithPrevious = (trends) => {
const history = loadHistory();
if (history.length < 2) return;
const prev = history[history.length - 2];
const prevMap = new Map(prev.trends.map((t) => [t.topic.toLowerCase(), t]));
const fresh = [];
const rising = [];
trends.forEach((t) => {
const p = prevMap.get(t.topic.toLowerCase());
if (!p) fresh.push(t);
else if (t.rank < p.rank) rising.push({ ...t, prevRank: p.rank, change: p.rank - t.rank });
});
if (fresh.length > 0) {
console.log('');
console.log(` 🆕 NEW since last scan (${fresh.length}):`);
fresh.slice(0, 8).forEach((t) => console.log(` #${t.rank} ${t.topic} [${t.category}]`));
}
if (rising.length > 0) {
console.log('');
console.log(` 📈 RISING (${rising.length}):`);
rising.sort((a, b) => b.change - a.change).slice(0, 5).forEach((t) => {
console.log(` ↑${t.change} ${t.topic} (#${t.prevRank} -> #${t.rank})`);
});
}
};
// ============================================
// 🎯 MAIN LOGIC
// ============================================
const runScan = async (scanNo) => {
console.log('');
console.log('━'.repeat(60));
console.log(`📡 TRENDS SCAN ${scanNo} at ${new Date().toLocaleTimeString()}`);
console.log('━'.repeat(60));
const trends = await scrapeTrends();
if (trends.length === 0) {
log.error('No trends found. Go to x.com/explore/tabs/trending and make sure trends are visible.');
return null;
}
trends.forEach((t) => {
const count = t.postCountRaw ? ` (${t.postCountRaw})` : '';
console.log(` ${String(t.rank).padStart(2)}. [${t.category.padEnd(13)}] ${t.topic}${count}`);
});
// Category distribution.
const catCounts = {};
trends.forEach((t) => { catCounts[t.category] = (catCounts[t.category] || 0) + 1; });
console.log('');
console.log(' 📊 CATEGORY MIX:');
Object.entries(catCounts).sort((a, b) => b[1] - a[1]).forEach(([cat, count]) => {
const bar = '█'.repeat(Math.round((count / trends.length) * 20));
console.log(` ${cat.padEnd(14)} ${count} (${((count / trends.length) * 100).toFixed(0)}%) ${bar}`);
});
// Watched keyword highlights.
const matches = [];
if (CONFIG.watchKeywords.length > 0) {
trends.forEach((t) => {
CONFIG.watchKeywords.forEach((kw) => {
if (t.topic.toLowerCase().includes(kw.toLowerCase())) {
matches.push({ topic: t.topic, keyword: kw, rank: t.rank, postCount: t.postCount });
}
});
});
console.log('');
console.log(' 🔔 WATCHED KEYWORDS:');
if (matches.length === 0) {
console.log(' No watched keywords in the current trends.');
} else {
matches.forEach((m) => console.log(` 🚨 "${m.topic}" matches "${m.keyword}" (rank #${m.rank})`));
}
}
saveSnapshot(trends);
compareWithPrevious(trends);
const snapshot = {
scannedAt: new Date().toISOString(),
page: window.location.href,
totalTrends: trends.length,
watchKeywords: CONFIG.watchKeywords,
keywordMatches: matches,
categoryMix: catCounts,
trends
};
if (CONFIG.exportResults) {
console.log('');
const stamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
download(snapshot, `xactions-trends-${stamp}.json`);
}
window.xactionsTrends = snapshot;
return snapshot;
};
console.log(`
╔══════════════════════════════════════════════════════════╗
║ 📡 TRENDING MONITOR - XActions ║
║ 👤 Author: nichxbt ║
║ 🌐 https://xactions.app ║
╚══════════════════════════════════════════════════════════╝
`);
if (!/(^|\.)x\.com$/.test(location.hostname) && !/(^|\.)twitter\.com$/.test(location.hostname)) {
log.warning('Not on x.com. Open x.com/explore/tabs/trending and run again.');
return;
}
if (!/\/explore/.test(location.pathname)) {
log.warning('You are not on the Explore/Trends page. Go to x.com/explore/tabs/trending for the full list.');
log.info('Continuing anyway in case trends are rendered in a sidebar.');
}
if (CONFIG.watchKeywords.length > 0) log.info(`Watching keywords: ${CONFIG.watchKeywords.join(', ')}`);
// Stop switch for the repeating monitor.
let stopped = false;
window.stopTrendingMonitor = () => {
stopped = true;
log.warning('Stop requested. The monitor will exit after the current scan.');
};
// First scan always runs.
await runScan(1);
if (CONFIG.repeatIntervalMs > 0) {
log.info(`Repeat mode ON. Re-scanning every ${(CONFIG.repeatIntervalMs / 60000).toFixed(1)} min.`);
log.info('To stop: window.stopTrendingMonitor()');
let scanNo = 2;
while (!stopped) {
// Sleep in short slices so the stop switch responds quickly.
const slices = Math.max(1, Math.round(CONFIG.repeatIntervalMs / 1000));
for (let i = 0; i < slices && !stopped; i++) await sleep(1000);
if (stopped) break;
await runScan(scanNo++);
}
log.success('Trending monitor stopped.');
} else {
log.info('Single scan complete. Set CONFIG.repeatIntervalMs > 0 for continuous monitoring.');
}
return window.xactionsTrends;
})();
⚡ More XActions Scripts
Browse 300+ free browser scripts for X/Twitter automation. No API keys, no fees.
Browse All Scripts