Skip to content

How LLMs Detect Spam: AI-Powered Email Classification Explained

The Problem

My Gmail inbox had 15,000 unread emails. I tried the traditional approach: filtering by sender, marking spam, creating rules. But new spam kept slipping through. Traditional filters use static rules that spammers easily evade.

Then I asked Claude Code to help me clean up my inbox. What happened next changed how I think about spam detection entirely.

Me: "How would you search for spam in my emails?"
Claude: "Let me teach you about email headers and what to look for..."

Claude didn’t just delete emails—it explained the signals spam leaves behind, grouped my emails by spam type, and showed me a classification system that adapts to new spam tactics.

What Traditional Spam Filters Miss

Traditional filters rely on three approaches:

  1. Static rule lists - Easy to evade by changing a few words
  2. Sender blacklists - Incomplete coverage, spammers rotate domains
  3. Keyword matching - False positives on legitimate emails

I’ve seen marketing emails flagged as spam while obvious phishing attempts landed in my inbox. The problem is that traditional filters don’t understand context.

LLMs change this entirely. They understand:

  • Context - “This looks like a receipt you need to keep”
  • Patterns - “This newsletter matches ones you marked keep”
  • Value - “This promotional email has no value”
  • Risk - “This could be phishing, verify sender”

How LLMs Detect Spam

Claude taught me that LLMs analyze four key signals to classify emails.

Signal 1: Email Header Analysis

Email headers contain metadata that reveals spam origins. Claude showed me which headers matter:

Key Email Headers for Spam Detection
Received: Chain of mail servers (spoofing detection)
X-Spam-Score: Pre-computed spam likelihood (if available)
Return-Path: Bounce address vs From: mismatch = suspicious
X-Mailer: Spam tool signatures (bulk mailer names)
DKIM/SPF/DMARC: Authentication results (failed = likely spoofed)

Here’s how to extract and analyze headers:

header_analyzer.py
import email
from email import policy
def extract_spam_signals(msg_path):
"""Extract spam signals from email headers"""
with open(msg_path, 'rb') as f:
msg = email.message_from_binary_file(f, policy=policy.default)
signals = {
'from': msg['From'],
'return_path': msg['Return-Path'],
'received_chain': msg.get_all('Received', []),
'dkim': msg.get('DKIM-Signature') is not None,
'spf': 'pass' in (msg.get('Received-SPF') or '').lower(),
'x_mailer': msg.get('X-Mailer'),
'spam_score': msg.get('X-Spam-Score', '0')
}
# Check for spoofing: Return-Path differs from From
if signals['return_path'] and signals['from']:
from_domain = signals['from'].split('@')[-1].strip('>')
return_domain = signals['return_path'].split('@')[-1].strip('>')
signals['domain_mismatch'] = from_domain != return_domain
return signals

Claude identified that my spam emails often had Return-Path domains from .xyz or .top TLDs while the From address showed legitimate company names.

Signal 2: Content Pattern Recognition

LLMs detect spam by analyzing language patterns. Claude flagged these indicators:

Spam Content Indicators
Urgency Language:
- "ACT NOW" / "Limited time" / "Don't miss out"
- "Your account will be closed"
- "Immediate action required"
Money Promises:
- "You've won" / "Claim your prize"
- "Free money" / "Guaranteed returns"
- "Wire transfer" / "Bitcoin payment"
Suspicious Links:
- URL shorteners (bit.ly, tinyurl)
- Mismatched display text vs actual URL
- Domains with typos (amaz0n.com, paypa1.com)
Unsubscribe Issues:
- Missing List-Unsubscribe header
- Unsubscribe link leads to different domain
- Requires login to unsubscribe

I asked Claude to scan my inbox for these patterns:

