Playbooks · Skill & signal
An n8n workflow with two owners: the enrichment half is an HTTP Request node hitting RichAPI, and the HubSpot half is n8n's built-in HubSpot node. We have no HubSpot app, so nothing in this workflow writes to your CRM except n8n itself. Costs are scoped per endpoint: email_finder and email_verifier are waterfall and do not bill on a miss, everything else bills on any 2xx.
Last updated September 22, 2026
Pattern: Trigger (schedule, webhook, or a Sheets row) → HTTP Request to /email_finder → IF on success → HTTP Request to /email_verifier → n8n HubSpot node, Contact: Create or Update. Two HTTP nodes you configure once and one CRM node n8n maintains.
Credit math: 500 rows through the enrichment half: email_finder at 5 credits bills only on the rows that produced an address. At a 60% hit rate that is 300 × 5 = 1,500 credits, and the 200 misses cost zero because email_finder is a waterfall endpoint. email_verifier at 2 credits runs on the 300 found addresses for 600 credits. 2,100 credits for 500 rows. The 60% is an illustration; measure your own on 100 rows first. If you add enrich_profile at 1 credit it bills on every 2xx including an empty one, so budget it at 500 × 1, not 300 × 1.
ROI math: The saving here is that the enrichment half is four fields in an HTTP node instead of a connector you wait on a vendor to ship. Against a per-seat enrichment tool, compare seats you would need versus a workflow that runs unattended. Credit tiers on /pricing.
Owned internally by: Whoever already owns the n8n instance. No engineer needed for the HubSpot half, because n8n's node does the OAuth.
Best for: Teams self-hosting n8n who want CRM writes without buying a CRM-native enrichment seat, and anyone whose HubSpot is already connected in n8n.
# The enrichment half is an HTTP Request node. This is the same call,
# as curl, so you can test it before you build the node.
curl -s https://api.richapi.ai/api/v1/email_finder \
-H "x-api-key: $RICHAPI_KEY" \
-H "content-type: application/json" \
-d '{
"first_name": "Dana",
"last_name": "Weiss",
"company_domain": "acme.com",
"company_name": "Acme"
}'
# In the n8n HTTP Request node:
# Method: POST
# URL: https://api.richapi.ai/api/v1/email_finder
# Authentication: Generic Credential Type -> Header Auth
# Header name: x-api-key
# Body: JSON, with expressions: {{ $json.first_name }} etc.
#
# Then an IF node on {{ $json.success }} = true before the verifier node,
# and the HubSpot node after that. The HubSpot node is n8n's, not ours.
Store the key as an n8n Header Auth credential named x-api-key.
Do not paste it into the node body, where it ends up in every exported workflow JSON and every screenshot you send to support.
Build the /email_finder HTTP Request node and pin one real row of sample data so you can see the response shape before wiring anything downstream.
Add an IF node reading {{ $json.success }}.
A miss returns HTTP 200 with success:false, so n8n's own 'continue on fail' setting will not catch it. This IF is the only thing standing between a miss and a blank email written to HubSpot.
Add the /email_verifier HTTP Request node on the true branch only.
Add n8n's HubSpot node, operation Contact: Create or Update, keyed on email.
Authenticate it with n8n's HubSpot OAuth credential. This leg is entirely n8n's; we have no HubSpot app.
Send the false branch somewhere visible, a Sheet or a Slack node, rather than dropping it.
Unfindable rows are a list-quality signal and you want to see the rate.
Run the workflow on 20 rows manually before you put it on a schedule.
n8n loops fast and an unbounded schedule trigger on a 4,000-row source is the usual way people discover their credit balance.
This workflow has two owners and it's worth being clear about which half is ours before you build it. The enrichment half is ours: an HTTP Request node pointed at `https://api.richapi.ai/api/v1/email_finder`, a header, a JSON body. The HubSpot half is n8n's: their HubSpot node, their OAuth credential, their field mapping. **We do not have a HubSpot app.** Nothing in this workflow writes to your CRM on our behalf. If n8n's HubSpot node breaks, that's a conversation with n8n. That's not a hedge, it's the design. See [HubSpot](/integrations/hubspot) for what we do and don't do there, and [n8n](/integrations/n8n) for the node setup in full.
There is an npm package called `n8n-nodes-richapi`, currently v0.1.3. It exists, it works, and someone in the community built it. It is **not verified, not built into n8n, and not maintained by us.** On n8n Cloud, community nodes are restricted; on self-hosted you install it yourself and you own the upgrade path. Use it if you like fewer boxes on a canvas. Use the plain HTTP Request node if you want the thing to still work in six months without checking whether a volunteer is still shipping. We build these pages around the HTTP node for that reason.
Five nodes. Trigger, finder, IF, verifier, HubSpot. The only node with any subtlety is the IF. A miss on `/email_finder` returns **HTTP 200** with `success: false` and `billed: false`. n8n's error handling never sees it, because there is no error. If you skip the IF and map `{{ $json.email }}` straight into the HubSpot node, you will write empty strings over contacts that already had good addresses, and HubSpot will happily accept them. Branch on `success`. That's the whole trick.
`/email_finder` and `/email_verifier` are **waterfall endpoints**: multiple providers run cheapest-first, and if every one of them comes back with nothing, you get a 2xx with `billed: false` and pay zero. That is a narrow claim and it does not extend to the rest of the workflow. If you add `/enrich_profile` at 1 credit to pull a title, it bills on any 2xx, including one that comes back thin. Budget it per row attempted, not per row that worked. Same for `/enrich_company` at 1 credit, which takes a LinkedIn company URL rather than a domain, so you need that URL on the row already. Every waterfall response carries an `execution_log` showing which providers ran on that request. Log it to a Sheet from the false branch; it's the only record of why a row missed and it can't be reconstructed later.
The IF-node bug costs you data quality. This one costs you money. Somebody builds the workflow, tests it on five rows, sets a Schedule Trigger to every 15 minutes, and points it at a Google Sheet that grows. The workflow has no "already enriched" column, so every run re-enriches every row. n8n does not care. It will do this cheerfully until someone notices. Add a `enriched_at` column, filter on it before the finder node, and write it back after the HubSpot node. It's one extra Sheets node and it is the difference between a workflow and a leak. Re-running a row you already resolved is the most common way to spend credits twice, in n8n more than anywhere else, because n8n makes looping so easy.
No sending. The contact lands in HubSpot and your sequencer takes over from there. No dedupe beyond whatever HubSpot's email-keyed upsert does. No intent data, no scoring. And there is no CRM-native app. If what you actually want is a button inside HubSpot, we don't have one, and [HubSpot](/integrations/hubspot) says so plainly.
**Can I use the community node instead of HTTP Request?** Yes, on self-hosted n8n. `n8n-nodes-richapi` v0.1.3 installs like any community node. It is not ours and it is not verified by n8n, so treat it as a convenience, not a dependency. **Does this work on n8n Cloud?** The HTTP Request approach does, unchanged. The community node runs into n8n Cloud's community-node restrictions. **Why not just use n8n's HubSpot node to enrich?** HubSpot's own enrichment is a HubSpot product with HubSpot's pricing. This workflow exists for people who don't want that seat. **Where do I put the key?** An n8n Header Auth credential, name `x-api-key`. Workflow JSON gets exported and shared far more often than people expect. **What if a row has a LinkedIn URL instead of a domain?** Send `linkedin_url` to `/email_finder` instead of name plus domain. Same price, better hit rate.
[n8n](/integrations/n8n) for node-by-node setup, [HubSpot](/integrations/hubspot) for the CRM boundary, [waterfall email then verify](/use-cases/waterfall-email-then-verify) for the billing detail on both legs, [zapier email find on form fill](/use-cases/zapier-email-find-on-form-fill) if you're on Zapier instead, and [use cases](/use-cases) for the rest of the plays. Credit tiers on [pricing](/pricing). **25 free credits, no card.** Enough to run the workflow on a handful of rows and watch a miss cost nothing.