Google Sheets + Hunter.io: enriched leads, ready to email
You finally have a list of prospects… and then you lose an afternoon figuring out company domains and guessing emails. Copy-paste. Open ten tabs. Second-guess everything. It’s tedious, and it’s the kind of busywork that quietly kills outbound momentum.
Recruiters feel it when they’re trying to message candidates fast. A lead gen manager feels it when the sheet grows past 200 rows. And if you run an agency, you’ve probably promised “fresh leads weekly” while your team is stuck doing Hunter Google Sheets enrichment by hand.
This workflow pulls contacts from Google Sheets, uses AI to figure out the right company domain, then calls Hunter.io to find the best-match email. You will see how the automation works, what you need, and what outcomes to expect once it’s running.
How This Automation Works
The full n8n workflow, from trigger to final output:
n8n Workflow Template: Google Sheets + Hunter.io: enriched leads, ready to email
flowchart LR
subgraph sg0["When clicking ‘Execute workflow’ Flow"]
direction LR
n0@{ icon: "mdi:play-circle", form: "rounded", label: "When clicking ‘Execute workf..", pos: "b", h: 48 }
n1@{ icon: "mdi:database", form: "rounded", label: "Get row(s) in sheet", pos: "b", h: 48 }
n2@{ icon: "mdi:robot", form: "rounded", label: "AI Agent", pos: "b", h: 48 }
n3@{ icon: "mdi:brain", form: "rounded", label: "Google Gemini Chat Model", pos: "b", h: 48 }
n4["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/code.svg' width='40' height='40' /></div><br/>Code in JavaScript"]
n5@{ icon: "mdi:swap-horizontal", form: "rounded", label: "Switch", pos: "b", h: 48 }
n6["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/httprequest.dark.svg' width='40' height='40' /></div><br/>HTTP Request"]
n7["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/httprequest.dark.svg' width='40' height='40' /></div><br/>HTTP Request1"]
n8@{ icon: "mdi:database", form: "rounded", label: "Append or update row in sheet", pos: "b", h: 48 }
n9["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/httprequest.dark.svg' width='40' height='40' /></div><br/>HTTP Request2"]
n10["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/code.svg' width='40' height='40' /></div><br/>Code in JavaScript1"]
n11@{ icon: "mdi:robot", form: "rounded", label: "AI Agent1", pos: "b", h: 48 }
n12@{ icon: "mdi:brain", form: "rounded", label: "Google Gemini Chat Model1", pos: "b", h: 48 }
n13["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/code.svg' width='40' height='40' /></div><br/>Code in JavaScript2"]
n14@{ icon: "mdi:database", form: "rounded", label: "Append or update row in sheet1", pos: "b", h: 48 }
n5 --> n6
n5 --> n7
n2 --> n4
n11 --> n13
n6 --> n8
n7 --> n10
n9 --> n14
n4 --> n5
n10 --> n11
n13 --> n9
n1 --> n2
n3 -.-> n2
n12 -.-> n11
n0 --> n1
end
%% Styling
classDef trigger fill:#e8f5e9,stroke:#388e3c,stroke-width:2px
classDef ai fill:#e3f2fd,stroke:#1976d2,stroke-width:2px
classDef aiModel fill:#e8eaf6,stroke:#3f51b5,stroke-width:2px
classDef decision fill:#fff8e1,stroke:#f9a825,stroke-width:2px
classDef database fill:#fce4ec,stroke:#c2185b,stroke-width:2px
classDef api fill:#fff3e0,stroke:#e65100,stroke-width:2px
classDef code fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px
classDef disabled stroke-dasharray: 5 5,opacity: 0.5
class n0 trigger
class n2,n11 ai
class n3,n12 aiModel
class n5 decision
class n1,n8,n14 database
class n6,n7,n9 api
class n4,n10,n13 code
class n10 disabled
classDef customIcon fill:none,stroke:none
class n4,n6,n7,n9,n10,n13 customIcon
The Problem: Lead enrichment turns into tab-hopping
A Google Sheet full of “Name, Title, Description” looks organized. In reality, it’s incomplete. To actually use it, somebody has to translate messy profile text into a real company domain, then use that domain to find a deliverable email. If you’re pulling profiles from LinkedIn, it gets worse because descriptions are inconsistent, companies are abbreviated, and a single wrong domain sends you down a rabbit hole. After a few dozen leads, the mental load kicks in. You start making guesses. Those guesses become bounces, wasted follow-ups, and awkward “wrong person” replies.
It adds up fast. Here’s where it breaks down in real teams.
- Each lead can take about 5–10 minutes to research when the company name is unclear.
- People “fix” the sheet differently, so domains and emails end up inconsistent across rows.
- Manual Hunter lookups invite mistakes, like mixing up two similar companies or copying the wrong result.
- You don’t know which records are safe to email until someone checks them one-by-one.
The Solution: Google Sheets enrichment with AI + Hunter.io
This n8n workflow starts with your existing Google Sheet, reads each contact row (name, position, description, and any existing domain), and then makes a smart decision about what to do next. An AI agent first tries to infer the company’s domain directly from the profile text, which is often enough when the description mentions the company clearly. If the domain isn’t obvious, the workflow automatically creates a better search query and calls Google Custom Search to pull real web results. Another AI agent reviews those results to choose the most accurate domain (not a random directory site). Once the domain is confirmed, the workflow calls Hunter.io to find the best-matching email for that person at that domain. Finally, it writes the domain and email back into the same Google Sheet, updating the row so your list is immediately usable.
The workflow kicks off from an n8n trigger, then processes your sheet in batches so it can handle larger lists without choking. It routes contacts down a “domain already known” path or a “domain needs discovery” path, then merges back into a single output that updates your sheet with enriched fields.
What You Get: Automation vs. Results
| What This Workflow Automates | Results You’ll Get |
|---|---|
|
|
Example: What This Looks Like
Say you collect 200 LinkedIn profiles into a sheet each week. Manually, even a quick process is usually about 7 minutes per lead (find domain, run Hunter lookup, paste results), which is roughly 23 hours of work. With this workflow, you can kick it off in about 5 minutes, let n8n process the list in the background for around an hour, then review the sheet and spot-check a handful of rows. That’s basically two workdays back, every week.
What You’ll Need
- n8n instance (try n8n Cloud free)
- Self-hosting option if you prefer (Hostinger works well)
- Google Sheets to store leads and write results back.
- Hunter.io to find best-match emails by domain.
- Google Gemini (PaLM) API key (get it from Google AI Studio / Google Cloud).
- Google Custom Search API key + CSE ID (get it from Google Cloud and Custom Search Engine setup).
- Google Sheets OAuth2 credentials (create in Google Cloud console).
Skill level: Intermediate. You’ll be connecting credentials and matching your sheet columns, plus doing light prompt edits if you want better accuracy.
Don’t want to set this up yourself? Talk to an automation expert (free 15-minute consultation).
How It Works
A manual run (or scheduled run) starts the enrichment. In the template, you click Manual Run in n8n, and it immediately fetches rows from your Google Sheet. Many teams later switch this trigger to a schedule so new leads get enriched every morning.
The workflow reads the sheet and sends each contact to an AI “domain discovery” pass. The AI looks at the person’s name, role, and description to infer a likely company domain. If it can confidently pull a domain, it passes that along.
If the domain is missing, it uses Google Custom Search and a second AI check. n8n runs a search request, extracts candidate domains from the results, then asks another AI agent to pick the correct match. This is the difference between “probably right” and “safe enough to use in outreach.”
Hunter.io finds the best email and Google Sheets gets updated. Once a domain is confirmed, the workflow calls Hunter’s API to get the best match email for the contact, then updates the row with the email (and domain if it was missing). Your sheet becomes the source of truth.
You can easily modify the sheet columns it reads to match your CRM export, and you can swap the domain discovery prompt to fit your market. See the full implementation guide below for customization options.
Step-by-Step Implementation Guide
Step 1: Configure the Manual Trigger
This workflow starts manually so you can control when enrichment runs.
- Add the Manual Run Trigger node as the starting trigger.
- Keep the default settings—no fields are required for this trigger.
- Confirm the flow connects from Manual Run Trigger to Fetch Sheet Rows.
Step 2: Connect Google Sheets
Pull LinkedIn profile rows from Google Sheets as the input data.
- Open Fetch Sheet Rows and set Document to
[YOUR_ID]and Sheet toSheet1(value:gid=0). - Credential Required: Connect your
googleSheetsOAuth2Apicredentials in Fetch Sheet Rows. - In Update Sheet with Email, keep Operation set to
appendOrUpdateand verify column mappings use expressions like{{ $('Fetch Sheet Rows').item.json.name }}. - Credential Required: Connect your
googleSheetsOAuth2Apicredentials in Update Sheet with Email. - In Update Sheet with Domain, keep Operation set to
appendOrUpdateand confirm mappings for name, email, and domain are set to{{ $('Fetch Sheet Rows').item.json.name }},{{ $json.data.email }}, and{{ $json.data.domain }}. - Credential Required: Connect your
googleSheetsOAuth2Apicredentials in Update Sheet with Domain.
Step 3: Set Up AI Domain Discovery
The AI agent extracts a domain or creates a search term when no domain is found.
- Open Domain Discovery Agent and keep the prompt text as configured, including the JSON output requirement.
- Ensure the input data placeholders are set to
{{ $json.name }},{{ $json.position }}, and{{ $json.description }}. - Confirm Gemini Chat Engine is connected as the language model for Domain Discovery Agent.
- Credential Required: Connect your
googlePalmApicredentials in Gemini Chat Engine (credentials are applied on the language model, not the agent node). - Keep Parse AI Output code unchanged to strip code fences and parse JSON reliably.
Step 4: Configure Domain Routing and Hunter Lookup
Route based on whether a domain exists and enrich emails through Hunter.
- In Branch by Domain, keep the rules that check
{{ $json.domain }}is not equal tonulland that{{ $json["search term"] }}is not equal tonull. - Configure Hunter Email Lookup to use URL
https://api.hunter.io/v2/email-finder?and keep Query Parameters set todomain={{ $json.domain }},first_name={{ $('Fetch Sheet Rows').item.json.name }}, andlast_name={{ $('Fetch Sheet Rows').item.json.name }}. - Credential Required: Connect your
httpBearerAuthcredentials in Hunter Email Lookup. - Confirm the flow from Hunter Email Lookup goes to Update Sheet with Email to write back enriched data.
Step 5: Configure Google Search and Domain Matching
When no domain is known, the workflow searches Google and matches the best domain using AI.
- In Google Search Request, set URL to
https://www.googleapis.com/customsearch/v1and keep query parameterscx=[YOUR_ID]andq={{ $json["search term"] }}. - Credential Required: Connect your
httpBasicAuthcredentials in Google Search Request (the node also includeshttpBearerAuthin credentials; ensure the correct auth matches your API setup). - Enable Extract Domains Script (it is currently disabled) and keep the provided JavaScript to normalize and extract domains from search results.
- Open Domain Match Agent and keep the prompt that uses
{{ $('Branch by Domain').item.json["search term"] }}and{{ $json.domains }}for matching. - Confirm Gemini Chat Engine 2 is connected as the language model for Domain Match Agent.
- Credential Required: Connect your
googlePalmApicredentials in Gemini Chat Engine 2 (credentials are applied on the language model, not the agent node). - Keep Parse Match Output code unchanged so the JSON output is parsed correctly.
Step 6: Configure Secondary Hunter Lookup and Writeback
After matching a domain, the workflow performs a second Hunter lookup and writes results to the sheet.
- Configure Hunter Email Lookup 2 with URL
https://api.hunter.io/v2/email-finder?and query parametersdomain={{ $json.domain }},first_name={{ $('Fetch Sheet Rows').item.json.name }}, andlast_name={{ $('Fetch Sheet Rows').item.json.name }}. - Credential Required: Connect your
httpBearerAuthcredentials in Hunter Email Lookup 2. - Confirm the flow from Hunter Email Lookup 2 goes to Update Sheet with Domain to save the matched domain and email.
Step 7: Test and Activate Your Workflow
Run a manual test to validate the enrichment, then activate the workflow for ongoing use.
- Click Execute Workflow and trigger Manual Run Trigger to start the run.
- Verify that Fetch Sheet Rows returns rows and that Domain Discovery Agent outputs a JSON object that Parse AI Output can parse.
- Check that either Hunter Email Lookup or Google Search Request runs depending on the output of Branch by Domain.
- Confirm that Update Sheet with Email or Update Sheet with Domain updates the appropriate row with
emailanddomain. - Once successful, toggle the workflow to Active for production usage.
Common Gotchas
- Google Sheets credentials can expire or need specific permissions. If things break, check your Google Cloud OAuth consent screen and the connected account access in n8n first.
- If you’re using Wait nodes or external rendering, processing times vary. Bump up the wait duration if downstream nodes fail on empty responses.
- Hunter.io will return weak matches if the domain is wrong. When results look off, inspect the “Update Sheet with Domain” output row and confirm the domain is a real company site, not a profile directory.
- Default prompts in AI nodes are generic. Add your brand voice early or you’ll be editing outputs forever.
Frequently Asked Questions
About an hour if your API keys and sheet are ready.
No. You’ll connect accounts, paste a few API keys, and match the column names in your sheet.
Yes. n8n has a free self-hosted option and a free trial on n8n Cloud. Cloud plans start at $20/month for higher volume. You’ll also need to factor in Hunter.io and Google API usage, plus Gemini model costs depending on how many rows you process.
Two options: n8n Cloud (managed, easiest setup) or self-hosting on a VPS. For self-hosting, Hostinger VPS is affordable and handles n8n well. Self-hosting gives you unlimited executions but requires basic server management.
Yes, and it’s one of the best reasons to use n8n. You can point the “Fetch Sheet Rows” node at a different spreadsheet, then update the “Update Sheet with Domain” and “Update Sheet with Email” nodes to write into your preferred columns. Common tweaks include adding industry tags, adding LinkedIn profile URLs, or pushing “ready” rows into a separate tab for outreach. If you want to swap the AI provider, you would replace the Gemini chat nodes and keep the rest of the flow the same.
Usually it’s an invalid or expired API key. Regenerate your Hunter.io API key, update it in the HTTP Request nodes used for “Hunter Email Lookup,” then run a single test row to confirm you’re getting a response. If it still fails, check Hunter account limits and make sure the domain you’re passing is a real company domain (not a LinkedIn URL or a directory site). Rate limiting can also show up when you push large batches at once, so smaller batches help.
A lot, as long as your API quotas support it. On n8n Cloud Starter you’re limited by monthly executions, while self-hosting removes the n8n execution cap and shifts the limit to your server and API providers. Practically, most teams run hundreds of leads per day without issues once batching is set sensibly. If you want to process thousands at a time, plan for Google Custom Search limits and Hunter request limits, because those usually hit first.
Often, yes, because this flow needs branching logic, fallbacks, and a couple of AI “decision points.” Zapier and Make can do it, but it gets fiddly and expensive when you add searches, parsing, and multiple HTTP calls per lead. n8n also gives you the option to self-host, which matters when you want to run big lists without watching task counts. The honest caveat: if you only need “domain present → Hunter lookup → write back,” Zapier is quick to set up. If you want the full domain discovery and validation path, n8n is a better fit. Talk to an automation expert if you’re torn.
Once this is set up, your sheet stops being “raw leads” and becomes “ready to email.” The workflow handles the repetitive parts, and you get to focus on outreach that actually converts.
Need Help Setting This Up?
Our automation experts can build and customize this workflow for your specific needs. Free 15-minute consultation—no commitment required.