I Built a LinkedIn Spam Filter in a Weekend (And You Can Too)
The Problem
LinkedIn has become unusable for job seekers.
Last month, I received 47 connection requests. 19 were "life coaches." 8 were forex traders. 6 were MLM recruiters. Only 7 were actual talent acquisition professionals.
I missed 2 legitimate recruiter messages because they were buried under spam.
The Solution
I built Request Radar - a Chrome extension that automatically labels LinkedIn invitations:
- π’ Recruiter - Don't miss these
- π΄ Spam - Avoid these
- π΅ Normal - Consider these
How It Works
1. Content Script Injection
// manifest.json
{
"content_scripts": [{
"matches": ["https://www.linkedin.com/*"],
"js": ["content.js"],
"run_at": "document_idle"
}]
}
The extension injects a content script into LinkedIn pages. We use document_idle to ensure LinkedIn's content has loaded.
2. Detecting New Invitations
LinkedIn loads invitations dynamically. We use MutationObserver to watch for new content:
const observer = new MutationObserver(() => {
scanInvitations();
});
observer.observe(document.body, {
childList: true,
subtree: true
});
3. Classification Logic
Simple keyword matching:
function classifyInvitation(headline) {
const recruiterKeywords = [
'recruiter', 'talent acquisition',
'hiring manager', 'headhunter'
];
const spamKeywords = [
'life coach', 'passive income',
'financial freedom', 'dm me'
];
let recruiterScore = 0;
let spamScore = 0;
const text = headline.toLowerCase();
recruiterKeywords.forEach(keyword => {
if (text.includes(keyword)) recruiterScore++;
});
spamKeywords.forEach(keyword => {
if (text.includes(keyword)) spamScore++;
});
if (recruiterScore > spamScore && recruiterScore > 0) {
return 'recruiter';
} else if (spamScore > recruiterScore && spamScore > 0) {
return 'spam';
}
return 'normal';
}
4. Adding Visual Badges
function addBadge(card, type) {
const badge = document.createElement('div');
badge.className = 'radar-badge';
const config = {
recruiter: { emoji: 'π’', label: 'Recruiter', color: '#28a745' },
spam: { emoji: 'π΄', label: 'Spam', color: '#dc3545' },
normal: { emoji: 'π΅', label: 'Normal', color: '#0077b5' }
};
const { emoji, label, color } = config[type];
badge.style.cssText = `
position: absolute;
top: 10px;
right: 10px;
background: ${color};
color: white;
padding: 8px 14px;
border-radius: 20px;
font-weight: 700;
`;
badge.innerHTML = `${emoji} ${label}`;
card.appendChild(badge);
}
Performance Optimization
Initial version was slow (200ms per invitation). Optimizations:
- Cache keyword processing: Lowercase keywords once, not on every check
-
Mark processed nodes: Use
dataset.processedto avoid re-scanning - Debounce mutations: Don't process every single DOM change
Result: 15ms per invitation (13x faster)
Privacy by Design
No external API calls. No tracking. No data collection.
All settings stored locally using Chrome's Storage API:
chrome.storage.sync.set({
settings: userSettings
});
This syncs across user's devices via Chrome's encrypted sync.
Results
Week 1: 1,000+ users
Week 2: 3,000+ users
Average rating: 4.8/5
Top feedback: "This should be built into LinkedIn"
What I Learned
Simple > Complex: I almost built an ML classifier. Keyword matching works for 85% of cases.
Performance matters: Users notice 200ms delays. They don't notice 15ms.
Privacy sells: "No tracking" was the most-mentioned feature in reviews.
Distribution is everything: Building took 16 hours. Marketing took 40 hours.
Try It Yourself
Chrome Web Store: https://chromewebstore.google.com/detail/Request%20Radar/pmpkcbnkoojpenphcempidfgkjkemife
What's Next?
Planning to add:
- Analytics dashboard (local-only)
- Better classification (context-aware)
- Premium tier ($3.99/mo) with advanced features
Build Your Own
Want to create a similar extension? Here's the tech stack:
- Vanilla JavaScript (no frameworks)
- Chrome Extension Manifest V3
- MutationObserver API
- Chrome Storage API
For me, it wasn't "detect spam" - it was "don't miss opportunities."
What obvious-but-unbuilt tool will you create?
Questions? Drop them in the comments!
Top comments (0)