Batch enrich HubSpot contacts missing job title or company size using Zapier

medium complexityCost: $20-50/mo

Prerequisites

Prerequisites
  • Zapier account on the Professional plan or higher (required for Webhooks by Zapier, Code by Zapier, and Looping)
  • HubSpot account connected to Zapier via OAuth
  • Apollo API key with enrichment credits

Overview

This Zap runs weekly, searches HubSpot for contacts missing key fields, enriches them via Apollo, and updates HubSpot — writing only to empty fields. Since Zapier doesn't support Apollo natively, you'll use Webhooks by Zapier for all API calls.

Why not use HubSpot's built-in trigger?

HubSpot's "New Contact" trigger in Zapier only fires for newly created contacts. For batch enrichment of existing contacts that are missing data, you need to search via the API using Webhooks by Zapier.

Step 1: Add a weekly Schedule trigger

Create a new Zap. Choose Schedule by Zapier:

  • Trigger event: Every Week
  • Day of the week: Sunday
  • Time of day: 10:00pm
  • Timezone: Your team's timezone

Step 2: Search HubSpot for contacts missing fields

Add a Webhooks by Zapier action:

  • Action event: Custom Request
  • Method: POST
  • URL: https://api.hubapi.com/crm/v3/objects/contacts/search
  • Headers:
    • Authorization: Bearer YOUR_HUBSPOT_TOKEN
    • Content-Type: application/json
  • Data:
{
  "filterGroups": [{
    "filters": [{
      "propertyName": "jobtitle",
      "operator": "NOT_HAS_PROPERTY"
    }]
  }],
  "properties": ["email", "firstname", "lastname", "jobtitle", "company", "phone"],
  "limit": 20
}
Keep the batch small

Zapier charges tasks per loop iteration per step. A 20-contact batch with 4 steps per iteration = 80 tasks. Keep the limit small (10-20) to avoid burning through your monthly task allowance in a single run.

Step 3: Loop through contacts

Add a Looping by Zapier step:

  • Values to loop: Map the results array from the search response

Zapier will iterate through each contact in the array, running the subsequent steps for each one.

Step 4: Call Apollo for enrichment

Inside the loop, add a Webhooks by Zapier action:

  • Method: POST
  • URL: https://api.apollo.io/api/v1/people/match
  • Headers:
    • x-api-key: Your Apollo API key
    • Content-Type: application/json
  • Data:
{
  "email": "{{contact_email}}"
}

Map the email from the current loop iteration.

Step 5: Filter for successful matches

Add a Filter by Zapier step:

  • Field: person from the Apollo response
  • Condition: Exists

This stops processing for contacts Apollo couldn't match.

Step 6: Build the update with Code by Zapier

Add a Code by Zapier step (JavaScript) to only include fields that are currently empty:

const existing = {
  jobtitle: inputData.existing_jobtitle || "",
  company: inputData.existing_company || "",
  phone: inputData.existing_phone || "",
};
 
const apollo = {
  jobtitle: inputData.apollo_title || "",
  company: inputData.apollo_company || "",
  phone: inputData.apollo_phone || "",
};
 
const updates = {};
if (!existing.jobtitle && apollo.jobtitle) updates.jobtitle = apollo.jobtitle;
if (!existing.company && apollo.company) updates.company = apollo.company;
if (!existing.phone && apollo.phone) updates.phone = apollo.phone;
 
return {
  hasUpdates: Object.keys(updates).length > 0 ? "true" : "false",
  jobtitle: updates.jobtitle || "",
  company: updates.company || "",
  phone: updates.phone || "",
};

Set up Input Data to map:

  • existing_jobtitle → contact's current job title from the search
  • existing_company → contact's current company
  • existing_phone → contact's current phone
  • apollo_titleperson__title from Apollo
  • apollo_companyperson__organization__name from Apollo
  • apollo_phoneperson__phone_numbers__sanitized_number from Apollo

Step 7: Filter and update HubSpot

Add a Filter by Zapier step:

  • Field: hasUpdates from the Code step
  • Condition: Exactly matches true

Add a HubSpot action:

  • Action event: Update Contact
  • Contact to update: Map the Contact ID from the loop
  • Job Title: Map jobtitle from the Code step (only if non-empty)
  • Company Name: Map company from the Code step
Empty fields in Zapier mappings

When you map an empty string to a HubSpot field in Zapier, it sends an empty string — which overwrites the existing value. Use the Code step to ensure you only map non-empty values, or use Zapier's built-in "Don't send this field if empty" toggle if available.

Step 8: Test and publish

  1. Test each step to verify the data flow
  2. Run the full Zap manually and check HubSpot for updates
  3. Verify that existing data was not overwritten
  4. Turn the Zap On

Cost and task usage

  • Zapier Professional: $29.99/mo (750 tasks)
  • Per contact: ~4-5 tasks (loop + webhook + filter + code + update). 20 contacts = 80-100 tasks per run.
  • Weekly runs: ~320-400 tasks/month. Fits within Professional plan, but leaves limited headroom.
  • Apollo: 1 credit per enrichment. 20 contacts/week = 80 credits/month.
  • Scale warning: At 50+ contacts/week, you'll likely need the Team plan ($69.99/mo for 2,000 tasks).

Limitations

  • No bulk Apollo endpoint — Zapier calls Apollo individually for each contact (no batch support within a Webhook step)
  • Task-intensive looping — each loop iteration multiplies task usage across all downstream steps
  • No persistent state — Zapier can't remember which contacts were previously enriched without an external store
  • Pagination — the search only returns the first page of results (up to the limit). For more than 100 contacts, you'd need additional Webhook steps with the after cursor.

Next steps

  • Add a Delay step — insert a 1-second Delay by Zapier between loop iterations to respect Apollo's rate limits
  • Track enrichment — add a final Update Contact step to set enrichment_date and enrichment_source properties
  • Add Slack notification — append a Slack step after the loop to summarize the batch results

Need help implementing this?

We build and optimize automation systems for mid-market businesses. Let's discuss the right approach for your team.