← Blog

How to enrich your CRM data with Claude Code

CRM11 min readMay 2026

Your CRM is full of bad data. Not because anyone is doing a bad job. Because data decays. People change jobs. Companies get acquired. Email addresses go stale. Phone numbers get reassigned. Industry research from HubSpot shows that 30% of email addresses become invalid every year. That means roughly a third of your CRM contacts have bad emails right now.

Bad CRM data is not just a data quality issue. It is a revenue issue. Your reps are emailing dead addresses. Your sequences are bouncing. Your sender reputation is eroding. Your marketing campaigns report inflated list sizes with deflated engagement rates. Every department that touches customer data suffers when the CRM is dirty.

This guide shows how to use Claude Code and gtmcli to clean and enrich your entire CRM. Export your contacts. Validate every email. Find replacement addresses for the invalid ones. Fill in emails for contacts that have none. Write everything back. We will walk through the full process with real commands and real cost math.

The scope of the problem

Most CRMs have three categories of bad contact data. Understanding each one matters because the fix is different for each.

Invalid emails

These are addresses that were valid when they entered the CRM but have since become undeliverable. The person left the company. The domain expired. The mailbox was deleted. Sending to these addresses causes hard bounces that damage your sender reputation. In a typical CRM that has not been cleaned in 12 months, 20-30% of emails fall into this category.

Missing emails

Contacts that exist in your CRM with a name and company but no email address. These are often imported from LinkedIn, conference scans, or manual entries where the email was never captured. They sit in your CRM doing nothing. In many databases, 10-15% of contacts have no email at all.

Stale enrichment data

The email might be valid, but everything else is wrong. The person's title changed. The company's employee count doubled. The industry classification is outdated. Stale enrichment data causes misrouting, wrong personalization, and inaccurate reporting. It makes your ICP filters unreliable.

Setup: what you need

This workflow uses Claude Code running locally in your terminal with gtmcli configured as an MCP server. You also need API access to your CRM.

# Install gtmcli
npm install -g gtmcli

# Set your API key
export GTMCLI_API_KEY=your_key_here

# Set your CRM API key (HubSpot example)
export HUBSPOT_API_KEY=your_hubspot_key_here

# Configure gtmcli as MCP server in .mcp.json
{
  "mcpServers": {
    "gtmcli": {
      "command": "npx",
      "args": ["gtmcli", "mcp"],
      "env": {
        "GTMCLI_API_KEY": "your_key_here"
      }
    }
  }
}

Step 1: Export contacts from your CRM

Start by pulling your contact data. You can export a CSV manually from HubSpot, or have Claude Code pull contacts directly via the API.

Prompt to Claude Code:

"Pull all contacts from HubSpot using the CRM API.
Include these properties: email, firstname, lastname,
jobtitle, company, website, hs_object_id.
Write them to crm_export.csv.
Use HUBSPOT_API_KEY from env. Handle pagination."

Claude Code will write a script that handles HubSpot's pagination (100 contacts per page), pulls every contact, and assembles a single CSV. For a 10,000 contact CRM, this takes about 2 minutes.

Agent output:

Exported 10,247 contacts to crm_export.csv
  With email:    8,891 (86.8%)
  Without email: 1,356 (13.2%)

Step 2: Validate every email

Take the 8,891 contacts that have emails and validate every one.

Prompt to Claude Code:

"Read crm_export.csv. For every row that has an email,
validate it using gtmcli validate. Add columns:
validation_status, catch_all, disposable, role.
Write results to crm_validated.csv.

Categorize each contact:
- 'keep' if status is valid or valid_catchall
- 'replace' if status is invalid
- 'risky' if status is catch_all (unconfirmed)
- 'remove' if status is disposable

Print a summary when done."
Agent output:

Validated 8,891 emails in 28m 14s.

Summary:
  keep:       6,234 (70.1%) — valid emails, safe to send
  replace:    1,867 (21.0%) — invalid, need new address
  risky:        612 (6.9%)  — catch-all, monitor closely
  remove:       178 (2.0%)  — disposable, suppress

21% invalid. That is typical for a CRM that has not been validated in a year. Those 1,867 contacts have been receiving bounces every time you email them. If even 10% of your active sequences included these addresses, your bounce rate was well above the 2% threshold that triggers reputation damage.

Step 3: Find replacement emails for invalid contacts

The 1,867 invalid contacts are not dead leads. The person likely still works somewhere. They just changed email addresses or companies. Use gtmcli to find their current email.

Prompt to Claude Code:

"Read crm_validated.csv. Filter to rows where
category is 'replace'. For each contact, use gtmcli
find-email with their name and company domain to find
a new email address. If found, validate the new email
immediately. Only keep results where the new email
is valid or valid_catchall.

Write results to replacements.csv with columns:
hs_object_id, old_email, new_email, new_status.

Print how many replacements were found."
Agent output:

Searched for replacements for 1,867 invalid contacts.

Found new valid email:  1,024 (54.8%)
No email found:           843 (45.2%)

replacements.csv written with 1,024 rows.

