Find and verify emails for HubSpot prospects using Apollo and Hunter in n8n
Prerequisites
- n8n instance — n8n cloud or self-hosted
- HubSpot private app token with
crm.objects.contacts.readandcrm.objects.contacts.writescopes - Apollo API key with email finder credits (Settings → Integrations → API)
- Hunter.io API key with verification credits (Dashboard → API)
- n8n credential configured for HubSpot
Step 1: Schedule the workflow
Add a Schedule Trigger node:
- Trigger interval: Days
- Days between triggers: 1
- Hour: 6
- Timezone: Your team's timezone
Running daily catches new prospects imported the previous day.
Step 2: Search HubSpot for contacts without emails
Add an HTTP Request node to find contacts that have a name and company but no email:
- Method: POST
- URL:
https://api.hubapi.com/crm/v3/objects/contacts/search - Authentication: HubSpot credential
- Body:
{
"filterGroups": [
{
"filters": [
{
"propertyName": "email",
"operator": "NOT_HAS_PROPERTY"
},
{
"propertyName": "firstname",
"operator": "HAS_PROPERTY"
},
{
"propertyName": "company",
"operator": "HAS_PROPERTY"
}
]
}
],
"properties": ["firstname", "lastname", "company", "domain", "email"],
"limit": 50
}Apollo's People Match works best with an email or a domain + name combination. If your contacts have a domain property (e.g., acme.com), use that. If they only have company (e.g., "Acme Inc"), Apollo will try to resolve the domain, but results are less reliable.
Step 3: Loop through contacts and call Apollo
Add a Split In Batches node to iterate over the search results (set batch size to 1).
Add an HTTP Request node to find the email via Apollo:
- Method: POST
- URL:
https://api.apollo.io/api/v1/people/match - Headers:
x-api-key: Your Apollo API keyContent-Type:application/json
- Body:
{
"first_name": "{{ $json.properties.firstname }}",
"last_name": "{{ $json.properties.lastname }}",
"organization_name": "{{ $json.properties.company }}"
}Apollo returns the person's email in person.email along with a confidence indicator. If no match is found, person will be null.
Step 4: Check confidence and decide whether to verify
Add an IF node to evaluate the Apollo result:
- Condition:
{{ $json.person.email }}is not empty
On the true branch, add a second IF node to check confidence:
- High confidence (skip verification): Check if
{{ $json.person.email_status }}equalsverified - Lower confidence (needs verification): Everything else goes to Hunter
Apollo marks some emails as verified — these have already been checked by Apollo's internal systems. Skip Hunter verification for these to save credits. Only verify emails marked as guessed or likely.
Step 5: Verify with Hunter
On the "needs verification" branch, add an HTTP Request node:
- Method: GET
- URL:
https://api.hunter.io/v2/email-verifier - Query Parameters:
email:{{ $json.person.email }}api_key: Your Hunter API key
Hunter returns a data.result field with one of these values:
deliverable— safe to userisky— might bounceundeliverable— definitely badunknown— Hunter couldn't determine
Add an IF node:
- Condition:
{{ $json.data.result }}equalsdeliverable
Hunter's free plan allows 25 verifications/month. Paid plans start at $49/mo for 1,000 verifications. If you're verifying more than a handful of emails, you'll need a paid plan. The API rate limit is 15 requests/second.
Step 6: Update HubSpot with the verified email
On the verified branch, add an HTTP Request node:
- Method: PATCH
- URL:
https://api.hubapi.com/crm/v3/objects/contacts/{{ $('Search Contacts').item.json.id }} - Authentication: HubSpot credential
- Body:
{
"properties": {
"email": "{{ $json.person.email }}",
"email_verification_status": "verified",
"email_source": "apollo+hunter"
}
}For contacts where the email was risky or undeliverable, route to a separate update that sets a flag:
{
"properties": {
"email_verification_status": "unverified",
"email_source": "apollo"
}
}Step 7: Add a Wait node for rate limiting
Add a Wait node (set to 1 second) after the Apollo HTTP Request to stay within Apollo's rate limits (5 req/sec on Basic plans).
Step 8: Test and activate
- Click Execute Workflow to run against your prospect list
- Check each node — verify Apollo returns emails and Hunter verifies them
- Open a few contacts in HubSpot to confirm the email was written
- Toggle the workflow to Active
Cost
- n8n cloud: Starts at $24/mo. Each prospect uses ~5-7 executions depending on branch.
- Apollo: 1 credit per people match lookup. Basic plan ($49/mo) = 900 credits.
- Hunter: 1 credit per verification. Starter plan ($49/mo) = 1,000 verifications. Free plan = 25/month.
- Per prospect (typical): 1 Apollo credit + 0-1 Hunter credit = 1-2 credits total. High-confidence Apollo results skip Hunter entirely.
Next steps
- Add company domain resolution — if contacts only have a company name, add a step to find the domain first using Hunter's Domain Search endpoint
- Flag for manual review — route contacts with
riskyemails to a HubSpot list for a human to decide - Add a completion notification — send a Slack summary with counts: found, verified, failed
Need help implementing this?
We build and optimize automation systems for mid-market businesses. Let's discuss the right approach for your team.