Made this workflow since I was losing a lot of leads for cold email, since AnyMail Finder could not find decision-maker emails for like 40% of the leads I scraped. Ideally, I would want an owner's name and their email to send.
The old system would just pull a list of qualified leads from a Google Sheet, then for each lead try to find the decision‑maker’s email using anymail finder.
Now, if that fails, it crawls the company’s website to extract any email address using a crawler I made with Claude code or using a webcrawler Apify actor which I also built with Claude code. It then verifies the found email with anymail finders email verification tool, and finally uses AI to identify the owner or founder’s name from the same used crawler to get the email.
All the discovered information is written back to the same Google Sheet where I now would have updated leads with emails found from the website and the owner's name. So now while yes, it's a generic company website email with the owner's name, I can still use these as leads for cold email at scale.
would now
Any tips to improve it would be appreciated!
If you want the template here!
{ "nodes": [ { "parameters": { "operation": "findDecisionMaker", "domain": "={{ $('Find qualified lead').item.json.website }}", "companyName": "={{ $('Find qualified lead').item.json['Company name'] }}" }, "type": "n8n-nodes-anymailfinder-official.anymailFinder", "typeVersion": 1, "position": [ -1056, 224 ], "id": "eb4094a8-24ba-4aba-ace9-aaaf299cfb59", "name": "Anymail Finder" }, { "parameters": { "options": {} }, "type": "n8n-nodes-base.splitInBatches", "typeVersion": 3, "position": [ -1328, 224 ], "id": "e9695097-7e1e-4c73-b91e-0e17a8b68808", "name": "Loop Over Items" }, { "parameters": { "jsCode": "const pages = $input.first().json.pages\nconst input = pages.map(p => p.content).join(' ')\n\nconst regex = /[a-zA-Z0-9.%+-]+@[a-zA-Z0-9.-]+\.(?!png|jpg|jpeg|gif|webp|svg)[a-zA-Z]{2,}/g\nconst blacklist = [\n 'wix.com', 'wixpress.com', 'sentry.io', 'sentry-next.wixpress.com',\n 'squarespace.com', 'shopify.com', 'webflow.io', 'amazonaws.com',\n 'cloudfront.net', 'google.com', 'facebook.com', 'instagram.com'\n]\nconst emails = [...new Set(input.match(regex) || [])]\n .filter(email => !blacklist.some(domain => email.includes(domain)))\nreturn { json: { emails: emails, firstEmail: emails[0] || null } }" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ -272, 448 ], "id": "650fb7f3-f05e-45be-bfb7-622541b71603", "name": "Regex to get email" }, { "parameters": { "method": "POST", "url": "https://api.apify.com/v2/acts/transparent_baobab~web-crawler/run-sync-get-dataset-items", "sendBody": true, "specifyBody": "json", "jsonBody": "={ \"url\": \"{{ $('Find qualified lead').item.json.website }}\" }", "options": {} }, "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.4, "position": [ 224, 896 ], "id": "4a9028c2-bbbe-4aa8-bfc7-3fc60506ed18", "name": "Apify actor web crawler" }, { "parameters": { "jsCode": "const input = $input.first().json.html\nconst regex = /[a-zA-Z0-9.%+-]+@[a-zA-Z0-9.-]+\.(?!png|jpg|jpeg|gif|webp|svg)[a-zA-Z]{2,}/g\nconst blacklist = [\n 'wix.com', 'wixpress.com', 'sentry.io', 'sentry-next.wixpress.com',\n 'squarespace.com', 'shopify.com', 'webflow.io', 'amazonaws.com',\n 'cloudfront.net', 'google.com', 'facebook.com', 'instagram.com'\n]\n\nconst emails = [...new Set(input.match(regex) || [])]\n .filter(email => !blacklist.some(domain => email.includes(domain)))\n\nreturn { json: { emails: emails, firstEmail: emails[0] || null } }" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 432, 896 ], "id": "94595b8d-e938-4d73-a1a0-6a2021999f88", "name": "Regex for email2" }, { "parameters": { "assignments": { "assignments": [ { "id": "b0556af2-48b0-4e24-98d8-26acdf542f01", "name": "First name", "value": "={{ $json.personfull_name.split(' ')[0] }}", "type": "string" }, { "id": "f408e9f8-5b01-495c-a997-ed88ae421fe0", "name": "Last Name", "value": "={{ $json.person_full_name.split(' ')[1] }}", "type": "string" } ] }, "options": {} }, "type": "n8n-nodes-base.set", "typeVersion": 3.4, "position": [ -592, 208 ], "id": "91225af5-375a-4eba-8577-62d13f5f1ebf", "name": "Separate first and last name to different strings" }, { "parameters": { "assignments": { "assignments": [ { "id": "10638cf5-3d19-4324-8800-d11e6e8d156e", "name": "Emails found", "value": "", "type": "string" }, { "id": "1eefc3cf-c928-4912-904d-e309793b981f", "name": "Has been verified", "value": "not_found", "type": "string" } ] }, "options": {} }, "type": "n8n-nodes-base.set", "typeVersion": 3.4, "position": [ 1488, 880 ], "id": "02d75318-ae35-44c8-8a92-b8c53dd929d9", "name": "Set email not_found and empty" }, { "parameters": { "assignments": { "assignments": [ { "id": "ac0531c4-7149-42ce-baa4-28877edeb5db", "name": "email found", "value": "={{ $json.firstEmail }}", "type": "string" }, { "id": "9e69b5c8-96d2-47b1-bafc-414d3f87136f", "name": "has been verified", "value": "valid", "type": "string" } ] }, "options": {} }, "type": "n8n-nodes-base.set", "typeVersion": 3.4, "position": [ 1184, 416 ], "id": "05422e72-a4f8-4327-8b94-e11e1c019f6d", "name": "set email as valid and found" }, { "parameters": { "jsCode": "const pages = $('Crawler DOMcontentloader hostinger').first().json.pages || [];\n\nconst priorityKeywords = [\n ['team', 'staff', 'people', 'meet'],\n ['about', 'story', 'history'],\n];\n\nlet selectedPage = null;\n\nfor (const keywords of priorityKeywords) {\n const found = pages.find(p => {\n const url = p.url.toLowerCase();\n const title = p.title?.toLowerCase() || '';\n \n return keywords.some(kw => url.includes(kw) || title.includes(kw));\n });\n \n if (found) {\n selectedPage = found;\n break;\n }\n}\n\nif (!selectedPage) {\n selectedPage = pages.find(p => p.url === '/' || p.url === '') || pages[0];\n}\n\nconst content = selectedPage ? selectedPage.content : '';\nconst capped = content\n .split(' ')\n .slice(0, 8000)\n .join(' ');\n\nreturn [{\n json: {\n combined_content: capped,\n used_page: selectedPage?.url || 'none',\n used_title: selectedPage?.title || 'none',\n word_count: capped.split(' ').length\n }\n}];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 1696, 416 ], "id": "4af800b6-ce2a-46a4-a1e2-2f4811f380e1", "name": "Filter about us pages/team" }, { "parameters": { "modelId": { "_rl": true, "value": "claude-haiku-4-5-20251001", "mode": "list", "cachedResultName": "claude-haiku-4-5-20251001" }, "messages": { "values": [ { "content": "=Extract the owner, founder, CEO, or head coach name from this website content.\n\nContent:\n{{ $json.combined_content }}\n\nInstructions:\n- Look for any person's name associated with ownership, leadership, or founding\n- Search for phrases like:\n * \"Founded by [name]\"\n * \"Owner: [name]\"\n * \"Led by [name]\"\n * \"[Name] established\"\n * \"[Name], founder/owner/CEO\"\n * \"Meet [name], our coach/owner\"\n * Signatures or author names at the bottom\n \n- If you find multiple people, return the one most likely to be the primary owner/founder\n- Do NOT include titles, just the name\n\nReturn ONLY a valid JSON object with no additional text or markdown:\n{\n \"first_name\": \"Steve\",\n \"last_name\": \"Johnson\"\n}\n\nIf only one name found:\n{\n \"first_name\": \"Steve\",\n \"last_name\": \"\"\n}\n\nIf not found:\n{\n \"first_name\": \"Not found\",\n \"last_name\": \"\"\n}" } ] }, "options": {} }, "type": "@n8n/n8n-nodes-langchain.anthropic", "typeVersion": 1, "position": [ 1984, 416 ], "id": "e7f6b3a6-cb32-4012-b7f8-9e67fd044f76", "name": "AI to find owner name", "onError": "continueErrorOutput" }, { "parameters": { "jsCode": "const response = $json.content || [];\nconst textBlock = response.find(item => item.type === 'text');\nconst rawText = textBlock?.text || '{}';\n\nconst cleaned = rawText\n .replace(/json/g, '')\n .replace(//g, '')\n .replace(/\n/g, '')\n .trim();\n\nconsole.log('Raw text:', rawText);\nconsole.log('Cleaned:', cleaned);\n\ntry {\n const parsed = JSON.parse(cleaned);\n console.log('Parsed:', parsed);\n return [{\n json: {\n first_name: parsed.first_name || 'Not found',\n last_name: parsed.last_name || ''\n }\n }];\n} catch (e) {\n console.log('Parse error:', e.message);\n return [{\n json: {\n first_name: 'Parse failed',\n last_name: e.message\n }\n }];\n}" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 2368, 416 ], "id": "b16866d3-26f2-48c1-bf74-9e39687cf048", "name": "cleanup" }, { "parameters": { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "id": "584e43b5-1bf7-44ff-953b-cf8f621d6cf1", "leftValue": "={{ $json['first name'] }}", "rightValue": "0", "operator": { "type": "string", "operation": "empty", "singleValue": true } }, { "id": "88a334ea-5826-4c0a-a006-74be3f77c66c", "leftValue": "={{ $json['Used/not used'] }}", "rightValue": "", "operator": { "type": "string", "operation": "empty", "singleValue": true } } ], "combinator": "and" }, "options": {} }, "type": "n8n-nodes-base.if", "typeVersion": 2.3, "position": [ -1552, 240 ], "id": "bf99bd3b-3bfd-4338-ab6f-b3b7f7616c54", "name": "Check if lead needs processing" }, { "parameters": { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "id": "42f55dd3-991a-4794-9fc0-2bbf11224b21", "leftValue": "={{ $json.email_status }}", "rightValue": "valid", "operator": { "type": "string", "operation": "equals" } }, { "id": "9447f2ad-e4c0-43ca-bb14-f0e9acba8ef7", "leftValue": "={{ $json.person_full_name }}", "rightValue": "not_found", "operator": { "type": "string", "operation": "exists", "singleValue": true } } ], "combinator": "and" }, "options": {} }, "type": "n8n-nodes-base.if", "typeVersion": 2.3, "position": [ -848, 224 ], "id": "3ad4a0b0-615b-4812-a615-5664e58bddc2", "name": "If decision maker email is found" }, { "parameters": { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "id": "b3abd17d-b6b8-436e-9c2f-b5a71b44f6cd", "leftValue": "={{ $json.email_status }}", "rightValue": "valid", "operator": { "type": "string", "operation": "equals" } } ], "combinator": "and" }, "options": {} }, "type": "n8n-nodes-base.if", "typeVersion": 2.3, "position": [ 512, 432 ], "id": "09a6b78d-4b36-490e-ad4e-901384a30871", "name": "If verified email" }, { "parameters": { "conditions": { "options": { "caseSensitive": false, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "id": "b36984bb-af81-4a53-a4f6-664ff72b8005", "leftValue": "={{ $json.firstEmail }}", "rightValue": "valid", "operator": { "type": "string", "operation": "exists", "singleValue": true } } ], "combinator": "and" }, "options": { "ignoreCase": true } }, "type": "n8n-nodes-base.if", "typeVersion": 2.3, "position": [ -80, 448 ], "id": "7daf1e61-0e4b-4fa3-be7f-fd9ed3cda07e", "name": "If generic email exists" }, { "parameters": { "conditions": { "options": { "caseSensitive": false, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "id": "b36984bb-af81-4a53-a4f6-664ff72b8005", "leftValue": "={{ $json.firstEmail }}", "rightValue": "valid", "operator": { "type": "string", "operation": "exists", "singleValue": true } } ], "combinator": "and" }, "options": { "ignoreCase": true } }, "type": "n8n-nodes-base.if", "typeVersion": 2.3, "position": [ 624, 896 ], "id": "ba5ceab7-167f-4b16-bde5-94f13cea47b6", "name": "If generic email exists1" }, { "parameters": { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 3 }, "conditions": [ { "id": "b36984bb-af81-4a53-a4f6-664ff72b8005", "leftValue": "={{ $json.email_status }}", "rightValue": "valid", "operator": { "type": "string", "operation": "notContains" } } ], "combinator": "and" }, "options": {} }, "type": "n8n-nodes-base.if", "typeVersion": 2.3, "position": [ 1088, 1040 ], "id": "624e415c-d64c-43f3-b7c6-55279fcde21e", "name": "If valid email" }, { "parameters": {}, "type": "n8n-nodes-base.manualTrigger", "typeVersion": 1, "position": [ -1952, 240 ], "id": "eef3b044-243c-4171-9bd9-428eb0590f18", "name": "When clicking 'Execute workflow'" } ], "connections": { "Anymail Finder": { "main": [ [ { "node": "If decision maker email is found", "type": "main", "index": 0 } ] ] }, "Loop Over Items": { "main": [ [ { "node": "Anymail Finder", "type": "main", "index": 0 } ] ] } } }