Batch enrich HubSpot contacts missing job title or company size using Zapier
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.
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_TOKENContent-Type:application/json
- Data:
{
"filterGroups": [{
"filters": [{
"propertyName": "jobtitle",
"operator": "NOT_HAS_PROPERTY"
}]
}],
"properties": ["email", "firstname", "lastname", "jobtitle", "company", "phone"],
"limit": 20
}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
resultsarray 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 keyContent-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:
personfrom 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 searchexisting_company→ contact's current companyexisting_phone→ contact's current phoneapollo_title→person__titlefrom Apolloapollo_company→person__organization__namefrom Apolloapollo_phone→person__phone_numbers__sanitized_numberfrom Apollo
Step 7: Filter and update HubSpot
Add a Filter by Zapier step:
- Field:
hasUpdatesfrom 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
jobtitlefrom the Code step (only if non-empty) - Company Name: Map
companyfrom the Code step
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
- Test each step to verify the data flow
- Run the full Zap manually and check HubSpot for updates
- Verify that existing data was not overwritten
- 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
aftercursor.
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_dateandenrichment_sourceproperties - 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.