This workflow corresponds to n8n.io template #9299 — we link there as the canonical source.
This workflow follows the Google Sheets → HTTP Request recipe pattern — see all workflows that pair these two integrations.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"id": "",
"meta": {
"templateCredsSetupCompleted": false
},
"name": "LinkedIn Lead Enrichment with AI Personalization for Cold Outreach",
"tags": [
{
"id": "1",
"name": "Sales",
"createdAt": "2025-01-01T00:00:00.000Z",
"updatedAt": "2025-01-01T00:00:00.000Z"
},
{
"id": "2",
"name": "Lead Generation",
"createdAt": "2025-01-01T00:00:00.000Z",
"updatedAt": "2025-01-01T00:00:00.000Z"
},
{
"id": "3",
"name": "AI",
"createdAt": "2025-01-01T00:00:00.000Z",
"updatedAt": "2025-01-01T00:00:00.000Z"
}
],
"nodes": [
{
"id": "workflow-overview-note",
"name": "\ud83d\udccb Workflow Overview & Setup Guide",
"type": "n8n-nodes-base.stickyNote",
"position": [
-420,
240
],
"parameters": {
"color": 4,
"width": 680,
"height": 620,
"content": "## \ud83c\udfaf LinkedIn Lead Enrichment with AI Personalization\n\n### **Who's it for:**\nSales teams, growth marketers, and business development professionals who want to automate lead research and create personalized outreach at scale.\n\n### **What it does:**\n- Scrapes LinkedIn profiles to extract professional information\n- Enriches leads with email addresses using hunter.io\n- Generates personalized outreach messages using AI\n- Stores enriched data in Google Sheets for easy management\n\n### **How to set up:**\n1. **Configure Google Sheets:** Create a sheet with columns: Name, LinkedIn URL, Email, Company, Title, Personalized Message\n2. **Set up credentials:** Add your OpenAI API key and Hunter.io API key in n8n credentials\n3. **Customize the AI prompt:** Edit the OpenAI node to match your outreach style and value proposition\n4. **Test with sample data:** Start with 2-3 LinkedIn URLs before scaling\n\n### **Requirements:**\n- OpenAI API key (GPT-4 recommended for best personalization)\n- Hunter.io API key for email finding\n- Google Sheets access\n- LinkedIn profile URLs of your target leads\n\n### **How to customize:**\n- **Change AI tone:** Modify the system prompt in the OpenAI node\n- **Add more data sources:** Integrate company website scraping\n- **Filter leads:** Add conditional logic to qualify leads before enrichment\n- **Connect to CRM:** Replace Google Sheets with Salesforce, HubSpot, or Pipedrive\n\n\ud83d\udca1 **Pro Tip:** Use the \"Set Fields\" node to easily update your target industry and value proposition without editing multiple nodes.\n\n\ud83d\udcfa **Watch Setup Guide:** [Add your Loom video link here]\n\n\u26a0\ufe0f **Important:** This workflow uses community nodes for LinkedIn scraping. Self-hosted n8n instance required."
},
"typeVersion": 1
},
{
"id": "step-1-note",
"name": "Step 1 Context",
"type": "n8n-nodes-base.stickyNote",
"position": [
140,
240
],
"parameters": {
"color": 7,
"width": 380,
"height": 180,
"content": "## Step 1: Configure Your Campaign\n\nSet your target criteria and personalization variables here.\n\nThis makes it easy to update without touching other nodes."
},
"typeVersion": 1
},
{
"id": "step-2-note",
"name": "Step 2 Context",
"type": "n8n-nodes-base.stickyNote",
"position": [
660,
240
],
"parameters": {
"color": 7,
"width": 380,
"height": 180,
"content": "## Step 2: Extract LinkedIn Data\n\nScrape professional information from LinkedIn profiles.\n\n\u26a0\ufe0f Rate limit: Max 20 profiles per minute"
},
"typeVersion": 1
},
{
"id": "step-3-note",
"name": "Step 3 Context",
"type": "n8n-nodes-base.stickyNote",
"position": [
1180,
240
],
"parameters": {
"color": 7,
"width": 380,
"height": 180,
"content": "## Step 3: Find Email Addresses\n\nUse Hunter.io to find verified work emails.\n\nFallback to company domain + name patterns if not found."
},
"typeVersion": 1
},
{
"id": "step-4-note",
"name": "Step 4 Context",
"type": "n8n-nodes-base.stickyNote",
"position": [
1700,
240
],
"parameters": {
"color": 7,
"width": 380,
"height": 180,
"content": "## Step 4: AI Personalization\n\nGenerate custom outreach messages based on:\n- Job title\n- Company info\n- Recent activity\n- Your value proposition"
},
"typeVersion": 1
},
{
"id": "step-5-note",
"name": "Step 5 Context",
"type": "n8n-nodes-base.stickyNote",
"position": [
2220,
240
],
"parameters": {
"color": 7,
"width": 380,
"height": 180,
"content": "## Step 5: Store Enriched Leads\n\nSave all enriched data to Google Sheets for review and outreach."
},
"typeVersion": 1
},
{
"id": "campaign-settings-node",
"name": "Set Campaign Variables",
"type": "n8n-nodes-base.set",
"position": [
180,
480
],
"parameters": {
"mode": "raw",
"options": {},
"jsonOutput": "{\n \"target_industry\": \"SaaS\",\n \"target_role\": \"Head of Sales\",\n \"company_name\": \"YourCompany\",\n \"value_proposition\": \"We help sales teams book 30% more meetings using AI-powered personalization\",\n \"linkedin_urls\": [\n \"https://www.linkedin.com/in/sample-profile-1\",\n \"https://www.linkedin.com/in/sample-profile-2\"\n ]\n}"
},
"typeVersion": 3.4
},
{
"id": "split-urls-node",
"name": "Split LinkedIn URLs",
"type": "n8n-nodes-base.splitOut",
"position": [
380,
480
],
"parameters": {
"options": {},
"fieldToSplitOut": "linkedin_urls"
},
"typeVersion": 1
},
{
"id": "scrape-linkedin-node",
"name": "Scrape LinkedIn Profile",
"type": "n8n-nodes-base.httpRequest",
"position": [
700,
480
],
"parameters": {
"url": "={{ $json.linkedin_urls }}",
"options": {
"timeout": 10000
},
"sendHeaders": true,
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"headerParameters": {
"parameters": [
{
"name": "User-Agent",
"value": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "parse-linkedin-data-node",
"name": "Parse LinkedIn Data",
"type": "n8n-nodes-base.code",
"position": [
900,
480
],
"parameters": {
"jsCode": "// Extract key information from LinkedIn HTML\nconst html = $input.item.json.data || '';\n\n// Helper function to extract text between patterns\nfunction extractText(pattern, text) {\n const match = text.match(pattern);\n return match ? match[1].trim() : null;\n}\n\n// Extract name\nconst name = extractText(/<title>([^|]+)/, html) || 'Unknown';\n\n// Extract current position\nconst title = extractText(/aria-label=\"Current position[^>]*>([^<]+)/, html) || 'Not specified';\n\n// Extract company\nconst company = extractText(/aria-label=\"Current company[^>]*>([^<]+)/, html) || 'Unknown';\n\n// Extract location\nconst location = extractText(/aria-label=\"Location[^>]*>([^<]+)/, html) || 'Unknown';\n\n// Clean up name\nconst cleanName = name.replace(/[\\|\\-].*$/, '').trim();\nconst nameParts = cleanName.split(' ');\nconst firstName = nameParts[0] || '';\nconst lastName = nameParts.slice(1).join(' ') || '';\n\nreturn {\n full_name: cleanName,\n first_name: firstName,\n last_name: lastName,\n title: title,\n company: company,\n location: location,\n linkedin_url: $json.linkedin_urls,\n raw_html: html.substring(0, 1000) // Keep snippet for debugging\n};"
},
"typeVersion": 2
},
{
"id": "find-email-node",
"name": "Find Email with Hunter.io",
"type": "n8n-nodes-base.httpRequest",
"position": [
1220,
480
],
"parameters": {
"url": "=https://api.hunter.io/v2/email-finder",
"method": "GET",
"options": {
"response": {
"response": {
"neverError": true
}
}
},
"sendQuery": true,
"authentication": "genericCredentialType",
"genericAuthType": "queryAuth",
"queryParameters": {
"parameters": [
{
"name": "domain",
"value": "={{ $json.company.toLowerCase().replace(/[^a-z0-9]/g, '') }}.com"
},
{
"name": "first_name",
"value": "={{ $json.first_name }}"
},
{
"name": "last_name",
"value": "={{ $json.last_name }}"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "process-email-node",
"name": "Process Email Result",
"type": "n8n-nodes-base.code",
"position": [
1420,
480
],
"parameters": {
"jsCode": "// Extract email from Hunter.io response\nconst hunterData = $input.item.json.data || {};\nconst emailData = hunterData.email || null;\n\n// Get previous node data\nconst leadData = $('Parse LinkedIn Data').item.json;\n\nreturn {\n ...leadData,\n email: emailData,\n email_confidence: hunterData.score || 0,\n email_verified: emailData ? true : false\n};"
},
"typeVersion": 2
},
{
"id": "check-email-exists-node",
"name": "Email Found?",
"type": "n8n-nodes-base.if",
"position": [
1620,
480
],
"parameters": {
"options": {},
"conditions": {
"options": {
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "email-exists-condition",
"operator": {
"type": "string",
"operation": "isNotEmpty"
},
"leftValue": "={{ $json.email }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.2
},
{
"id": "generate-personalization-node",
"name": "Generate AI Personalization",
"type": "@n8n/n8n-nodes-langchain.openAi",
"position": [
1900,
400
],
"parameters": {
"modelId": {
"__rl": true,
"mode": "list",
"value": "gpt-4-turbo-preview",
"cachedResultName": "GPT-4 Turbo"
},
"options": {},
"messages": {
"values": [
{
"content": "=You are an expert sales copywriter. Generate a highly personalized cold outreach message based on the prospect's information.\n\n**Prospect Details:**\n- Name: {{ $json.first_name }} {{ $json.last_name }}\n- Title: {{ $json.title }}\n- Company: {{ $json.company }}\n- Location: {{ $json.location }}\n\n**Our Company:** {{ $('Set Campaign Variables').item.json.company_name }}\n**Value Proposition:** {{ $('Set Campaign Variables').item.json.value_proposition }}\n\n**Instructions:**\n1. Start with a personalized opener that references their role or company\n2. Clearly state the value proposition in one sentence\n3. Include a soft call-to-action\n4. Keep it under 100 words\n5. Professional but conversational tone\n6. No hype or pushy language\n\n**Output Format:**\nProvide ONLY the message body, no subject line or greetings. Return as JSON:\n{\n \"message\": \"your personalized message here\"\n}"
}
]
},
"jsonOutput": true
},
"typeVersion": 1.8
},
{
"id": "save-to-sheets-node",
"name": "Save to Google Sheets",
"type": "n8n-nodes-base.googleSheets",
"position": [
2260,
480
],
"parameters": {
"columns": {
"value": {
"Email": "={{ $json.email }}",
"Title": "={{ $json.title }}",
"Company": "={{ $json.company }}",
"Location": "={{ $json.location }}",
"Full Name": "={{ $json.full_name }}",
"Last Name": "={{ $json.last_name }}",
"First Name": "={{ $json.first_name }}",
"LinkedIn URL": "={{ $json.linkedin_url }}",
"Enriched Date": "={{ $now.toFormat('yyyy-MM-dd HH:mm:ss') }}",
"Email Confidence": "={{ $json.email_confidence }}",
"Personalized Message": "={{ $json.ai_message }}"
},
"schema": [],
"mappingMode": "defineBelow",
"matchingColumns": []
},
"options": {},
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultName": "Sheet1"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "YOUR_GOOGLE_SHEET_ID",
"cachedResultName": "Lead Enrichment Sheet"
}
},
"typeVersion": 4.5
},
{
"id": "merge-ai-data-node",
"name": "Merge AI Message",
"type": "n8n-nodes-base.code",
"position": [
2100,
400
],
"parameters": {
"jsCode": "// Merge AI-generated message with lead data\nconst leadData = $('Process Email Result').item.json;\nconst aiResponse = $input.item.json.message?.content || {};\nconst aiMessage = aiResponse.message || 'Personalization failed - manual review needed';\n\nreturn {\n ...leadData,\n ai_message: aiMessage,\n processing_timestamp: new Date().toISOString()\n};"
},
"typeVersion": 2
},
{
"id": "no-email-found-node",
"name": "No Email - Skip",
"type": "n8n-nodes-base.noOp",
"position": [
1900,
560
],
"parameters": {},
"typeVersion": 1
},
{
"id": "rate-limit-wait-node",
"name": "Rate Limit Delay",
"type": "n8n-nodes-base.wait",
"position": [
560,
480
],
"parameters": {
"unit": "seconds",
"amount": 2
},
"typeVersion": 1.1
}
],
"active": false,
"settings": {
"callerPolicy": "workflowsFromSameOwner",
"errorWorkflow": "",
"executionOrder": "v1",
"saveManualExecutions": true
},
"versionId": "",
"connections": {
"Email Found?": {
"main": [
[
{
"node": "Generate AI Personalization",
"type": "main",
"index": 0
}
],
[
{
"node": "No Email - Skip",
"type": "main",
"index": 0
}
]
]
},
"Merge AI Message": {
"main": [
[
{
"node": "Save to Google Sheets",
"type": "main",
"index": 0
}
]
]
},
"Rate Limit Delay": {
"main": [
[
{
"node": "Scrape LinkedIn Profile",
"type": "main",
"index": 0
}
]
]
},
"Parse LinkedIn Data": {
"main": [
[
{
"node": "Find Email with Hunter.io",
"type": "main",
"index": 0
}
]
]
},
"Split LinkedIn URLs": {
"main": [
[
{
"node": "Rate Limit Delay",
"type": "main",
"index": 0
}
]
]
},
"Process Email Result": {
"main": [
[
{
"node": "Email Found?",
"type": "main",
"index": 0
}
]
]
},
"Set Campaign Variables": {
"main": [
[
{
"node": "Split LinkedIn URLs",
"type": "main",
"index": 0
}
]
]
},
"Scrape LinkedIn Profile": {
"main": [
[
{
"node": "Parse LinkedIn Data",
"type": "main",
"index": 0
}
]
]
},
"Find Email with Hunter.io": {
"main": [
[
{
"node": "Process Email Result",
"type": "main",
"index": 0
}
]
]
},
"Generate AI Personalization": {
"main": [
[
{
"node": "Merge AI Message",
"type": "main",
"index": 0
}
]
]
}
}
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Supercharge your sales and marketing efforts with this powerful automation that transforms a list of LinkedIn profiles into a fully enriched, personalized outreach campaign. This workflow is designed for sales teams, growth marketers, and business development professionals…
Source: https://n8n.io/workflows/9299/ — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
Ask questions like “How much did I spend on food last month?” and get instant answers from your financial data — directly in Telegram.
AI Institutional Stock Valuation Engine with Risk Scoring & Scenario Targets
Overview This is a production-grade, fully automated stock analysis system built entirely in n8n. It combines institutional-level financial analysis, dual AI model consensus, and a self-improving back
The Problem That it Solves
This intelligent email automation workflow helps you maximize engagement through domain-based outreach. It utilizes AI-powered personalization and strategic follow-ups to increase response rates. The