content_scanner.py
import re
SPAM_PATTERNS = {
'urgency': [
r'act now',
r'limited time',
r'don\'?t miss',
r'immediate action',
r'expires (today|soon)',
],
'money_promises': [
r'you\'?ve? won',
r'claim your (prize|reward)',
r'free money',
r'guaranteed (return|profit)',
],
'suspicious_links': [
r'bit\.ly',
r'tinyurl',
r'click here',
]
}
def scan_email_content(body):
"""Scan email body for spam patterns"""
body_lower = body.lower()
signals = {}
for category, patterns in SPAM_PATTERNS.items():
matches = []
for pattern in patterns:
if re.search(pattern, body_lower):
matches.append(pattern)
if matches:
signals[category] = matches
return signals
# Claude output for my inbox:
# {
# 'urgency': ['act now', 'limited time'],
# 'money_promises': ['claim your prize'],
# 'suspicious_links': ['bit.ly']
# }

Signal 3: Sender Behavior Patterns

Claude indexed my senders and found patterns I couldn’t see manually:

Spam Sender Characteristics:
- High frequency: 5+ emails per week from same sender
- Zero engagement: Never opened any email from them
- No List-Unsubscribe header
- Professional tone ratio < 20% (mostly promotional)
- Domain age: Registered within last 6 months

Claude grouped my emails by sender and computed spam scores:

sender_analysis.py
from collections import defaultdict
from datetime import datetime
def analyze_sender_behavior(emails):
"""Analyze sender patterns across email history"""
senders = defaultdict(lambda: {
'count': 0,
'first_seen': None,
'last_seen': None,
'has_unsubscribe': False,
'opened_count': 0
})
for email in emails:
sender = email['from']
sender_data = senders[sender]
sender_data['count'] += 1
if 'List-Unsubscribe' in email['headers']:
sender_data['has_unsubscribe'] = True
# Track engagement
if email.get('opened'):
sender_data['opened_count'] += 1
# Track date range
date = datetime.parse(email['date'])
if not sender_data['first_seen'] or date < sender_data['first_seen']:
sender_data['first_seen'] = date
if not sender_data['last_seen'] or date > sender_data['last_seen']:
sender_data['last_seen'] = date
# Calculate spam probability
for sender, data in senders.items():
engagement_rate = data['opened_count'] / data['count'] if data['count'] > 0 else 0
frequency_score = min(data['count'] / 10, 1.0) # Normalize
# High frequency + low engagement + no unsubscribe = spam
data['spam_score'] = (
frequency_score * 0.3 +
(1 - engagement_rate) * 0.4 +
(0 if data['has_unsubscribe'] else 0.3)
)
return senders

Signal 4: Contextual Classification

The real power of LLMs is contextual understanding. Claude classified my emails into categories:

Claude's Classification Output
Category Count Example Action
------------------------------------------------
Receipts 234 Keep (financial records)
Newsletters 1,892 Review (some valuable)
Promotions 4,567 Bulk delete
Phishing 23 Delete + report
Personal 892 Keep
Notifications 567 Keep (security alerts)
Spam 3,234 Delete

I asked Claude to explain its reasoning for ambiguous cases:

Email: "Your Amazon order has shipped"
Claude: "This is a receipt. Contains order number, tracking link,
shipping address. Legitimate domain (amazon.com), DKIM passed.
Action: Keep"
Email: "ACT NOW: Your Amazon account needs verification"
Claude: "This is phishing. Urgency language, link leads to
amaz0n-verify.com (typosquatting), no DKIM signature.
Action: Delete + Report"

My Spam Detection Workflow

Claude walked me through a practical workflow for cleaning up my inbox:

+------------------+ +------------------+
| Export Emails | --> | Extract Headers |
+------------------+ +------------------+
|
v
+------------------+ +------------------+
| Group by Signal | --> | LLM Classify |
+------------------+ +------------------+
|
v
+------------------+ +------------------+
| Generate Index | --> | Review & Action |
+------------------+ +------------------+

Step 1: Extract Email Batch

extract_emails.py
import imaplib
import email
def fetch_emails(server, user, password, limit=1000):
"""Fetch emails with headers and body"""
mail = imaplib.IMAP4_SSL(server)
mail.login(user, password)
mail.select('inbox')
_, message_ids = mail.search(None, 'ALL')
emails = []
for msg_id in message_ids[0].split()[:limit]:
_, msg_data = mail.fetch(msg_id, '(RFC822)')
raw_email = msg_data[0][1]
msg = email.message_from_bytes(raw_email)
emails.append({
'id': msg_id.decode(),
'from': msg['From'],
'subject': msg['Subject'],
'date': msg['Date'],
'headers': dict(msg.items()),
'body': get_body(msg)
})
mail.logout()
return emails

