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

medium complexityCost: $10-29/mo

Prerequisites

Prerequisites
  • Make account (Free plan works for small batches; Core plan recommended for larger volumes)
  • HubSpot connection configured in Make via OAuth
  • Apollo API key with enrichment credits

Step 1: Create the scenario and schedule it weekly

Create a new scenario. Set the schedule:

  • Schedule type: At regular intervals
  • Run scenario: Every week
  • Day: Sunday
  • Time: 22:00
  • Timezone: Your team's timezone

Step 2: Search for contacts missing fields

Add an HTTP module (Make an API Call):

  • URL: https://api.hubapi.com/crm/v3/objects/contacts/search
  • Method: POST
  • Headers: Authorization handled by HubSpot connection
  • Body:
{
  "filterGroups": [{
    "filters": [{
      "propertyName": "jobtitle",
      "operator": "NOT_HAS_PROPERTY"
    }]
  }],
  "properties": ["email", "firstname", "lastname", "jobtitle", "company", "phone", "linkedin_url", "industry"],
  "limit": 100
}
Combine missing field searches

To find contacts missing any critical field, use separate filterGroups. Each group is ORed together. This finds contacts where title OR company is empty, catching more gaps in a single search.

Step 3: Iterate over contacts

Add an Iterator module:

  • Array: {{1.results}}

Each contact is emitted as a separate bundle.

Step 4: Call Apollo for enrichment

Add an HTTP module:

  • URL: https://api.apollo.io/api/v1/people/match
  • Method: POST
  • Headers: x-api-key: YOUR_APOLLO_KEY, Content-Type: application/json
  • Body:
{
  "email": "{{2.properties.email}}"
}

Add a Filter on the connection after Apollo:

  • Condition: person exists
Apollo returns null for no-match

Apollo returns {"person": null} when it can't find a match — not an error. Without the filter, downstream modules would try to read properties from null and fail. Always filter on person existence.

Step 5: Build the update payload (only empty fields)

Add a Code module (Core plan required) to build a properties object that only includes fields currently missing from the contact:

const contact = input.iteratorBundle; // from Iterator
const person = input.person; // from Apollo
 
const properties = {};
 
if (!contact.properties.jobtitle && person.title) {
  properties.jobtitle = person.title;
}
if (!contact.properties.company && person.organization?.name) {
  properties.company = person.organization.name;
}
if (!contact.properties.phone && person.phone_numbers?.[0]?.sanitized_number) {
  properties.phone = person.phone_numbers[0].sanitized_number;
}
 
return {
  contactId: contact.id,
  properties,
  hasUpdates: Object.keys(properties).length > 0,
};

Alternatively, on the Free plan, use Set Multiple Variables with ifempty():

VariableValue
jobtitle{{ifempty(2.properties.jobtitle; 3.person.title)}}
company{{ifempty(2.properties.company; 3.person.organization.name)}}
phone{{ifempty(2.properties.phone; 3.person.phone_numbers[1].sanitized_number)}}
ifempty() preserves existing data

Make's ifempty(a; b) returns a if it has a value, or b if a is empty. This means existing HubSpot data is preserved — Apollo only fills gaps.

Step 6: Filter and update HubSpot

Add a Filter:

  • Condition: hasUpdates equals true (or check that at least one variable is non-empty)

Add an HTTP module:

  • Method: PATCH
  • URL: https://api.hubapi.com/crm/v3/objects/contacts/{{4.contactId}}
  • Headers: Authorization via HubSpot connection
  • Body:
{
  "properties": {
    "jobtitle": "{{4.properties.jobtitle}}",
    "company": "{{4.properties.company}}",
    "phone": "{{4.properties.phone}}",
    "enrichment_date": "{{formatDate(now; 'YYYY-MM-DD')}}",
    "enrichment_source": "apollo"
  }
}

Step 7: Add rate limiting and error handling

  1. Add a Sleep module (500ms) after the Apollo call within the Iterator loop
  2. Add a Resume error handler on the Apollo HTTP module (handles 429 rate limits)
  3. Add a Break error handler on the HubSpot PATCH module for manual retry of failed writes
  4. Click Run once to test
  5. Toggle the scenario to Active

Cost and credits

  • Make Free plan: 1,000 credits/month. Each contact uses ~4-5 credits. Handles ~200-250 contacts/month.
  • Make Core plan: $29/mo for 10,000 credits. Handles ~2,000-2,500 contacts/month.
  • Apollo: 1 credit per person enrichment. Basic plan ($49/mo) = 900 credits. At 50 contacts/week, that's 200 credits/month.
  • Weekly batch of 50 contacts: ~200-250 Make credits + 50 Apollo credits.
Iterator credit multiplication

Each bundle from the Iterator passes through every downstream module, and each module execution counts as 1 credit. For 50 contacts with 4 downstream modules, that's 200 credits per run. Monitor credit usage in Make's dashboard.

Next steps

  • Handle pagination — add a loop for the HubSpot search to process all pages, not just the first 100 results
  • Add a Data Store — log enrichment results to Make's Data Store for audit and reporting
  • Multi-field search — expand the search filters to catch contacts missing company, phone, or LinkedIn in addition to job title
  • Add Slack summary — append a Slack module to post batch results: "Enriched 42/50 contacts this week"

Need help implementing this?

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