DEV Community

Cover image for I Built a LinkedIn Spam Filter in a Weekend (And You Can Too)
Kevin Tran
Kevin Tran

Posted on

I Built a LinkedIn Spam Filter in a Weekend (And You Can Too)

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"
  }]
}
Enter fullscreen mode Exit fullscreen mode

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 
});
Enter fullscreen mode Exit fullscreen mode

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';
}
Enter fullscreen mode Exit fullscreen mode

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);
}
Enter fullscreen mode Exit fullscreen mode

Performance Optimization

Initial version was slow (200ms per invitation). Optimizations:

  1. Cache keyword processing: Lowercase keywords once, not on every check
  2. Mark processed nodes: Use dataset.processed to avoid re-scanning
  3. 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 
});
Enter fullscreen mode Exit fullscreen mode

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

  1. Simple > Complex: I almost built an ML classifier. Keyword matching works for 85% of cases.

  2. Performance matters: Users notice 200ms delays. They don't notice 15ms.

  3. Privacy sells: "No tracking" was the most-mentioned feature in reviews.

  4. 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)