Over half the invalid contacts got a fresh, validated email address. These are contacts that were essentially dead in your CRM. Now they are reachable again. The other 843 might have left the company entirely. You can try again with a broader search or mark them for review.

Step 4: Find emails for contacts missing them

Remember the 1,356 contacts with no email? Let us fix those too.

Prompt to Claude Code:

"Read crm_export.csv. Filter to rows where email is
empty. For each contact that has a name and company
domain (website column), use gtmcli find-email to
find their email. Validate any found email immediately.
Only keep valid or valid_catchall results.

Write results to new_emails.csv with columns:
hs_object_id, firstname, lastname, company, new_email,
validation_status."
Agent output:

Processed 1,356 contacts without emails.

Had enough data to search: 1,189
Found valid email:           748 (62.9%)
No email found:              441 (37.1%)
Skipped (no name or domain): 167

new_emails.csv written with 748 rows.

748 contacts that were sitting in your CRM with no way to reach them now have validated email addresses. That is 748 new contacts you can add to sequences, campaigns, and nurture tracks.

Step 5: Write results back to your CRM

Now push the clean data back into HubSpot. Three operations: update invalid emails with replacements, add emails to contacts that had none, and suppress disposable addresses.

Prompt to Claude Code:

"Update HubSpot contacts with enrichment results:

1. Read replacements.csv. For each row, update the
   HubSpot contact (by hs_object_id) with the new_email
   in the email property. Save old_email to a custom
   property called 'previous_email'.

2. Read new_emails.csv. For each row, update the
   HubSpot contact with the new_email in the email
   property.

3. Read crm_validated.csv. For rows where category
   is 'remove' (disposable), add the contact to a
   static list called 'Suppressed - Disposable'.

Use HUBSPOT_API_KEY from env. Handle rate limits.
Log every update to enrichment_log.csv."
Agent output:

CRM update complete.

Emails replaced:           1,024 contacts
New emails added:            748 contacts
Disposable suppressed:       178 contacts

Total contacts improved:   1,950
Log written to enrichment_log.csv

1,950 contacts fixed in a single run. The old emails are preserved in a custom property so you have an audit trail. The disposable addresses are suppressed so they never accidentally get emailed.

Credit cost math

Let us break down the actual cost of this enrichment run and project it across CRM sizes.

10,000 contact CRM (the example above)

Validate existing emails: 8,891 credits

Find replacement emails: 1,867 credits

Validate replacements: 1,024 credits

Find missing emails: 1,189 credits

Validate new finds: 748 credits

──────────────────────────────────────

Total credits: 13,719

Cost at $0.01/credit: $137.19

$137 to validate, replace, and fill your entire 10,000 contact CRM. That is $0.014 per contact for a complete data quality pass. Compare that to hiring a VA to do it manually ($2,000+) or paying a data vendor for a one-time clean ($500-1,500).

5,000 contact CRM

Validate existing: ~4,350 credits

Find + validate new: ~2,500 credits

──────────────────────────────────────

Total credits: ~6,850

Cost: ~$68.50

50,000 contact CRM

Validate existing: ~43,500 credits

Find + validate new: ~25,000 credits

──────────────────────────────────────

Total credits: ~68,500

Cost: ~$685.00

Even at 50,000 contacts, the total cost is under $700. That is less than one month of most data vendor subscriptions. And you get actual validation results, not just a confidence score.

Making it ongoing: monthly CRM hygiene

A one-time clean is valuable but not enough. Data decays continuously. Set up a monthly enrichment run to catch new invalids before they cause bounces.

Prompt for monthly maintenance run:

"Pull all HubSpot contacts modified in the last 30 days
or that haven't been validated in 30+ days. Validate
every email. For any that flipped to invalid, find a
replacement email. Update HubSpot with results.
Add a custom property 'last_validated' with today's
date. Print a summary."

Monthly runs are cheaper because you only validate contacts that are new or stale. For a 10,000 contact CRM, the monthly run typically validates 1,000-2,000 contacts. That is $10-$20 per month to keep your entire CRM clean.

Save the prompt. Run it on the first of every month. The process takes about 10 minutes of agent execution time. You review the summary, confirm nothing looks unusual, and move on. Your CRM stays clean without manual work.

The ROI of clean CRM data

Clean data produces measurable improvements across every GTM function.

Outbound sequences see higher reply rates because emails actually reach inboxes. When your bounce rate drops from 8% to under 1%, your sender reputation recovers and deliverability improves for all your email, not just the cleaned contacts.

Marketing campaigns report accurate engagement metrics. When you remove 2,000 invalid emails from a 10,000 person list, your open rate jumps because the denominator is now real contacts, not ghosts.

Sales reps stop wasting time on dead contacts. Every minute spent crafting a message to an invalid email is a minute not spent on a live prospect.

Revenue operations gets reliable data for forecasting and territory planning. When your CRM accurately reflects your reachable market, every decision built on that data improves.

The $137 spent to clean a 10,000 contact CRM pays for itself if it prevents even one domain blacklisting event. Domain recovery takes 2-4 weeks. During that time, your entire outbound program is dead. The cost of that downtime dwarfs the cost of validation.

Try gtmcli

100 free credits. No credit card.