This workflow corresponds to n8n.io template #13854 — 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 →
{
"meta": {
"templateCredsSetupCompleted": false
},
"nodes": [
{
"id": "a1000000-0000-0000-0000-000000000001",
"name": "Start Enrichment",
"type": "n8n-nodes-base.manualTrigger",
"position": [
-2000,
400
],
"parameters": {},
"typeVersion": 1
},
{
"id": "a1000000-0000-0000-0000-000000000002",
"name": "Configure Workflow",
"type": "n8n-nodes-base.set",
"position": [
-1750,
400
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "cfg-001",
"name": "inputDocumentId",
"type": "string",
"value": "="
},
{
"id": "cfg-002",
"name": "inputSheetName",
"type": "string",
"value": "="
},
{
"id": "cfg-003",
"name": "outputDocumentId",
"type": "string",
"value": "="
},
{
"id": "cfg-004",
"name": "outputSheetName",
"type": "string",
"value": "="
}
]
}
},
"typeVersion": 3.4
},
{
"id": "a1000000-0000-0000-0000-000000000003",
"name": "Read Contacts from Sheet",
"type": "n8n-nodes-base.googleSheets",
"position": [
-1500,
400
],
"parameters": {
"options": {},
"operation": "readAllRows",
"sheetName": {
"__rl": true,
"mode": "id",
"value": "={{ $('Configure Workflow').item.json.inputSheetName }}"
},
"documentId": {
"__rl": true,
"mode": "id",
"value": "={{ $('Configure Workflow').item.json.inputDocumentId }}"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.6
},
{
"id": "a1000000-0000-0000-0000-000000000004",
"name": "Format Contacts for Email Finder",
"type": "n8n-nodes-base.code",
"position": [
-1250,
400
],
"parameters": {
"jsCode": "// Build the contacts array required by ScraperCity Email Finder\nconst contacts = items.map(item => ({\n first_name: (item.json.first_name || '').trim(),\n last_name: (item.json.last_name || '').trim(),\n domain: (item.json.domain || '').trim().replace(/^https?:\\/\\//, '').replace(/^www\\./, '').split('/')[0]\n})).filter(c => c.first_name && c.last_name && c.domain);\n\nreturn [{ json: { contacts } }];"
},
"typeVersion": 2
},
{
"id": "a1000000-0000-0000-0000-000000000005",
"name": "Start Email Finder Job",
"type": "n8n-nodes-base.httpRequest",
"position": [
-1000,
400
],
"parameters": {
"url": "https://scrapercity.com/api/v1/scrape/email-finder",
"method": "POST",
"options": {},
"jsonBody": "={{ JSON.stringify({ contacts: $json.contacts }) }}",
"sendBody": true,
"specifyBody": "json",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth"
},
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"typeVersion": 4.2
},
{
"id": "a1000000-0000-0000-0000-000000000006",
"name": "Store Email Finder Run ID",
"type": "n8n-nodes-base.set",
"position": [
-750,
400
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "ef-run-001",
"name": "emailFinderRunId",
"type": "string",
"value": "={{ $json.runId }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "a1000000-0000-0000-0000-000000000007",
"name": "Wait Before Checking Email Finder",
"type": "n8n-nodes-base.wait",
"position": [
-500,
400
],
"parameters": {
"amount": 30
},
"typeVersion": 1.1
},
{
"id": "a1000000-0000-0000-0000-000000000008",
"name": "Poll Email Finder Loop",
"type": "n8n-nodes-base.splitInBatches",
"position": [
-250,
400
],
"parameters": {
"options": {
"maxIterations": 40
}
},
"typeVersion": 3,
"alwaysOutputData": false
},
{
"id": "a1000000-0000-0000-0000-000000000009",
"name": "Check Email Finder Status",
"type": "n8n-nodes-base.httpRequest",
"position": [
0,
400
],
"parameters": {
"url": "=https://scrapercity.com/api/v1/scrape/status/{{ $('Store Email Finder Run ID').item.json.emailFinderRunId }}",
"method": "GET",
"options": {},
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth"
},
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"typeVersion": 4.2
},
{
"id": "a1000000-0000-0000-0000-000000000010",
"name": "Is Email Finder Complete?",
"type": "n8n-nodes-base.if",
"position": [
250,
400
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "ef-cond-001",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.status }}",
"rightValue": "SUCCEEDED"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "a1000000-0000-0000-0000-000000000011",
"name": "Wait Before Next Email Finder Poll",
"type": "n8n-nodes-base.wait",
"position": [
250,
592
],
"parameters": {
"amount": 60
},
"typeVersion": 1.1
},
{
"id": "a1000000-0000-0000-0000-000000000012",
"name": "Download Email Finder Results",
"type": "n8n-nodes-base.httpRequest",
"position": [
500,
400
],
"parameters": {
"url": "=https://scrapercity.com/api/downloads/{{ $('Store Email Finder Run ID').item.json.emailFinderRunId }}",
"method": "GET",
"options": {},
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth"
},
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"typeVersion": 4.2
},
{
"id": "a1000000-0000-0000-0000-000000000013",
"name": "Parse Email Finder CSV",
"type": "n8n-nodes-base.code",
"position": [
750,
400
],
"parameters": {
"jsCode": "// Parse CSV from email-finder download\n// Expected columns: first_name, last_name, domain, email, confidence\nconst raw = items[0].json.data || items[0].json.body || '';\nif (!raw) return [];\n\nconst lines = raw.trim().split('\\n');\nif (lines.length < 2) return [];\n\nconst headers = lines[0].split(',').map(h => h.trim().replace(/^\"|\"$/g, ''));\nconst results = [];\n\nfor (let i = 1; i < lines.length; i++) {\n const cols = lines[i].split(',').map(c => c.trim().replace(/^\"|\"$/g, ''));\n if (cols.length < headers.length) continue;\n const row = {};\n headers.forEach((h, idx) => { row[h] = cols[idx] || ''; });\n if (row.email) results.push({ json: row });\n}\n\nreturn results;"
},
"typeVersion": 2
},
{
"id": "a1000000-0000-0000-0000-000000000014",
"name": "Remove Duplicate Emails",
"type": "n8n-nodes-base.removeDuplicates",
"position": [
1000,
400
],
"parameters": {
"options": {},
"fieldToCompare": "email"
},
"typeVersion": 2
},
{
"id": "a1000000-0000-0000-0000-000000000015",
"name": "Filter Valid Emails Only",
"type": "n8n-nodes-base.filter",
"position": [
1250,
400
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"caseSensitive": false,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"id": "fv-cond-001",
"operator": {
"type": "string",
"operation": "exists",
"singleValue": true
},
"leftValue": "={{ $json.email }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.2,
"alwaysOutputData": true
},
{
"id": "a1000000-0000-0000-0000-000000000016",
"name": "Format Emails for Mobile Finder",
"type": "n8n-nodes-base.code",
"position": [
1500,
400
],
"parameters": {
"jsCode": "// Mobile Finder accepts an array of email strings\nconst inputs = items\n .map(item => (item.json.email || '').trim().toLowerCase())\n .filter(e => e.includes('@'));\n\nreturn [{ json: { inputs } }];"
},
"typeVersion": 2
},
{
"id": "a1000000-0000-0000-0000-000000000017",
"name": "Start Mobile Finder Job",
"type": "n8n-nodes-base.httpRequest",
"position": [
1750,
400
],
"parameters": {
"url": "https://scrapercity.com/api/v1/scrape/mobile-finder",
"method": "POST",
"options": {},
"jsonBody": "={{ JSON.stringify({ inputs: $json.inputs }) }}",
"sendBody": true,
"specifyBody": "json",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth"
},
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"typeVersion": 4.2
},
{
"id": "a1000000-0000-0000-0000-000000000018",
"name": "Store Mobile Finder Run ID",
"type": "n8n-nodes-base.set",
"position": [
2000,
400
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "mf-run-001",
"name": "mobileFinderRunId",
"type": "string",
"value": "={{ $json.runId }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "a1000000-0000-0000-0000-000000000019",
"name": "Wait Before Checking Mobile Finder",
"type": "n8n-nodes-base.wait",
"position": [
2250,
400
],
"parameters": {
"amount": 30
},
"typeVersion": 1.1
},
{
"id": "a1000000-0000-0000-0000-000000000020",
"name": "Poll Mobile Finder Loop",
"type": "n8n-nodes-base.splitInBatches",
"position": [
2500,
400
],
"parameters": {
"options": {
"maxIterations": 40
}
},
"typeVersion": 3,
"alwaysOutputData": false
},
{
"id": "a1000000-0000-0000-0000-000000000021",
"name": "Check Mobile Finder Status",
"type": "n8n-nodes-base.httpRequest",
"position": [
2750,
400
],
"parameters": {
"url": "=https://scrapercity.com/api/v1/scrape/status/{{ $('Store Mobile Finder Run ID').item.json.mobileFinderRunId }}",
"method": "GET",
"options": {},
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth"
},
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"typeVersion": 4.2
},
{
"id": "a1000000-0000-0000-0000-000000000022",
"name": "Is Mobile Finder Complete?",
"type": "n8n-nodes-base.if",
"position": [
3000,
400
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "mf-cond-001",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.status }}",
"rightValue": "SUCCEEDED"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "a1000000-0000-0000-0000-000000000023",
"name": "Wait Before Next Mobile Finder Poll",
"type": "n8n-nodes-base.wait",
"position": [
500,
592
],
"parameters": {
"amount": 60
},
"typeVersion": 1.1
},
{
"id": "a1000000-0000-0000-0000-000000000024",
"name": "Download Mobile Finder Results",
"type": "n8n-nodes-base.httpRequest",
"position": [
3250,
400
],
"parameters": {
"url": "=https://scrapercity.com/api/downloads/{{ $('Store Mobile Finder Run ID').item.json.mobileFinderRunId }}",
"method": "GET",
"options": {},
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth"
},
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"typeVersion": 4.2
},
{
"id": "a1000000-0000-0000-0000-000000000025",
"name": "Parse Mobile Finder CSV",
"type": "n8n-nodes-base.code",
"position": [
3500,
400
],
"parameters": {
"jsCode": "// Parse CSV from mobile-finder download\n// Expected columns: input, phone, phone_type, country\nconst raw = items[0].json.data || items[0].json.body || '';\nif (!raw) return [];\n\nconst lines = raw.trim().split('\\n');\nif (lines.length < 2) return [];\n\nconst headers = lines[0].split(',').map(h => h.trim().replace(/^\"|\"$/g, ''));\nconst results = [];\n\nfor (let i = 1; i < lines.length; i++) {\n const cols = lines[i].split(',').map(c => c.trim().replace(/^\"|\"$/g, ''));\n if (cols.length < headers.length) continue;\n const row = {};\n headers.forEach((h, idx) => { row[h] = cols[idx] || ''; });\n // Normalise: map 'input' -> 'email' so we can merge later\n row.email = row.input || row.email || '';\n results.push({ json: row });\n}\n\nreturn results;"
},
"typeVersion": 2
},
{
"id": "a1000000-0000-0000-0000-000000000026",
"name": "Format Emails for Validator",
"type": "n8n-nodes-base.code",
"position": [
3750,
400
],
"parameters": {
"jsCode": "// Email Validator accepts {\"emails\": [\"user@example.com\", ...]}\n// We pull emails from the parsed email-finder results stored earlier\nconst emails = items\n .map(item => (item.json.email || '').trim().toLowerCase())\n .filter(e => e.includes('@'));\n\nreturn [{ json: { emails } }];"
},
"typeVersion": 2
},
{
"id": "a1000000-0000-0000-0000-000000000027",
"name": "Start Email Validator Job",
"type": "n8n-nodes-base.httpRequest",
"position": [
4000,
400
],
"parameters": {
"url": "https://scrapercity.com/api/v1/scrape/email-validator",
"method": "POST",
"options": {},
"jsonBody": "={{ JSON.stringify({ emails: $json.emails }) }}",
"sendBody": true,
"specifyBody": "json",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth"
},
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"typeVersion": 4.2
},
{
"id": "a1000000-0000-0000-0000-000000000028",
"name": "Store Validator Run ID",
"type": "n8n-nodes-base.set",
"position": [
4250,
400
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "ev-run-001",
"name": "validatorRunId",
"type": "string",
"value": "={{ $json.runId }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "a1000000-0000-0000-0000-000000000029",
"name": "Wait Before Checking Validator",
"type": "n8n-nodes-base.wait",
"position": [
4500,
400
],
"parameters": {
"amount": 30
},
"typeVersion": 1.1
},
{
"id": "a1000000-0000-0000-0000-000000000030",
"name": "Poll Validator Loop",
"type": "n8n-nodes-base.splitInBatches",
"position": [
4750,
400
],
"parameters": {
"options": {
"maxIterations": 40
}
},
"typeVersion": 3,
"alwaysOutputData": false
},
{
"id": "a1000000-0000-0000-0000-000000000031",
"name": "Check Validator Status",
"type": "n8n-nodes-base.httpRequest",
"position": [
5000,
400
],
"parameters": {
"url": "=https://scrapercity.com/api/v1/scrape/status/{{ $('Store Validator Run ID').item.json.validatorRunId }}",
"method": "GET",
"options": {},
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth"
},
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"typeVersion": 4.2
},
{
"id": "a1000000-0000-0000-0000-000000000032",
"name": "Is Validator Complete?",
"type": "n8n-nodes-base.if",
"position": [
5250,
400
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "ev-cond-001",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.status }}",
"rightValue": "SUCCEEDED"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "a1000000-0000-0000-0000-000000000033",
"name": "Wait Before Next Validator Poll",
"type": "n8n-nodes-base.wait",
"position": [
750,
592
],
"parameters": {
"amount": 60
},
"typeVersion": 1.1
},
{
"id": "a1000000-0000-0000-0000-000000000034",
"name": "Download Validator Results",
"type": "n8n-nodes-base.httpRequest",
"position": [
5500,
400
],
"parameters": {
"url": "=https://scrapercity.com/api/downloads/{{ $('Store Validator Run ID').item.json.validatorRunId }}",
"method": "GET",
"options": {},
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth"
},
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"typeVersion": 4.2
},
{
"id": "a1000000-0000-0000-0000-000000000035",
"name": "Parse Validator CSV",
"type": "n8n-nodes-base.code",
"position": [
5750,
400
],
"parameters": {
"jsCode": "// Parse CSV from email-validator download\n// Expected columns: email, status, deliverability, catch_all, mx_found\nconst raw = items[0].json.data || items[0].json.body || '';\nif (!raw) return [];\n\nconst lines = raw.trim().split('\\n');\nif (lines.length < 2) return [];\n\nconst headers = lines[0].split(',').map(h => h.trim().replace(/^\"|\"$/g, ''));\nconst results = [];\n\nfor (let i = 1; i < lines.length; i++) {\n const cols = lines[i].split(',').map(c => c.trim().replace(/^\"|\"$/g, ''));\n if (cols.length < headers.length) continue;\n const row = {};\n headers.forEach((h, idx) => { row[h] = cols[idx] || ''; });\n results.push({ json: row });\n}\n\nreturn results;"
},
"typeVersion": 2
},
{
"id": "a1000000-0000-0000-0000-000000000036",
"name": "Merge All Enrichment Data",
"type": "n8n-nodes-base.code",
"position": [
6250,
400
],
"parameters": {
"jsCode": "/**\n * Merge email-finder results, mobile-finder results, and validator results\n * by email address into a single enriched record per contact.\n *\n * This node must be triggered after all three downloads are complete.\n * It reads the outputs stored from the three parse steps.\n *\n * Since n8n Code nodes only receive items from the immediately connected\n * upstream node, we build a lookup from each stored array.\n *\n * The workflow passes all three arrays into this node via the Set node\n * \"Prepare Merge Inputs\" that precedes it.\n */\n\nconst emailRows = JSON.parse($json.emailFinderRows || '[]');\nconst mobileRows = JSON.parse($json.mobileFinderRows || '[]');\nconst validatorRows = JSON.parse($json.validatorRows || '[]');\n\n// Build lookup maps keyed by lowercase email\nconst mobileMap = {};\nconst validatorMap = {};\n\nfor (const r of mobileRows) {\n const key = (r.email || '').toLowerCase();\n if (key) mobileMap[key] = r;\n}\nfor (const r of validatorRows) {\n const key = (r.email || '').toLowerCase();\n if (key) validatorMap[key] = r;\n}\n\nconst merged = emailRows.map(row => {\n const key = (row.email || '').toLowerCase();\n const mobile = mobileMap[key] || {};\n const valid = validatorMap[key] || {};\n return {\n json: {\n first_name: row.first_name || '',\n last_name: row.last_name || '',\n domain: row.domain || '',\n email: row.email || '',\n email_confidence: row.confidence || '',\n phone: mobile.phone || '',\n phone_type: mobile.phone_type || '',\n phone_country: mobile.country || '',\n email_status: valid.status || '',\n deliverability: valid.deliverability || '',\n catch_all: valid.catch_all || '',\n mx_found: valid.mx_found || ''\n }\n };\n});\n\nreturn merged;"
},
"typeVersion": 2
},
{
"id": "a1000000-0000-0000-0000-000000000037",
"name": "Prepare Merge Inputs",
"type": "n8n-nodes-base.set",
"position": [
6000,
400
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "pm-001",
"name": "emailFinderRows",
"type": "string",
"value": "={{ JSON.stringify($('Parse Email Finder CSV').all().map(i => i.json)) }}"
},
{
"id": "pm-002",
"name": "mobileFinderRows",
"type": "string",
"value": "={{ JSON.stringify($('Parse Mobile Finder CSV').all().map(i => i.json)) }}"
},
{
"id": "pm-003",
"name": "validatorRows",
"type": "string",
"value": "={{ JSON.stringify($('Parse Validator CSV').all().map(i => i.json)) }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "a1000000-0000-0000-0000-000000000038",
"name": "Write Enriched Leads to Sheet",
"type": "n8n-nodes-base.googleSheets",
"position": [
6500,
400
],
"parameters": {
"columns": {
"value": {
"email": "={{ $json.email }}",
"phone": "={{ $json.phone }}",
"domain": "={{ $json.domain }}",
"mx_found": "={{ $json.mx_found }}",
"catch_all": "={{ $json.catch_all }}",
"last_name": "={{ $json.last_name }}",
"first_name": "={{ $json.first_name }}",
"phone_type": "={{ $json.phone_type }}",
"email_status": "={{ $json.email_status }}",
"phone_country": "={{ $json.phone_country }}",
"deliverability": "={{ $json.deliverability }}",
"email_confidence": "={{ $json.email_confidence }}"
},
"schema": [
{
"id": "first_name",
"required": false,
"displayName": "first_name",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "last_name",
"required": false,
"displayName": "last_name",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "domain",
"required": false,
"displayName": "domain",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "email",
"required": false,
"displayName": "email",
"defaultMatch": true,
"canBeUsedToMatch": true
},
{
"id": "email_confidence",
"required": false,
"displayName": "email_confidence",
"defaultMatch": false,
"canBeUsedToMatch": false
},
{
"id": "phone",
"required": false,
"displayName": "phone",
"defaultMatch": false,
"canBeUsedToMatch": false
},
{
"id": "phone_type",
"required": false,
"displayName": "phone_type",
"defaultMatch": false,
"canBeUsedToMatch": false
},
{
"id": "phone_country",
"required": false,
"displayName": "phone_country",
"defaultMatch": false,
"canBeUsedToMatch": false
},
{
"id": "email_status",
"required": false,
"displayName": "email_status",
"defaultMatch": false,
"canBeUsedToMatch": false
},
{
"id": "deliverability",
"required": false,
"displayName": "deliverability",
"defaultMatch": false,
"canBeUsedToMatch": false
},
{
"id": "catch_all",
"required": false,
"displayName": "catch_all",
"defaultMatch": false,
"canBeUsedToMatch": false
},
{
"id": "mx_found",
"required": false,
"displayName": "mx_found",
"defaultMatch": false,
"canBeUsedToMatch": false
}
],
"mappingMode": "defineBelow"
},
"options": {},
"operation": "appendOrUpdate",
"sheetName": {
"__rl": true,
"mode": "id",
"value": "={{ $('Configure Workflow').item.json.outputSheetName }}"
},
"documentId": {
"__rl": true,
"mode": "id",
"value": "={{ $('Configure Workflow').item.json.outputDocumentId }}"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.6
},
{
"id": "a1000000-0000-0000-0000-000000000050",
"name": "Overview",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2600,
100
],
"parameters": {
"color": 3,
"width": 500,
"height": 700,
"content": "## Lead Enrichment with Emails, Phones, and Validation\n\n### Who is this for\nB2B sales teams, SDRs, and growth marketers who have a spreadsheet of prospect names and company domains and need verified email addresses and mobile phone numbers -- without manual research.\n\n### What it does\n1. Reads contacts (first_name, last_name, domain) from a Google Sheet.\n2. Submits them to ScraperCity Email Finder to discover business emails.\n3. Submits found emails to ScraperCity Mobile Finder to look up phone numbers.\n4. Submits emails to ScraperCity Email Validator for deliverability checks.\n5. Merges all results and writes an enriched list back to Google Sheets.\n\n### Setup steps\n1. Create an HTTP Header Auth credential named **ScraperCity API Key** using your ScraperCity API key as the value (header name: Authorization, value: Bearer YOUR_KEY).\n2. Connect your Google account via the Google Sheets OAuth2 credential.\n3. Open the **Configure Workflow** node and fill in your input and output Google Sheets document IDs and sheet names.\n4. Make sure your input sheet has columns: first_name, last_name, domain.\n5. Click **Start Enrichment** to run.\n\n### Requirements\n- ScraperCity account with Email Finder, Mobile Finder, and Email Validator.\n- Google Sheets with OAuth2 credentials.\n\n### Notes\n- ScraperCity jobs are asynchronous and can take 10--60 minutes. The polling loops handle this automatically.\n- Each polling loop retries up to 40 times (up to ~40 minutes per stage)."
},
"typeVersion": 1
},
{
"id": "a1000000-0000-0000-0000-000000000051",
"name": "Section: Configuration",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2040,
-160
],
"parameters": {
"color": 5,
"width": 1730,
"height": 330,
"content": "## Configuration\n\nThe **Configure Workflow** node is the single place to set all user-configurable variables: input sheet document ID, input sheet name, output sheet document ID, and output sheet name. The **Read Contacts from Sheet** node reads every row from your input sheet and passes the contacts downstream for enrichment."
},
"typeVersion": 1
},
{
"id": "a1000000-0000-0000-0000-000000000052",
"name": "Setup: ScraperCity API key here",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1016,
288
],
"parameters": {
"color": 4,
"width": 280,
"height": 70,
"content": "Add your ScraperCity API key credential here"
},
"typeVersion": 1
},
{
"id": "a1000000-0000-0000-0000-000000000053",
"name": "Setup: Google Sheets IDs here",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1516,
288
],
"parameters": {
"color": 4,
"width": 280,
"height": 70,
"content": "Set your input and output Google Sheets document IDs here"
},
"typeVersion": 1
},
{
"id": "a1000000-0000-0000-0000-000000000054",
"name": "Section: Email Finder",
"type": "n8n-nodes-base.stickyNote",
"position": [
-290,
-160
],
"parameters": {
"color": 6,
"width": 1730,
"height": 530,
"content": "## Email Finder\n\nThe **Format Contacts for Email Finder** node transforms raw sheet rows into the JSON structure ScraperCity expects. **Start Email Finder Job** submits the batch and returns a runId. The **Poll Email Finder Loop** (splitInBatches) repeatedly calls **Check Email Finder Status** every 60 seconds until **Is Email Finder Complete?** sees status SUCCEEDED, then **Download Email Finder Results** fetches the CSV and **Parse Email Finder CSV** converts it into individual contact items."
},
"typeVersion": 1
},
{
"id": "a1000000-0000-0000-0000-000000000055",
"name": "Section: Mobile Finder",
"type": "n8n-nodes-base.stickyNote",
"position": [
1460,
-160
],
"parameters": {
"color": 7,
"width": 1730,
"height": 330,
"content": "## Mobile Finder\n\nOnce emails are found, **Format Emails for Mobile Finder** extracts the email list. **Start Mobile Finder Job** submits them to ScraperCity Mobile Finder, which looks up mobile numbers via work email. The **Poll Mobile Finder Loop** waits and polls **Check Mobile Finder Status** until complete, then **Download Mobile Finder Results** and **Parse Mobile Finder CSV** produce phone-enriched records keyed by email."
},
"typeVersion": 1
},
{
"id": "a1000000-0000-0000-0000-000000000056",
"name": "Section: Email Validator",
"type": "n8n-nodes-base.stickyNote",
"position": [
3210,
-160
],
"parameters": {
"color": 5,
"width": 1730,
"height": 330,
"content": "## Email Validator\n\n**Format Emails for Validator** prepares the email list for bulk verification. **Start Email Validator Job** submits them to ScraperCity Email Validator for deliverability, catch-all detection, and MX record checks. The **Poll Validator Loop** waits and polls **Check Validator Status** until done, then **Download Validator Results** and **Parse Validator CSV** produce validation-enriched records keyed by email."
},
"typeVersion": 1
},
{
"id": "a1000000-0000-0000-0000-000000000057",
"name": "Section: Merge and Output",
"type": "n8n-nodes-base.stickyNote",
"position": [
4960,
-160
],
"parameters": {
"color": 6,
"width": 1730,
"height": 330,
"content": "## Merge and Output\n\n**Prepare Merge Inputs** collects all three parsed result sets (email finder, mobile finder, validator) into a single node context. **Merge All Enrichment Data** joins them by email address into one row per contact. **Write Enriched Leads to Sheet** appends or updates the output Google Sheet with all enriched columns: email, confidence, phone, phone type, and validation status."
},
"typeVersion": 1
}
],
"settings": {
"executionOrder": "v1"
},
"connections": {
"Start Enrichment": {
"main": [
[
{
"node": "Configure Workflow",
"type": "main",
"index": 0
}
]
]
},
"Configure Workflow": {
"main": [
[
{
"node": "Read Contacts from Sheet",
"type": "main",
"index": 0
}
]
]
},
"Parse Validator CSV": {
"main": [
[
{
"node": "Prepare Merge Inputs",
"type": "main",
"index": 0
}
]
]
},
"Poll Validator Loop": {
"main": [
[
{
"node": "Check Validator Status",
"type": "main",
"index": 0
}
]
]
},
"Prepare Merge Inputs": {
"main": [
[
{
"node": "Merge All Enrichment Data",
"type": "main",
"index": 0
}
]
]
},
"Check Validator Status": {
"main": [
[
{
"node": "Is Validator Complete?",
"type": "main",
"index": 0
}
]
]
},
"Is Validator Complete?": {
"main": [
[
{
"node": "Download Validator Results",
"type": "main",
"index": 0
}
],
[
{
"node": "Wait Before Next Validator Poll",
"type": "main",
"index": 0
}
]
]
},
"Parse Email Finder CSV": {
"main": [
[
{
"node": "Remove Duplicate Emails",
"type": "main",
"index": 0
}
]
]
},
"Poll Email Finder Loop": {
"main": [
[
{
"node": "Check Email Finder Status",
"type": "main",
"index": 0
}
]
]
},
"Start Email Finder Job": {
"main": [
[
{
"node": "Store Email Finder Run ID",
"type": "main",
"index": 0
}
]
]
},
"Store Validator Run ID": {
"main": [
[
{
"node": "Wait Before Checking Validator",
"type": "main",
"index": 0
}
]
]
},
"Parse Mobile Finder CSV": {
"main": [
[
{
"node": "Format Emails for Validator",
"type": "main",
"index": 0
}
]
]
},
"Poll Mobile Finder Loop": {
"main": [
[
{
"node": "Check Mobile Finder Status",
"type": "main",
"index": 0
}
]
]
},
"Remove Duplicate Emails": {
"main": [
[
{
"node": "Filter Valid Emails Only",
"type": "main",
"index": 0
}
]
]
},
"Start Mobile Finder Job": {
"main": [
[
{
"node": "Store Mobile Finder Run ID",
"type": "main",
"index": 0
}
]
]
},
"Filter Valid Emails Only": {
"main": [
[
{
"node": "Format Emails for Mobile Finder",
"type": "main",
"index": 0
}
]
]
},
"Read Contacts from Sheet": {
"main": [
[
{
"node": "Format Contacts for Email Finder",
"type": "main",
"index": 0
}
]
]
},
"Check Email Finder Status": {
"main": [
[
{
"node": "Is Email Finder Complete?",
"type": "main",
"index": 0
}
]
]
},
"Is Email Finder Complete?": {
"main": [
[
{
"node": "Download Email Finder Results",
"type": "main",
"index": 0
}
],
[
{
"node": "Wait Before Next Email Finder Poll",
"type": "main",
"index": 0
}
]
]
},
"Merge All Enrichment Data": {
"main": [
[
{
"node": "Write Enriched Leads to Sheet",
"type": "main",
"index": 0
}
]
]
},
"Start Email Validator Job": {
"main": [
[
{
"node": "Store Validator Run ID",
"type": "main",
"index": 0
}
]
]
},
"Store Email Finder Run ID": {
"main": [
[
{
"node": "Wait Before Checking Email Finder",
"type": "main",
"index": 0
}
]
]
},
"Check Mobile Finder Status": {
"main": [
[
{
"node": "Is Mobile Finder Complete?",
"type": "main",
"index": 0
}
]
]
},
"Download Validator Results": {
"main": [
[
{
"node": "Parse Validator CSV",
"type": "main",
"index": 0
}
]
]
},
"Is Mobile Finder Complete?": {
"main": [
[
{
"node": "Download Mobile Finder Results",
"type": "main",
"index": 0
}
],
[
{
"node": "Wait Before Next Mobile Finder Poll",
"type": "main",
"index": 0
}
]
]
},
"Store Mobile Finder Run ID": {
"main": [
[
{
"node": "Wait Before Checking Mobile Finder",
"type": "main",
"index": 0
}
]
]
},
"Format Emails for Validator": {
"main": [
[
{
"node": "Start Email Validator Job",
"type": "main",
"index": 0
}
]
]
},
"Download Email Finder Results": {
"main": [
[
{
"node": "Parse Email Finder CSV",
"type": "main",
"index": 0
}
]
]
},
"Download Mobile Finder Results": {
"main": [
[
{
"node": "Parse Mobile Finder CSV",
"type": "main",
"index": 0
}
]
]
},
"Wait Before Checking Validator": {
"main": [
[
{
"node": "Poll Validator Loop",
"type": "main",
"index": 0
}
]
]
},
"Format Emails for Mobile Finder": {
"main": [
[
{
"node": "Start Mobile Finder Job",
"type": "main",
"index": 0
}
]
]
},
"Wait Before Next Validator Poll": {
"main": [
[
{
"node": "Poll Validator Loop",
"type": "main",
"index": 0
}
]
]
},
"Format Contacts for Email Finder": {
"main": [
[
{
"node": "Start Email Finder Job",
"type": "main",
"index": 0
}
]
]
},
"Wait Before Checking Email Finder": {
"main": [
[
{
"node": "Poll Email Finder Loop",
"type": "main",
"index": 0
}
]
]
},
"Wait Before Checking Mobile Finder": {
"main": [
[
{
"node": "Poll Mobile Finder Loop",
"type": "main",
"index": 0
}
]
]
},
"Wait Before Next Email Finder Poll": {
"main": [
[
{
"node": "Poll Email Finder Loop",
"type": "main",
"index": 0
}
]
]
},
"Wait Before Next Mobile Finder Poll": {
"main": [
[
{
"node": "Poll Mobile Finder Loop",
"type": "main",
"index": 0
}
]
]
}
}
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
googleSheetsOAuth2ApihttpHeaderAuth
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This template is for B2B sales teams, SDRs, growth marketers, and founders who maintain a spreadsheet of prospects and need verified contact details -- emails and mobile numbers -- without manual research. Reads a list of contacts (first name, last name, company domain) from a…
Source: https://n8n.io/workflows/13854/ — 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.
This n8n workflow automates the process of finding ecommerce seller leads, enriching them with product and business details, discovering company websites, and extracting contact information such as em
This workflow finds local businesses from Google Maps and automatically enriches them with emails, social profiles, AI summaries, and personalized outreach messages — all saved to Google Sheets. Searc
This workflow leverages n8n to perform automated Google Maps API queries and manage data efficiently in Google Sheets. It's designed to extract specific location data based on a given list of ZIP code
This repository contains an SLA-based lead routing workflow built in n8n, designed to ensure fast lead response, fair sales distribution, and controlled escalation without relying on a full CRM system
This n8n workflow is a sophisticated B2B Lead Generation Scraper. It automates the entire journey from discovering businesses on Google Maps to extracting, scoring, and saving high-quality contact ema