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

Why Make?

Make's Iterator and ifempty() function handle the "only fill empty fields" logic naturally. The visual scenario layout makes it easy to see the search → enrich → filter → update flow, and the Code module (Core plan) gives you precise control over field mapping when ifempty() isn't enough.

The trade-off is credit consumption. Each contact passes through 4-5 modules, and every module execution counts as a credit. For 50 contacts, that's 200-250 credits per run. The Free plan (1,000 credits/month) handles about 4-5 weekly runs of 50 contacts. For larger batches, the Code approach processes the same volume with zero platform 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

Troubleshooting

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.

Common questions

How many Make credits does each contact use?

4-5 credits per contact (Iterator + Apollo HTTP + Filter + Code/Set Variables + HubSpot PATCH). A batch of 50 contacts costs 200-250 credits. The Free plan (1,000 credits/month) handles about 200-250 contacts/month across all runs.

Can I use this on Make's Free plan?

Yes, but with limitations. The Free plan includes 1,000 credits/month and a 15-minute polling interval. For weekly batch enrichment of 50 contacts, you'd use about 200-250 credits per run, fitting 4 weekly runs within the limit. The Code module requires the Core plan ($29/mo) — use ifempty() with Set Multiple Variables on Free.

Why does the scenario process the same contacts every week?

If Apollo can't find a job title for a contact, that contact still matches the NOT_HAS_PROPERTY filter and appears in the next run. Set an enrichment_date property for every processed contact (even with no Apollo match) and add it as an exclusion filter to prevent repeat processing.

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"

Looking to scale your AI operations?

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