You have a spreadsheet full of website URLs. You want company names, industries, tech stacks, and contact emails — without spending hours on manual research.
Here's how to wire up a fully automated enrichment pipeline in about 20 minutes using n8n and a RapidAPI enrichment endpoint.
What you'll build
A 4-node n8n workflow that:
- Reads a list of website URLs from a Google Sheet
- Calls a company enrichment API for each URL
- Writes back structured data (name, sector, description, contact email, tech stack) into the same sheet
No code. No server. Runs on demand or on a schedule.
Prerequisites
- An n8n cloud account (free trial works)
- A Google account (for Sheets)
- A RapidAPI account subscribed to the AI Live Company Enrichment & Tech Detector API
Step 1 — Set up your Google Sheet
Create a new spreadsheet with these columns in row 1:
website | domain_status | fetched_at | company_name | sector | description | contact_email | tech_stack | linkedin
Add a few website URLs in the website column to test with:
https://stripe.com
https://notion.so
https://linear.app
Note your Spreadsheet ID from the URL — it looks like:
https://docs.google.com/spreadsheets/d/YOUR_SPREADSHEET_ID/edit
Step 2 — Connect credentials in n8n
You need two credentials before building the workflow.
Google Sheets OAuth2
- In n8n, go to Credentials → New → Google Sheets OAuth2
- Follow the OAuth flow to connect your Google account
- Name it
Google Sheets account
RapidAPI Header Auth
- Go to Credentials → New → Header Auth
- Set Name to
x-rapidapi-key(exactly — this is the HTTP header name) - Set Value to your RapidAPI key
- Save as
RapidAPI – Company Enrichment
⚠️ Common mistake: setting the credential "Name" field to a display name like "My RapidAPI Key" instead of the actual header name
x-rapidapi-key. n8n uses this field as the HTTP header name, so it must match exactly.
Step 3 — Build the workflow
Node 1: Manual Trigger
Add a Manual Trigger node. This lets you run the workflow on demand by clicking "Execute workflow." You can swap it for a Schedule trigger later.
Node 2: Google Sheets — Read
Add a Google Sheets node:
- Operation: Read rows
- Document ID: paste your Spreadsheet ID
- Sheet: Sheet1 (tab index 0)
- Credential: Google Sheets account
This node outputs one item per row — each item is a JSON object with your column values.
Node 3: HTTP Request — Enrich
Add an HTTP Request node:
- Method: GET
- URL:
https://ai-live-company-enrichment-tech-detector.p.rapidapi.com/v1/enrich?url={{ $json.website }}
- Authentication: Generic Credential Type → Header Auth
- Credential: RapidAPI – Company Enrichment
-
Additional header:
x-rapidapi-host=ai-live-company-enrichment-tech-detector.p.rapidapi.com
The {{ $json.website }} expression dynamically injects the URL from the previous node for each row. n8n automatically loops this node over all rows from the Sheets node.
Node 4: Google Sheets — Update
Add a second Google Sheets node:
- Operation: Update row
- Document ID: same Spreadsheet ID
- Sheet: Sheet1
-
Matching column:
website(used to find the right row) - Columns to update:
| Column | Value |
|---|---|
website |
{{ $json.url }} |
domain_status |
{{ $json.domain_status }} |
fetched_at |
{{ $json.fetched_at }} |
company_name |
{{ $json.data ? $json.data.company_name : "" }} |
sector |
{{ $json.data ? $json.data.sector : "" }} |
description |
{{ $json.data ? $json.data.description : "" }} |
contact_email |
{{ $json.data ? $json.data.contact_email : "" }} |
tech_stack |
{{ $json.data && Array.isArray($json.data.tech_stack) ? $json.data.tech_stack.join(", ") : "" }} |
linkedin |
{{ $json.data && $json.data.social_links ? $json.data.social_links.linkedin : "" }} |
The
$json.data ?guards handle cases where the API returnsnullfor a field — the cell just stays empty instead of crashing the workflow.
Connect the nodes
Wire them in sequence:
Manual Trigger → Google Sheets (Read) → HTTP Request (Enrich) → Google Sheets (Update)
Step 4 — Run it
Click Execute workflow. Watch the green checkmarks light up node by node.
After it completes, open your Google Sheet — each row should now have company name, sector, description, contact email, and tech stack filled in.
What the API returns
The enrichment endpoint returns a JSON object like this:
{"url":"https://stripe.com","cached":false,"fetched_at":"2026-06-13T12:31:10.566368+00:00","domain_status":"active","data":{"company_name":"Stripe","sector":"Financial Infrastructure and Payments","description":"Stripe provides financial infrastructure enabling businesses to accept payments, offer financial services, and implement custom revenue models globally.","contact_email":null,"tech_stack":["Nginx","Next.js"],"social_links":{"linkedin":"https://www.linkedin.com/company/stripe","twitter":"https://twitter.com/stripe","github":"https://github.com/stripe"}}}
Three fields make this API stand out, and the workflow above captures them:
-
fetched_at— an ISO timestamp of when the site was actually read. Your sheet records exactly how fresh each row is, instead of trusting an invisible database refresh cycle. -
domain_status—active,parked, ordead. Filter your sheet on this column to instantly drop dead or parked domains before they reach your CRM. A dead domain returns a clean200withdata: null, so the workflow never crashes on a bad URL. -
social_links— extracted deterministically from the live page, never guessed. A network that isn't present comes backnull.
For well-known companies the data is usually complete. For smaller or less-indexed sites, some fields may be null — the API returns null rather than inventing a value, which is exactly why the guards in the expressions matter.
Going further
Schedule it: Swap the Manual Trigger for a Schedule Trigger to enrich new rows automatically every night.
Filter only empty rows: Add an IF node between the Read and Enrich nodes to skip rows that already have a company_name, saving API calls.
Drop dead domains automatically: Add an IF node after Enrich that keeps only rows where domain_status is active — parked and dead domains never reach your CRM.
Add error handling: Wrap the HTTP Request in an Error Trigger workflow to catch failed enrichments and log them to a separate sheet.
Export to a CRM: Add a HubSpot or Airtable node after the update step to push enriched companies straight into your sales pipeline.
The workflow JSON
You can import this directly into n8n (Workflows → Import from file). Replace YOUR_GOOGLE_SHEET_ID with your actual spreadsheet ID, then connect your credentials.
{"name":"Company Enrichment Pipeline","nodes":[{"id":"node-001","name":"Start","type":"n8n-nodes-base.manualTrigger","typeVersion":1,"position":[0,0],"parameters":{}},{"id":"node-002","name":"Google Sheets – Read","type":"n8n-nodes-base.googleSheets","typeVersion":4.7,"position":[224,0],"parameters":{"documentId":{"__rl":true,"mode":"id","value":"YOUR_GOOGLE_SHEET_ID"},"sheetName":{"__rl":true,"mode":"id","value":"0"},"options":{}}},{"id":"node-003","name":"HTTP Request – Enrich","type":"n8n-nodes-base.httpRequest","typeVersion":4.2,"position":[448,0],"parameters":{"method":"GET","url":"=https://ai-live-company-enrichment-tech-detector.p.rapidapi.com/v1/enrich?url={{ $json.website }}","authentication":"genericCredentialType","genericAuthType":"httpHeaderAuth","sendHeaders":true,"headerParameters":{"parameters":[{"name":"x-rapidapi-host","value":"ai-live-company-enrichment-tech-detector.p.rapidapi.com"}]},"options":{}},"credentials":{"httpHeaderAuth":{"name":"RapidAPI – Company Enrichment"}}},{"id":"node-004","name":"Google Sheets – Update","type":"n8n-nodes-base.googleSheets","typeVersion":4.7,"position":[672,0],"parameters":{"operation":"update","documentId":{"__rl":true,"mode":"id","value":"YOUR_GOOGLE_SHEET_ID"},"sheetName":{"__rl":true,"mode":"id","value":"0"},"columns":{"mappingMode":"defineBelow","value":{"website":"={{ $json.url }}","domain_status":"={{ $json.domain_status }}","fetched_at":"={{ $json.fetched_at }}","company_name":"={{ $json.data ? $json.data.company_name : '' }}","sector":"={{ $json.data ? $json.data.sector : '' }}","description":"={{ $json.data ? $json.data.description : '' }}","contact_email":"={{ $json.data ? $json.data.contact_email : '' }}","tech_stack":"={{ $json.data && Array.isArray($json.data.tech_stack) ? $json.data.tech_stack.join(', ') : '' }}"},"matchingColumns":["website"]},"options":{}},"credentials":{"googleSheetsOAuth2Api":{"name":"Google Sheets account"}}}],"connections":{"Start":{"main":[[{"node":"Google Sheets – Read","type":"main","index":0}]]},"Google Sheets – Read":{"main":[[{"node":"HTTP Request – Enrich","type":"main","index":0}]]},"HTTP Request – Enrich":{"main":[[{"node":"Google Sheets – Update","type":"main","index":0}]]}},"active":false,"settings":{"executionOrder":"v1"}}
Why I built this
I'm building CDCSaaS — an API for enriching company profiles from a URL. This pipeline is one of the most common use cases: you have a list of leads or prospects as websites, and you need structured data about each one fast.
The n8n workflow makes it accessible to anyone — no Python, no ETL tooling, just a visual canvas and a few credential inputs.
If you try it, let me know in the comments what dataset you used it on. 👇
Part of the "Building CDCSaaS" series — documenting the build of a bootstrapped API product in public.
For further actions, you may consider blocking this person and/or reporting abuse