Step 2: Ask LLM to Identify Spam Signals

I gave Claude a batch of emails and asked:

Analyze these emails and identify:
1. Spam signals in headers
2. Content patterns that indicate spam
3. Sender behavior anomalies
4. Recommended action for each

Claude responded with a detailed breakdown:

Claude's Analysis
Email ID: 1234
Signals:
- Return-Path: [email protected]
- Domain mismatch detected
- Body contains: "verify your account immediately"
- 3 urgency phrases found
Classification: Phishing attempt
Confidence: 95%
Action: Delete and report
Reason: Typosquatting domain, urgency language, no legitimate reason
for Amazon to ask for verification via email

Step 3: Group-by Classification

Claude grouped my emails by spam signal type:

classification_summary.py
classification_summary = {
'headers_suspicious': 892, # DKIM failed, domain mismatch
'urgency_language': 1234, # "Act now", "Limited time"
'money_promises': 567, # "You've won", "Claim prize"
'suspicious_links': 445, # Shortened URLs, mismatched
'low_engagement': 2341, # Never opened, high frequency
'legitimate_marketing': 1892, # Has unsubscribe, moderate frequency
'personal': 892, # From known contacts
'receipts': 234, # Order confirmations, invoices
}

Step 4: Generate Sender Index with Spam Scores

Claude created an index I could review:

Sender Spam Index
Sender Score Reason
--------------------------------------------------------
news@promo*.xyz 0.95 Bulk sender, no engagement
deals@marketing*.top 0.92 High frequency, urgency language
support@amaz0n*.com 0.98 Typosquatting, phishing
[email protected] 0.25 Has unsubscribe, opened before
[email protected] 0.05 Financial record, keep

Step 5: Review Edge Cases Before Bulk Action

Before deleting thousands of emails, Claude flagged edge cases for manual review:

Edge Cases for Manual Review
Email: "Your subscription is expiring"
Reason: Could be legitimate subscription you want to keep
Recommendation: Check if you still use this service
Email: "Invoice #12345 attached"
Reason: Might be a real invoice for business expenses
Recommendation: Verify vendor before deleting

Why LLM Spam Detection Works Better

The Reddit discussion that inspired this post highlighted the key advantage:

“I asked Claude how it would search for spam. It gave me a lesson on email headers and what to look for. It gave me a group-by count of my emails falling into those categories.”

Traditional filters can’t adapt. LLMs can:

CapabilityTraditional FilterLLM Classifier
New spam patternsRequires rule updateLearns from examples
Context understandingNoneHigh
ExplainabilityRule matchedNatural language reason
False positive handlingBinary (spam/not)Confidence scores
Phishing detectionPattern matchingContext analysis

Limitations to Keep in Mind

LLM spam detection isn’t perfect:

  1. Token cost - Analyzing thousands of emails costs money
  2. AI-generated spam - Spammers use LLMs too, making spam harder to detect
  3. False positives - Always review edge cases before bulk actions
  4. Not enterprise-ready - Don’t replace Gmail/Outlook spam filters for business

I use LLM classification as a second opinion, not my primary filter.

My Gmail Cleanup Results

After Claude’s analysis:

  • 15,000 emails reviewed in 2 hours
  • 8,234 deleted (promotions, spam, newsletters I never read)
  • 23 phishing attempts identified and reported
  • 6,766 kept (receipts, personal, legitimate newsletters)

The best part? Claude explained why each email was flagged, so I learned to spot spam patterns myself.

Summary

LLMs detect spam by analyzing four key signals: email headers, content patterns, sender behavior, and contextual relevance. Unlike traditional filters that rely on static rules, LLMs understand context and adapt to new spam tactics.

My workflow for inbox cleanup:

  1. Extract email batch with headers
  2. Ask LLM to identify spam signals
  3. Group by signal type
  4. Generate sender index with spam scores
  5. Review edge cases before bulk action

The result? A clean inbox and a better understanding of how spam actually works.

Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments