A cold email enrichment pipeline takes a company name, searches for recent company news and product info via the Scavio API, extracts relevant signals, and generates a personalized opening line using an LLM.
Prerequisites
- Python 3.9+
- Scavio API key
- anthropic or openai SDK
Walkthrough
Step 1: Search for company signals
Run two searches per company: one for recent news, one for tech stack and product.
import requests
API_KEY = "your-scavio-api-key"
def search_company(company: str) -> dict:
signals = {}
# Recent news
r1 = requests.post(
"https://api.scavio.dev/api/v1/search",
json={"query": f"{company} news 2026", "num_results": 3},
headers={"x-api-key": API_KEY}, timeout=15
)
signals["news"] = r1.json().get("organic_results", [])
# Product and tech
r2 = requests.post(
"https://api.scavio.dev/api/v1/search",
json={"query": f"{company} product features stack", "num_results": 3},
headers={"x-api-key": API_KEY}, timeout=15
)
signals["product"] = r2.json().get("organic_results", [])
return signalsStep 2: Extract snippets for the prompt
Combine news and product snippets into a compact context string.
def extract_context(signals: dict, company: str) -> str:
lines = [f"Company: {company}"]
for result in signals.get("news", [])[:2]:
lines.append(f"News: {result.get('title')} - {result.get('snippet', '')[:150]}")
for result in signals.get("product", [])[:2]:
lines.append(f"Product: {result.get('snippet', '')[:150]}")
return "\n".join(lines)Step 3: Generate the personalized opener
Send the context to an LLM and ask for a one-sentence opener.
import anthropic
ANTHROPIC_KEY = "your-anthropic-key"
def generate_opener(company: str, recipient_role: str = "founder") -> str:
signals = search_company(company)
context = extract_context(signals, company)
prompt = f"""Based on this company context, write ONE personalized first sentence for a cold email to their {recipient_role}. Be specific. No fluff. Max 25 words.
{context}
First sentence:"""
client = anthropic.Anthropic(api_key=ANTHROPIC_KEY)
msg = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=100,
messages=[{"role": "user", "content": prompt}]
)
return msg.content[0].text.strip()
opener = generate_opener("Retool", recipient_role="Head of Engineering")
print(opener)Python Example
import requests
import anthropic
SCAVIO_KEY = "your-scavio-api-key"
ANTHROPIC_KEY = "your-anthropic-key"
def get_company_signals(company: str) -> str:
snippets = []
for query in [f"{company} news 2026", f"{company} product"]:
r = requests.post(
"https://api.scavio.dev/api/v1/search",
json={"query": query, "num_results": 3},
headers={"x-api-key": SCAVIO_KEY}, timeout=15
)
for res in r.json().get("organic_results", [])[:2]:
snippets.append(f"{res.get('title')}: {res.get('snippet','')[:120]}")
return "\n".join(snippets)
def generate_opener(company: str, role: str = "founder") -> dict:
context = get_company_signals(company)
prompt = f"""Company context:\n{context}\n\nWrite ONE personalized cold email opening sentence for the {role}. Specific, no fluff, max 25 words."""
client = anthropic.Anthropic(api_key=ANTHROPIC_KEY)
msg = client.messages.create(
model="claude-sonnet-4-6", max_tokens=100,
messages=[{"role": "user", "content": prompt}]
)
return {"company": company, "opener": msg.content[0].text.strip(), "context": context}
def enrich_lead_list(companies: list, role: str = "founder") -> list:
results = []
for company in companies:
try:
result = generate_opener(company, role)
print(f"{company}: {result['opener']}")
results.append(result)
except Exception as e:
print(f"Error for {company}: {e}")
return results
if __name__ == "__main__":
companies = ["Retool", "Linear", "Vercel"]
enriched = enrich_lead_list(companies, role="Head of Engineering")
for e in enriched:
print(f"\n{e['company']}:\n {e['opener']}")JavaScript Example
const SCAVIO_KEY = 'your-scavio-api-key';
const ANTHROPIC_KEY = 'your-anthropic-key';
async function getCompanySignals(company) {
const queries = [`${company} news 2026`, `${company} product`];
const snippets = [];
for (const q of queries) {
const res = await fetch('https://api.scavio.dev/api/v1/search', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'x-api-key': SCAVIO_KEY },
body: JSON.stringify({ query: q, num_results: 3 })
});
const data = await res.json();
for (const r of (data.organic_results ?? []).slice(0, 2)) {
snippets.push(`${r.title}: ${r.snippet?.slice(0, 120) ?? ''}`);
}
}
return snippets.join('\n');
}
async function generateOpener(company, role = 'founder') {
const context = await getCompanySignals(company);
const prompt = `Context:\n${context}\n\nWrite ONE personalized cold email first sentence for the ${role}. Max 25 words.`;
const res = await fetch('https://api.anthropic.com/v1/messages', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'x-api-key': ANTHROPIC_KEY, 'anthropic-version': '2023-06-01' },
body: JSON.stringify({ model: 'claude-sonnet-4-6', max_tokens: 100, messages: [{ role: 'user', content: prompt }] })
});
const msg = await res.json();
return { company, opener: msg.content[0].text.trim() };
}
console.log(await generateOpener('Retool', 'Head of Engineering'));Expected Output
Retool: Saw Retool just launched multiplayer editing for internal tools — curious how your team is handling access control at scale.
Linear: Linear's new Triage mode looks like it halves the noise in engineering standups — is that holding up in practice?
Vercel: Noticed Vercel added Fluid Compute for always-warm serverless — how much did that move the needle on cold start complaints?