Apollo + LinkedIn to HubSpot, richer lead profiles
You finally get a demo request. Then the scramble starts: “Who are they?”, “What’s their role?”, “Is this the right company?”, “Do we already have them in HubSpot?” That frantic, manual enrichment loop quietly steals your best selling hours.
SDRs feel it first, because speed matters. RevOps gets the cleanup bill when fields are inconsistent and duplicates multiply. And if you’re a marketer building segmented audiences, HubSpot lead enrichment automation is the difference between “spray and pray” and precise targeting.
This workflow takes a plain name + email, finds the right person in Apollo, pulls recent LinkedIn context, summarizes it with AI, and upserts a clean record into HubSpot. You’ll see how it works, what you need, and where teams usually trip up.
How This Automation Works
The full n8n workflow, from trigger to final output:
n8n Workflow Template: Apollo + LinkedIn to HubSpot, richer lead profiles
flowchart LR
subgraph sg0["When Executed by Another Workflow Flow"]
direction LR
n0@{ icon: "mdi:play-circle", form: "rounded", label: "When Executed by Another Wor..", pos: "b", h: 48 }
n1["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/httprequest.dark.svg' width='40' height='40' /></div><br/>Enrich with Apollo"]
n2@{ icon: "mdi:swap-horizontal", form: "rounded", label: "Found?", pos: "b", h: 48 }
n3["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/hubspot.svg' width='40' height='40' /></div><br/>Enrich in HubSpot1"]
n4@{ icon: "mdi:robot", form: "rounded", label: "Auto-fixing Output Parser2", pos: "b", h: 48 }
n5@{ icon: "mdi:robot", form: "rounded", label: "Structured Output Parser2", pos: "b", h: 48 }
n6@{ icon: "mdi:brain", form: "rounded", label: "OpenAI Chat Model5", pos: "b", h: 48 }
n7@{ icon: "mdi:robot", form: "rounded", label: "Enrichment summary agent", pos: "b", h: 48 }
n8["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/merge.svg' width='40' height='40' /></div><br/>Merge"]
n9@{ icon: "mdi:brain", form: "rounded", label: "OpenAI Chat Model6", pos: "b", h: 48 }
n10["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/httprequest.dark.svg' width='40' height='40' /></div><br/>Get recent posts"]
n11@{ icon: "mdi:swap-vertical", form: "rounded", label: "Split Out", pos: "b", h: 48 }
n12@{ icon: "mdi:swap-horizontal", form: "rounded", label: "Filter out reshares and old ..", pos: "b", h: 48 }
n13@{ icon: "mdi:cog", form: "rounded", label: "Limit to 3", pos: "b", h: 48 }
n14@{ icon: "mdi:cog", form: "rounded", label: "Sort by post date", pos: "b", h: 48 }
n15@{ icon: "mdi:cog", form: "rounded", label: "Aggregate", pos: "b", h: 48 }
n16@{ icon: "mdi:swap-vertical", form: "rounded", label: "Loop Over Items", pos: "b", h: 48 }
n17@{ icon: "mdi:swap-vertical", form: "rounded", label: "Extract fields", pos: "b", h: 48 }
n18["<div style='background:#f5f5f5;padding:10px;border-radius:8px;display:inline-block;border:1px solid #e0e0e0'><img src='https://flowpast.com/wp-content/uploads/n8n-workflow-icons/code.svg' width='40' height='40' /></div><br/>clean"]
n8 --> n17
n18 --> n1
n2 --> n8
n2 --> n16
n15 --> n16
n11 --> n12
n13 --> n15
n17 --> n7
n16 --> n8
n16 --> n10
n10 --> n11
n14 --> n13
n1 --> n2
n6 -.-> n4
n9 -.-> n7
n7 --> n3
n5 -.-> n4
n4 -.-> n7
n12 --> n14
n0 --> n18
end
%% Styling
classDef trigger fill:#e8f5e9,stroke:#388e3c,stroke-width:2px
classDef ai fill:#e3f2fd,stroke:#1976d2,stroke-width:2px
classDef aiModel fill:#e8eaf6,stroke:#3f51b5,stroke-width:2px
classDef decision fill:#fff8e1,stroke:#f9a825,stroke-width:2px
classDef database fill:#fce4ec,stroke:#c2185b,stroke-width:2px
classDef api fill:#fff3e0,stroke:#e65100,stroke-width:2px
classDef code fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px
classDef disabled stroke-dasharray: 5 5,opacity: 0.5
class n0 trigger
class n4,n5,n7 ai
class n6,n9 aiModel
class n2,n12 decision
class n1,n10 api
class n18 code
classDef customIcon fill:none,stroke:none
class n1,n3,n8,n10,n18 customIcon
The Problem: HubSpot Contacts Are “Technically There,” But Not Usable
A name and an email address looks like a lead, but it’s not enough to route, personalize, or even qualify. Someone ends up bouncing between Apollo, LinkedIn, and HubSpot, trying to confirm job title, location, seniority, and “what they’ve been talking about lately.” And because it’s manual, you get inconsistencies: one rep writes “VP Marketing,” another uses “VPM,” and a third leaves it blank. Worse, duplicates creep in when emails are formatted differently or someone uses a secondary address.
It adds up fast. Here’s where it breaks down in real pipelines.
- A simple inbound demo request can take 15–20 minutes to enrich before anyone even responds.
- Different people enrich the same fields differently, which makes segmentation unreliable.
- Duplicates slip into HubSpot when emails aren’t normalized and checked consistently.
- “Context” lives in tabs and memory instead of in the CRM, so handoffs get sloppy.
The Solution: Apollo + LinkedIn Context, Cleanly Upserted Into HubSpot
This automation starts with the only two fields most inbound leads reliably provide: name and email. First, it normalizes and deduplicates the email so HubSpot updates the right record instead of creating a near-identical copy. Then it calls Apollo’s people match endpoint to find the best-fit profile, and it stops early if there’s no confident match (so you don’t pollute your CRM with guessed data). If a match exists, the workflow pulls the person’s recent LinkedIn activity (up to three original posts from the last 30 days) via RapidAPI. Finally, an AI summary chain turns all of that into strict, structured JSON, and n8n maps those fields into HubSpot with an upsert, including custom properties you define.
In practice, the workflow triggers when another workflow passes a lead into it. Apollo provides the “who,” LinkedIn provides the “what they care about right now,” and OpenAI turns that raw input into CRM-ready fields your team can actually use. HubSpot ends up cleaner, richer, and easier to route.
What You Get: Automation vs. Results
| What This Workflow Automates | Results You’ll Get |
|---|---|
|
|
Example: What This Looks Like
Say you handle 20 inbound demo requests a week. Manually, you might spend about 10 minutes in Apollo, another 10 minutes skimming LinkedIn, plus a few minutes copying fields into HubSpot, so roughly 25 minutes per lead (around 8 hours weekly). With this workflow, the “work” is basically sending name + email into the trigger, then waiting a couple minutes for enrichment and AI summarization. You get most of that day back, and you’re not guessing on job titles at 5:30 pm.
What You’ll Need
- n8n instance (try n8n Cloud free)
- Self-hosting option if you prefer (Hostinger works well)
- Apollo.io for matching and enrichment data.
- RapidAPI (Fresh-LinkedIn-Profile-Data) to fetch recent LinkedIn posts.
- OpenAI API key (get it from your OpenAI dashboard).
Skill level: Intermediate. You’ll connect a few accounts, add API keys, and confirm HubSpot properties match the fields being written.
Don’t want to set this up yourself? Talk to an automation expert (free 15-minute consultation).
How It Works
A lead gets passed in. This workflow is designed as a subworkflow, so it runs when another automation sends a name and email (for example, from a form, chatbot, or a routing intake flow).
The record is cleaned before enrichment. A code step normalizes the email and removes obvious duplicates, so downstream systems treat the contact consistently. This is small, but it prevents a lot of HubSpot mess.
Apollo and LinkedIn context are pulled and combined. Apollo’s people match lookup runs first, then an “exists” check decides if the workflow should continue. If there is a match, n8n fetches recent LinkedIn posts, filters down to original content from the last 30 days, sorts by date, and keeps the top three.
AI turns raw signals into CRM-ready fields. The LangChain AI agent plus structured and auto-fixing parsers produce a strict JSON payload (job title, location, summaries, and more). Then HubSpot gets an upsert that maps every key into the contact record, including custom properties.
You can easily modify the fields you write into HubSpot to match your lifecycle stages, routing logic, or account-based marketing tags. See the full implementation guide below for customization options.
Step-by-Step Implementation Guide
Step 1: Configure the Subworkflow Trigger Start Trigger
Set up the subworkflow trigger to accept the incoming contact payload fields used throughout the enrichment process.
- Add the Subworkflow Trigger Start node as your trigger.
- In Workflow Inputs, add the fields name and email exactly as defined.
- Connect Subworkflow Trigger Start to Normalize Contact Records.
name and email from the parent workflow to keep the schema consistent.Step 2: Connect Apollo and LinkedIn Data Sources
Configure the enrichment API calls used to look up profiles and recent posts.
- In Apollo Profile Lookup, set URL to
https://api.apollo.io/api/v1/people/matchand Method toPOST. - Under Query Parameters, set reveal_personal_emails to
false, reveal_phone_number tofalse, and email to{{ $json.email }}. - Under Header Parameters in Apollo Profile Lookup, set x-api-key to your Apollo key and keep accept as
application/json. - In Fetch Recent Posts, set URL to
https://fresh-linkedin-profile-data.p.rapidapi.com/get-profile-posts. - Set Query Parameters in Fetch Recent Posts to linkedin_url =
{{ $json.person.linkedin_url }}and type =posts. - Set Header Parameters in Fetch Recent Posts to x-rapidapi-host =
fresh-linkedin-profile-data.p.rapidapi.comand x-rapidapi-key = your RapidAPI key.
[CONFIGURE_YOUR_API_KEY] will cause silent failures.Step 3: Set Up Data Processing and Post Selection
Normalize contacts, branch on lookup success, and prepare recent activity for summarization.
- In Normalize Contact Records, keep the provided JavaScript to deduplicate by email and normalize casing.
- Configure Result Exists Check with the condition
{{ $json.person && ($json.person.name || $json.person.email) }}to gate enrichment. - Note the parallel flow: Result Exists Check outputs to both Combine Streams and Iterate Records in parallel.
- In Separate Post Items, set Field to Split Out to
data. - In Filter Recent Originals, keep conditions
{{ $json.reshared }}is false and{{ $json.posted }}after{{$now.minus({ days: 30 }).toISO()}}. - In Order by Post Date, set Sort Field to
postedand Order todescending. - In Restrict to Top 3, set Max Items to
3, then use Bundle Post Data with Aggregate =aggregateAllItemData. - In Combine Streams, keep Mode =
combineand Combine By =combineByPosition. - In Map Enrichment Fields, set person_data to
{{ $json.person }}and recent_activity to{{ $json.data }}.
Step 4: Configure the AI Summarization Stack
Wire the AI model, parsing, and output repair to generate clean CRM-ready JSON.
- In CRM Summary Agent, set Text to
Here’s the person data to enrich for CRM: {{ JSON.stringify($json, null, 2) }} Here's the recent activity to summarize for the CRM: {{ $json.recent_activity }}. - Keep CRM Summary Agent set to Prompt Type =
defineand Has Output Parser enabled. - Connect OpenAI Chat Engine A as the language model for CRM Summary Agent; set Model to
o3and Response Format tojson_object. - In Structured Parser, keep the provided JSON schema example for fields like
email,linkedin_url, andrecent_linkedin_activity. - Route Structured Parser into Auto Repair Parser for auto-fixing malformed outputs.
- Connect OpenAI Chat Engine B to Auto Repair Parser with Model set to
gpt-4o-mini.
Credential Required: Connect your openAiApi credentials in OpenAI Chat Engine A.
Credential Required: Connect your openAiApi credentials in OpenAI Chat Engine B.
Step 5: Configure HubSpot Output
Map the AI-enriched fields into your HubSpot contact record update.
- In HubSpot Contact Update, set Authentication to
oAuth2. - Set Email to
{{ $json.output.email }}. - Map City to
{{ $json.output.city }}, Country to{{ $json.output.country_region }}, and Job Title to{{ $json.output.job_title }}. - Under Custom Properties, map linkedin_account to
{{ $json.output.linkedin_url }}, experience_summary to{{ $json.output.experience_summary }}, education_summary to{{ $json.output.education_summary }}, and recent_linkedin_posts to{{ $json.output.recent_linkedin_activity }}. - Set last_enrichment_date to
{{ Math.floor(Date.now() / 86400000) * 86400000 }}.
Credential Required: Connect your hubspotOAuth2Api credentials.
Step 6: Test and Activate Your Workflow
Validate each stage to ensure enrichment, summarization, and CRM updates work end to end.
- Manually execute Subworkflow Trigger Start with sample
nameandemailinput. - Confirm that Result Exists Check returns a true path and that Result Exists Check outputs to both Combine Streams and Iterate Records in parallel.
- Verify CRM Summary Agent outputs a valid JSON object and that HubSpot Contact Update completes successfully.
- Check HubSpot to confirm the contact has updated fields including recent_linkedin_posts and last_enrichment_date.
- Toggle the workflow to Active once the test run succeeds.
Common Gotchas
- HubSpot OAuth credentials can expire or lack scopes. If updates fail, check the n8n HubSpot credential status and confirm the connected user can edit contacts and custom properties.
- If you’re using Wait nodes or external rendering, processing times vary. Bump up the wait duration if downstream nodes fail on empty responses.
- Default prompts in AI nodes are generic. Add your brand voice early or you’ll be editing outputs forever.
Frequently Asked Questions
About 45 minutes if you already have the API keys and HubSpot properties ready.
No. You’ll mostly connect accounts and paste API keys into n8n.
Yes. n8n has a free self-hosted option and a free trial on n8n Cloud. Cloud plans start at $20/month for higher volume. You’ll also need to factor in Apollo, RapidAPI, and OpenAI usage (often a few dollars a month at low volume).
Two options: n8n Cloud (managed, easiest setup) or self-hosting on a VPS. For self-hosting, Hostinger VPS is affordable and handles n8n well. Self-hosting gives you unlimited executions but requires basic server management.
Yes, and you probably should. You can change what gets written in the “Map Enrichment Fields” step, then update the “HubSpot Contact Update” mapping to match your contact properties. Common tweaks include adding lifecycle stage hints, writing a “personalization snippet” field for SDRs, and storing the top LinkedIn themes as tags for segmentation. If your team has multiple pipelines, you can also branch on domain or company size before the upsert.
Usually it’s an API key issue or a missing header. Confirm the Apollo key is set as x-api-key in the “Apollo Profile Lookup” HTTP request, then check n8n’s execution logs for a 401/403 response. If you’re getting rate limited, slow down the “Iterate Records” batching and avoid enriching large lists all at once. Also make sure the email you’re matching is normalized, because garbage-in can look like “Apollo is broken.”
On n8n Cloud Starter, you can run a few thousand workflow executions per month, which is plenty for most small teams doing inbound enrichment. If you self-host, there’s no n8n execution cap; your limits become Apollo/RapidAPI quotas and your server size. Practically, this workflow is best run continuously (as leads arrive) rather than in giant backfills, because LinkedIn fetches and AI summarization add latency per record. If you do need a backfill, batching keeps it stable.
Often, yes, because this flow relies on conditional logic, batching, and structured AI parsing that gets awkward (and pricey) in simpler automation tools. n8n also gives you the option to self-host, which is a big deal if you want high volume without a per-task bill. Zapier or Make can still work if you only need “Apollo to HubSpot” with minimal transformation and no LinkedIn context. The moment you care about deduping, strict JSON outputs, and retries, n8n is just more comfortable. If you want a sanity check for your exact stack, Talk to an automation expert.
Clean data changes the pace of your whole funnel. Set this up once, and your team can finally trust what’s in HubSpot.
Need Help Setting This Up?
Our automation experts can build and customize this workflow for your specific needs. Free 15-minute consultation—no commitment required.