Find and verify emails for HubSpot prospects using Apollo and Hunter in n8n

medium complexityCost: $0-24/moRecommended

Prerequisites

Prerequisites
  • n8n instance — n8n cloud or self-hosted
  • HubSpot private app token with crm.objects.contacts.read and crm.objects.contacts.write scopes
  • 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
}
Company domain vs. company name

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 key
    • Content-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 }} equals verified
  • Lower confidence (needs verification): Everything else goes to Hunter
Save Hunter credits

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 use
  • risky — might bounce
  • undeliverable — definitely bad
  • unknown — Hunter couldn't determine

Add an IF node:

  • Condition: {{ $json.data.result }} equals deliverable
Hunter rate limits

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

  1. Click Execute Workflow to run against your prospect list
  2. Check each node — verify Apollo returns emails and Hunter verifies them
  3. Open a few contacts in HubSpot to confirm the email was written
  4. 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 risky emails 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.