Research companies and find decision-makers across LinkedIn, Google, and news sources. Builds enriched prospect dossiers with outreach hooks, prospect scores, and exports to Google Sheets.
Given a company name, a list of target accounts, or ICP criteria, research and enrich prospects with company intelligence, decision-maker profiles, recent news, and personalized outreach hooks.
await requestApproval({
reason: "Need Sheets access to export enriched prospect list",
type: "service_auth",
payload: { provider: "google", service: "drive" }
})
// Option 1: User provides company names directly
var companies = ["Acme Corp", "Widget Inc", "StartupXYZ"]
// Option 2: Read from Google Sheet
var sheetData = await google.sheets.getValues({
spreadsheetId: userSheetId,
range: "Sheet1!A2:A" // Company name column
})
var companies = sheetData.map(row => row[0]).filter(c => c && c.trim())
console.log("Target companies: " + companies.length)
For each company, search Google for basic information:
var companyIntel = []
var page = await browser.newtab("https://www.google.com")
for (var i = 0; i < companies.length; i++) {
var company = companies[i]
await page.goto({ url: "https://www.google.com/search?q=" + encodeURIComponent(company + " company") })
await page.wait({ waitTime: 3 + Math.floor(Math.random() * 3) })
var snap = await page.snapshot()
// Extract from Knowledge Panel and search results:
var intel = {
name: company,
industry: "", // From Knowledge Panel or meta descriptions
hq: "", // Headquarters location
employeeCount: "", // Approximate headcount
founded: "", // Year founded
description: "", // What they do (one line)
website: "", // Company website URL
fundingStage: "", // If startup: Seed, Series A/B/C etc.
recentNews: [] // Will be filled in Step 3
}
// Parse snapshot for company data
// Look for: Knowledge Panel, Wikipedia excerpt, Crunchbase, LinkedIn company page
companyIntel.push(intel)
console.log("Researched " + (i + 1) + "/" + companies.length + ": " + company)
}
for (var i = 0; i < companyIntel.length; i++) {
var intel = companyIntel[i]
// Search for company on LinkedIn
await page.goto({
url: "https://www.linkedin.com/search/results/companies/?keywords=" + encodeURIComponent(intel.name)
})
await page.wait({ waitTime: 5 + Math.floor(Math.random() * 3) })
var snap = await page.snapshot()
// Click on first matching company result
// Navigate to /about/ page for detailed info
// Extract:
// - Company size (exact range like "51-200 employees")
// - Industry category
// - Specialties
// - Headquarters
// - Founded year
// - Company type (Public, Privately Held, etc.)
// - Associated LinkedIn URL (for future use)
await page.wait({ waitTime: 8 + Math.floor(Math.random() * 5) })
}
for (var i = 0; i < companyIntel.length; i++) {
var intel = companyIntel[i]
// Search Google News (past 30 days)
await page.goto({
url: "https://www.google.com/search?q=" + encodeURIComponent(intel.name) + "&tbm=nws&tbs=qdr:m"
})
await page.wait({ waitTime: 3 + Math.floor(Math.random() * 2) })
var snap = await page.snapshot()
// Extract top 3 news headlines:
// - Fundraising announcements
// - Product launches
// - Executive hires/departures
// - Partnerships
// - Awards/recognition
// These become personalization hooks for outreach
intel.recentNews = [
// { headline: "Acme raises $30M Series B", date: "Apr 8, 2026", source: "TechCrunch" }
]
}
var prospects = []
var targetRoles = ["VP Marketing", "Head of Growth", "CMO"] // User-configurable
for (var i = 0; i < companyIntel.length; i++) {
var intel = companyIntel[i]
for (var role of targetRoles) {
var searchQuery = role + " " + intel.name
await page.goto({
url: "https://www.linkedin.com/search/results/people/?keywords=" + encodeURIComponent(searchQuery) + "&origin=GLOBAL_SEARCH_HEADER"
})
await page.wait({ waitTime: 5 + Math.floor(Math.random() * 3) })
var snap = await page.snapshot()
// Parse search results (10 per page, only use first page)
// For each result extract:
// - Full name
// - Headline (title + company)
// - Location
// - Profile URL
// - Connection degree (1st, 2nd, 3rd)
// - Number of mutual connections
// Pick the best match (title contains target role + company matches)
break // One search per company is enough for safety
}
// Rate limiting between companies
await page.wait({ waitTime: 5 + Math.floor(Math.random() * 5) })
}
For the best-matched prospect at each company, visit their full profile:
for (var i = 0; i < prospects.length; i++) {
var prospect = prospects[i]
await page.goto({ url: prospect.profileUrl })
await page.wait({ waitTime: 8 + Math.floor(Math.random() * 7) }) // 8-15 second delay
var snap = await page.snapshot()
// Extract enriched data:
prospect.enriched = {
fullTitle: "", // Current title at current company
tenure: "", // How long in current role
previousCompanies: [], // Last 2-3 positions
education: "", // Degree + school
skills: [], // Top endorsed skills
about: "", // Summary/About section (first 200 chars)
recentPosts: [], // Last 3 posts (topic + date)
isActive: false, // Posted in last 30 days?
mutualConnections: [], // Names of shared connections (if visible)
}
console.log("Enriched " + (i + 1) + "/" + prospects.length + ": " + prospect.name)
}
// For prospects where you need to know if they're active posters:
await page.goto({ url: prospect.profileUrl + "/recent-activity/all/" })
await page.wait({ waitTime: 5 })
var activitySnap = await page.snapshot()
// Check: When was their last post? What topics?
// If posted in last 30 days: isActive = true
// If last 7 days: great candidate for warm-up engagement before outreach
Score each prospect (0-100) based on fit:
for (var prospect of prospects) {
var score = 0
var breakdown = []
// Role match (0-30)
if (exactTitleMatch(prospect.enriched.fullTitle, targetRoles)) {
score += 30; breakdown.push("Role match: +30")
} else if (partialTitleMatch(prospect.enriched.fullTitle, targetRoles)) {
score += 15; breakdown.push("Partial role match: +15")
}
// LinkedIn activity (0-20)
if (prospect.enriched.isActive) {
score += 20; breakdown.push("Active on LinkedIn: +20")
}
// Connection degree (0-25)
if (prospect.connectionDegree === "1st") {
score += 25; breakdown.push("1st degree connection: +25")
} else if (prospect.connectionDegree === "2nd") {
score += 15; breakdown.push("2nd degree connection: +15")
} else {
score += 5; breakdown.push("3rd degree connection: +5")
}
// Company signals (0-10)
if (prospect.companyIntel.recentNews.length > 0) {
score += 10; breakdown.push("Recent company news (outreach hook): +10")
}
// Tenure stability (0-10)
if (tenureMonths(prospect.enriched.tenure) >= 12) {
score += 10; breakdown.push("Stable tenure >1yr (decision power): +10")
}
// Mutual connections (0-5)
if (prospect.enriched.mutualConnections.length > 0) {
score += 5; breakdown.push("Has mutual connections: +5")
}
prospect.score = score
prospect.scoreBreakdown = breakdown
}
// Sort by score descending
prospects.sort((a, b) => b.score - a.score)
For each prospect, generate 2-3 personalized outreach hooks:
for (var prospect of prospects) {
prospect.outreachHooks = []
// Hook 1: News-based (if available)
if (prospect.companyIntel.recentNews.length > 0) {
var news = prospect.companyIntel.recentNews[0]
prospect.outreachHooks.push({
type: "news",
hook: "Congrats on [" + news.headline + "]. [Relevant observation about what this means for their role]."
})
}
// Hook 2: Post-based (if they're active)
if (prospect.enriched.recentPosts.length > 0) {
var post = prospect.enriched.recentPosts[0]
prospect.outreachHooks.push({
type: "post",
hook: "Your recent post about [" + post.topic + "] resonated - especially [specific point]. [Bridge to your value prop]."
})
}
// Hook 3: Mutual connection
if (prospect.enriched.mutualConnections.length > 0) {
prospect.outreachHooks.push({
type: "mutual",
hook: "We're both connected to [" + prospect.enriched.mutualConnections[0] + "]. [Reason for reaching out]."
})
}
// Hook 4: Company growth signal
if (prospect.companyIntel.employeeCount && parseInt(prospect.companyIntel.employeeCount) > 50) {
prospect.outreachHooks.push({
type: "growth",
hook: "I noticed [Company] is growing the [department] team. [Observation about scaling challenges at this stage]."
})
}
}
## Prospect Dossiers (5 companies researched)
### 1. Jane Doe - VP Marketing @ Acme Corp (Score: 85/100)
| Field | Details |
|-------|---------|
| Title | VP Marketing (2 yrs 3 mo) |
| Company | Acme Corp - SaaS, 250 employees, Series B, SF |
| LinkedIn | linkedin.com/in/janedoe |
| Connection | 2nd degree (3 mutual) |
| Activity | Active - posted 3 days ago about "content ops at scale" |
| Previous | Dir. Marketing @ PrevCo, Marketing Manager @ OtherCo |
**Company News:** "Acme raises $30M Series B" (TechCrunch, Apr 8)
**Best Outreach Hook:** "Congrats on the Series B! Scaling content ops is always
the fun challenge at that stage. Your recent post about it really resonated..."
**Score Breakdown:** Role +30, Active +20, 2nd degree +15, News hook +10, Stable tenure +10
var sheet = await google.sheets.createSpreadsheet({
title: "Enriched Prospects - " + new Date().toISOString().slice(0, 10),
sheets: ["Prospects", "Company Intel"]
})
// Prospects sheet
var prospectHeaders = [[
"Score", "Name", "Title", "Company", "LinkedIn URL",
"Connection Degree", "Mutual Connections", "Active on LinkedIn",
"Recent Post Topic", "Tenure", "Best Outreach Hook", "Notes"
]]
var prospectRows = prospects.map(p => [
p.score,
p.name,
p.enriched.fullTitle,
p.companyIntel.name,
p.profileUrl,
p.connectionDegree,
p.enriched.mutualConnections.join(", "),
p.enriched.isActive ? "Yes" : "No",
p.enriched.recentPosts.length > 0 ? p.enriched.recentPosts[0].topic : "",
p.enriched.tenure,
p.outreachHooks.length > 0 ? p.outreachHooks[0].hook : "",
""
])
await google.sheets.updateValues({
spreadsheetId: sheet.id,
range: "'Prospects'!A1",
values: prospectHeaders.concat(prospectRows)
})
// Company Intel sheet
var companyHeaders = [[
"Company", "Industry", "Size", "HQ", "Founded",
"Funding Stage", "Website", "Recent News", "LinkedIn URL"
]]
var companyRows = companyIntel.map(c => [
c.name, c.industry, c.employeeCount, c.hq, c.founded,
c.fundingStage, c.website,
c.recentNews.map(n => n.headline).join("; "),
c.linkedinUrl || ""
])
await google.sheets.updateValues({
spreadsheetId: sheet.id,
range: "'Company Intel'!A1",
values: companyHeaders.concat(companyRows)
})
console.log("Exported: https://docs.google.com/spreadsheets/d/" + sheet.id)