Fix weak outreach personalization in n8n by adding a search enrichment step that pulls recent prospect activity before email generation. Generic personalization like using first names and company names gets ignored. Effective personalization references something specific: a recent blog post, a funding announcement, a product launch, or a conference talk. By querying the search API for each prospect before drafting the email, your n8n workflow can generate genuinely personalized openers that reference real, recent events.
Prerequisites
- n8n instance running (self-hosted or n8n Cloud)
- A Scavio API key from scavio.dev
- A lead list with names and companies
- An email sending node configured in n8n
Walkthrough
Step 1: Search for prospect activity
Query for the prospect's name and company to find recent public activity.
import os, requests
API_KEY = os.environ['SCAVIO_API_KEY']
def find_prospect_activity(name: str, company: str) -> list:
resp = requests.post('https://api.scavio.dev/api/v1/search',
headers={'x-api-key': API_KEY},
json={'platform': 'google', 'query': f'{name} {company} 2026'}, timeout=15)
results = resp.json().get('organic_results', [])
activities = []
for r in results[:5]:
activities.append({
'title': r.get('title', ''),
'snippet': r.get('snippet', ''),
'url': r.get('link', ''),
'type': classify_activity(r),
})
return activities
def classify_activity(result: dict) -> str:
title = (result.get('title', '') + result.get('snippet', '')).lower()
if any(w in title for w in ['blog', 'article', 'wrote', 'published']):
return 'content'
if any(w in title for w in ['spoke', 'conference', 'keynote', 'panel']):
return 'speaking'
if any(w in title for w in ['raised', 'funding', 'series', 'launch']):
return 'news'
return 'mention'
activities = find_prospect_activity('Jane Smith', 'Acme Corp')
for a in activities:
print(f"[{a['type']}] {a['title'][:60]}")Step 2: Search company news
Pull recent company news to find relevant events for personalization.
def get_company_news(company: str) -> list:
resp = requests.post('https://api.scavio.dev/api/v1/search',
headers={'x-api-key': API_KEY},
json={'platform': 'google', 'query': f'{company} news announcement 2026'}, timeout=15)
results = resp.json().get('organic_results', [])
return [{
'headline': r.get('title', ''),
'snippet': r.get('snippet', '')[:150],
'url': r.get('link', ''),
} for r in results[:3]]
news = get_company_news('Acme Corp')
for n in news:
print(f" {n['headline'][:60]}")Step 3: Select best personalization hook
Rank the discovered activities and news items to pick the strongest personalization angle.
def select_hook(activities: list, news: list) -> dict:
# Prioritize: content > speaking > news > mention
priority = {'content': 1, 'speaking': 2, 'news': 3, 'mention': 4}
sorted_activities = sorted(activities, key=lambda a: priority.get(a['type'], 5))
if sorted_activities:
best = sorted_activities[0]
return {
'type': best['type'],
'reference': best['title'],
'url': best['url'],
'snippet': best['snippet'][:100],
}
if news:
return {
'type': 'company_news',
'reference': news[0]['headline'],
'url': news[0]['url'],
'snippet': news[0]['snippet'][:100],
}
return {'type': 'none', 'reference': '', 'url': '', 'snippet': ''}
hook = select_hook(activities, news)
print(f"Best hook: [{hook['type']}] {hook['reference'][:60]}")Step 4: Generate personalized opener
Create an email opener that references the specific activity or news item found.
def personalized_opener(name: str, company: str, hook: dict) -> str:
first_name = name.split()[0] if name else 'there'
if hook['type'] == 'content':
return f"Hi {first_name}, I came across your piece \"{hook['reference'][:50]}\" and it resonated with something we have been working on."
if hook['type'] == 'speaking':
return f"Hi {first_name}, I noticed your talk at {hook['reference'][:40]} and wanted to connect."
if hook['type'] in ('news', 'company_news'):
return f"Hi {first_name}, saw the news about {company} - {hook['reference'][:40]}. Congrats on the milestone."
return f"Hi {first_name}, I have been following {company}'s work and wanted to reach out."
opener = personalized_opener('Jane Smith', 'Acme Corp', hook)
print(opener)Step 5: Batch enrich lead list
Process an entire lead list and output personalized openers for each.
import time
def enrich_leads(leads: list) -> list:
enriched = []
for lead in leads:
activities = find_prospect_activity(lead['name'], lead['company'])
news = get_company_news(lead['company'])
hook = select_hook(activities, news)
opener = personalized_opener(lead['name'], lead['company'], hook)
enriched.append({
**lead,
'hook_type': hook['type'],
'hook_reference': hook['reference'][:80],
'personalized_opener': opener,
})
print(f"{lead['name']}: [{hook['type']}] {hook['reference'][:40]}")
time.sleep(0.5)
return enriched
test_leads = [
{'name': 'Jane Smith', 'company': 'Acme Corp', 'email': 'jane@acme.com'},
{'name': 'John Doe', 'company': 'Beta Inc', 'email': 'john@beta.com'},
]
results = enrich_leads(test_leads)Python Example
import requests, os
H = {'x-api-key': os.environ['SCAVIO_API_KEY']}
def personalize(name, company):
data = requests.post('https://api.scavio.dev/api/v1/search', headers=H,
json={'platform': 'google', 'query': f'{name} {company} 2026'}).json()
top = data.get('organic_results', [{}])[0]
return f"Re: {top.get('title', 'your recent work')[:50]}"
print(personalize('Jane Smith', 'Acme Corp'))JavaScript Example
const H = {'x-api-key': process.env.SCAVIO_API_KEY, 'Content-Type': 'application/json'};
async function personalize(name, company) {
const r = await fetch('https://api.scavio.dev/api/v1/search', {
method: 'POST', headers: H,
body: JSON.stringify({platform: 'google', query: `${name} ${company} 2026`})
});
const top = ((await r.json()).organic_results || [{}])[0];
return `Re: ${(top.title || 'your recent work').slice(0, 50)}`;
}
personalize('Jane Smith', 'Acme Corp').then(console.log);Expected Output
An n8n-compatible outreach personalization pipeline that enriches each lead with real prospect activity and company news, generating specific personalized email openers.