A job seeker posted in Hacker News’s monthly “Who Wants to Be Hired?” thread. Two hours later, they received a cold recruitment email that mirrored their post’s keywords (TypeScript, Python, LLMs, RAG, agent orchestration) but completely missed the context. They were looking for work, not offering it.
The post drew 946 points and 266 comments. The real issue is that agents parse text without understanding conversational intent, trigger on keywords without semantic classification, and execute actions without approval layers. This represents a common failure mode in autonomous outreach systems.
When you build a scraper, attach an LLM classifier, and wire it directly to an email sender, you create infrastructure that parses without understanding, matches without reasoning, and acts without judgment.
How Recruitment Bots Parse Public Forums
Hacker News publishes monthly hiring threads with predictable URLs and no authentication requirement. A basic scraper needs three components:
- Thread discovery: Poll
/item?id=<known_pattern>or watch the front page for posts matching “Who wants to be hired” or “Who is hiring.” - Comment extraction: Parse the Algolia API or scrape HTML for top-level comments (job posts or candidate profiles).
- Contact extraction: Regex for email addresses in comment text or profile pages.
Rate limits exist but are loose. HN’s robots.txt allows crawling. The Algolia API returns JSON without authentication. A polite scraper can pull 500 comments per thread without triggering defenses.
The bottleneck is classification: which comments represent candidates, which represent employers, and which keywords indicate a match worth acting on.
Trigger Logic: Keyword Matching vs. Semantic Classification
Most recruitment bots use one of three trigger methods:
| Method | Implementation | Accuracy Trade-off | Cost per Match |
|---|---|---|---|
| Keyword regex | if "Python" in text and "LLM" in text | Fastest but least accurate; high false positives | $0 |
| Embedding similarity | Cosine distance between post and job description embeddings | Moderate accuracy; reduces false positives | $0.0001 |
| Full LLM classification | GPT-4 prompt: “Is this person looking for work or offering work?” | Most accurate; lowest false positives | $0.01-0.05 |
Based on typical implementation costs; specific rates vary by provider and model.
The incident described in the HN post suggests keyword matching. The bot extracted “TypeScript,” “Python,” “LLMs,” “RAG,” and “agent orchestration” from the candidate’s post and matched them against a recruiter’s job description. It never asked whether the author was a candidate or an employer.
Embedding similarity would have reduced false positives by comparing the semantic shape of the post to known job-seeker profiles. But embeddings cost tokens and add latency. Most spam operations optimize for speed and volume, not precision.
Full LLM classification would have caught the context error. A prompt like “Does this person want a job or want to hire someone?” would return the correct answer most of the time. But at $0.02 per classification and 500 comments per thread, that’s $10 per scrape. Keyword matching costs nothing.
The Action Boundary: Draft vs. Send
The critical infrastructure question is where the approval layer sits. Does the agent:
- Draft emails for human review (safe but slow)
- Send emails automatically after classification (fast but risky)
- Send emails with a confidence threshold (middle ground, still risky)
The HN incident suggests full automation. The email arrived two hours after the post, which is consistent with a cron job running every hour, scraping new comments, classifying them, and sending emails in batch.
A safer architecture inserts a human approval step. Store drafts in Airtable or Redis. Trigger a Slack notification or dashboard alert. Only send when a human clicks “Approve.” This pattern works with any queue backend:
import requests
from datetime import datetime
# Scrape HN thread via Algolia API
def scrape_thread(thread_id):
url = f"https://hn.algolia.com/api/v1/items/{thread_id}"
response = requests.get(url)
return response.json()
# Extract candidate emails and skills
def extract_candidates(thread_data):
candidates = []
for comment in thread_data.get("children", []):
text = comment.get("text", "")
if "@" in text and "looking for" in text.lower():
candidates.append({
"email": extract_email(text),
"skills": extract_skills(text),
"text": text,
"timestamp": datetime.now().isoformat()
})
return candidates
# Push to approval queue (Airtable example)
def queue_for_approval(candidate, airtable_base_id, airtable_api_key):
url = f"https://api.airtable.com/v0/{airtable_base_id}/Drafts"
headers = {
"Authorization": f"Bearer {airtable_api_key}",
"Content-Type": "application/json"
}
payload = {
"fields": {
"Email": candidate["email"],
"Skills": ", ".join(candidate["skills"]),
"Draft": generate_email_draft(candidate),
"Status": "Pending Review"
}
}
requests.post(url, json=payload, headers=headers)
# Human reviews in Airtable, changes Status to "Approved"
# Separate process sends only approved drafts
But approval queues add friction. If you are running a lead-gen operation at scale, you want zero human touches. The entire value proposition is automation. So the approval layer gets skipped, and the agent sends directly.
Rate Limits and Anti-Spam Defenses
Hacker News does not block scrapers aggressively, but email providers do. Sending 500 cold emails per day from a single domain will trigger spam filters within 48 hours. Sophisticated operations use:
- Rotating sender domains: Register 10-20 domains, rotate daily.
- Warm-up sequences: Send 10 emails/day for two weeks before scaling.
- Personalization tokens: Insert recipient name, post excerpt, or thread URL to avoid identical message hashes.
- SMTP relay services: Use SendGrid, Mailgun, or AWS SES with separate IP pools.
The HN post does not describe bounce rates or deliverability, but the fact that the email arrived suggests the sender has working infrastructure. They are not a first-time spammer. They have done this before.
Role Detection Without Explicit Prompting
LLMs have context windows measured in tokens, not semantic understanding. A GPT-4 model can ingest 128k tokens, but it does not automatically understand conversational norms or social context.
The recruiter’s agent likely used a prompt like:
Extract skills and contact info from this job post:
{comment_text}
The model returned:
{
"skills": ["TypeScript", "Python", "LLMs", "RAG", "agent orchestration"],
"email": "ilia@example.com",
"match_score": 0.92
}
The prompt never asked “Is this person hiring or looking for work?” So the model never answered. The agent treated every comment as a potential lead.
A better prompt would include explicit role detection:
Analyze this comment from a hiring thread.
Return:
- role: "candidate" or "employer"
- skills: list of technical skills mentioned
- contact: email address if present
Comment: {comment_text}
But even this fails if the model misclassifies. A candidate who writes “I build production-ready TypeScript systems” sounds like an employer. The model needs additional context: thread title, comment position, or user history.
Note: These prompts are illustrative. Test against real HN thread data before deployment.
Observability Gaps in Autonomous Outreach
The recruiter probably has no idea they are spamming job seekers. Their dashboard shows:
- 500 emails sent
- 50 opens (10% open rate)
- 5 replies (1% reply rate)
They do not see:
- How many recipients were candidates vs. employers
- How many emails caused frustration or reputational damage
- How many recipients marked the email as spam
Observability in autonomous systems requires logging actions and outcomes. A production-grade outreach agent would track:
- Classification confidence: Log the model’s certainty for each decision.
- Feedback loops: Parse replies for negative sentiment (“I’m looking for work, not hiring”).
- Spam reports: Monitor bounce rates and spam complaints via SMTP headers.
- Manual audits: Sample 10% of sent emails for human review.
None of this is technically hard. It is just not prioritized when the goal is volume.
Failure Modes in Production
The HN incident reveals three failure modes common in autonomous outreach systems:
- Context collapse: The agent parses text but ignores conversational structure (thread title, user role, post intent).
- Action without approval: The agent sends emails directly instead of drafting for review.
- No feedback loop: The agent does not learn from negative replies or spam reports.
These are orchestration failures, not LLM limitations. The infrastructure exists to solve all three problems:
- Context collapse: Pass thread metadata (title, user history, comment position) to the classifier.
- Action without approval: Insert a human-in-the-loop step before sending.
- No feedback loop: Parse replies, log sentiment, and adjust classification thresholds.
But each solution adds latency, cost, or complexity. So they get skipped.
Technical Verdict
Recruitment bots are technically feasible but operationally dangerous without explicit role detection and mandatory approval layers. The infrastructure gap is not the LLM’s ability to parse text. It is the absence of context-aware classification prompts and human checkpoints between detection and action. The HN incident demonstrates that keyword matching plus direct email sending produces spam at scale. The fix requires three changes: add thread metadata to classification prompts, insert an approval queue (Airtable, Notion, or custom dashboard) before any send action, and monitor SMTP feedback for misclassification signals. For mech.app users building outreach agents, the recommendation is simple: if you cannot afford full LLM classification with role detection ($0.02 per contact) and a human approval step, do not automate outreach.
Guardrails for Autonomous Outreach
Use autonomous scraping and outreach when:
- You have explicit permission to contact the audience (newsletter subscribers, event attendees, API partners).
- You can afford full LLM classification ($0.02-0.05 per contact) with role detection prompts that include thread context.
- You implement a mandatory human approval layer. Use Airtable as a draft queue with status fields (“Pending,” “Approved,” “Rejected”). Trigger sends only on manual approval via Zapier or custom webhook.
- You monitor SMTP feedback in real time. Parse bounce codes (5.7.1 for spam blocks),
Feedback-Loopheaders from Gmail/Outlook, and reply sentiment. Log these as classification errors and adjust thresholds weekly. - You log every classification decision with confidence scores, thread metadata, and timestamps for post-incident review.
Avoid it when:
- You are scraping public forums without understanding conversational context or thread structure.
- You optimize for volume over precision. If your goal is 500 emails/day, you will spam people.
- You have no feedback loop to detect misclassification. If you cannot parse “I’m looking for work, not hiring” replies and flag them as errors, do not automate.
- You are targeting vulnerable populations (job seekers, people in crisis, anyone in a power-down position).
Deployment checklist for mech.app users:
Before deploying an outreach agent, verify:
- Role detection prompt tested on 50+ real HN comments with manual validation of results.
- Approval queue implemented (Airtable, Notion, or custom dashboard with explicit “Send” action).
- SMTP feedback monitoring active (bounce codes, spam complaints, reply parsing).
- Weekly audit process documented (sample 10% of sent emails, log false positives, retrain prompts).
- Rate limits configured (start at 10 emails/day, scale only after two weeks of clean delivery).
The HN incident is a cautionary example. If you wire a scraper to an LLM to an email sender without approval layers, you will spam people. The fix is architectural. Add role detection to your classifier prompt. Insert an approval queue. Parse SMTP feedback. Audit weekly. The infrastructure to build a respectful outreach agent exists. The challenge is that respectful agents are slower and more expensive than spammy ones. Most operators choose speed. If you are building an agent that sends emails, add an approval layer.
Source Links
- Hacker News discussion (946 points, 266 comments)