Turn a website contact scrape into a HubSpot or Pipedrive import file: one company per website, its inboxes linked
What you get: one CRM import file (a CSV made in Google Sheets or Excel) that HubSpot or Pipedrive can read in one go: one company per website, each public inbox on that company's own domain (info@, sales@, bookings@) as a contact linked to its company, plus the company phone, LinkedIn page, website and the page where each email was found. Emails on another domain, failed sites and addresses that cannot receive mail go to a separate check_first tab instead of your CRM. Setup is about 30 minutes; every later scrape takes two minutes.
The finished import file
| Company name | Company domain name | Website URL | Company phone | LinkedIn company page | Found on | Contact name | |
|---|---|---|---|---|---|---|---|
| example-clinic.in | example-clinic.in | https://example-clinic.in | +914000000001 | appointments@example-clinic.in | …/contact-us | Appointments inbox | |
| example-clinic.in | example-clinic.in | https://example-clinic.in | +914000000001 | info@example-clinic.in | …/ | Info inbox | |
| Example Studio | example-studio.com | https://example-studio.com | linkedin.com/company/example-studio | hello@example-studio.com | …/about | Hello inbox |
Two rows with the same domain become one company with two contacts in the CRM. That is what you want: the company record holds the phone and LinkedIn page, and each inbox sits under it.
Why you cannot import the raw scrape directly
- One row per website, several emails across the row. A CRM wants one contact per row. Apify's CSV export flattens lists into numbered columns (
emails/0/email,emails/1/email…), so the second inbox would be lost or land in the wrong field. - Not every email belongs to the company. A site can show its web designer's or booking service's address. The Unadhi scraper marks each email with
sameDomain(true when it is on the site's own domain) and lists those first. - Dead mailboxes. With
checkEmailMxon, each email getsmxValid; false means its domain cannot receive mail. Importing it only creates a bounce later. - Failed and blocked sites (status other than
ok) have nothing to import.
Step 1: get one contact CSV
Free, a few businesses
For ten or twenty companies, open each contact page and type what it publishes into a sheet with these column names: startUrl, domain, status (type ok), emails/0/email, emails/0/foundOn, emails/0/sameDomain (TRUE), phones/0/phone. Already have a CSV from another tool? Rename its columns to these names and go to Step 2.
No code, hundreds of websites (Apify)
- Create a free Apify account (no card needed for the free plan; see apify.com/pricing for current free monthly usage).
- Open Website Contact Scraper: Emails, Phones & WhatsApp and click Try for free.
- Paste your websites. In JSON view:
{
"urls": ["example-clinic.in", "example-studio.com"],
"maxPagesPerSite": 4,
"includePersonalEmails": false,
"checkEmailMx": true,
"respectRobotsTxt": true,
"respectTermsOfService": true,
"defaultCountry": "IN"
}
Keep includePersonalEmails off: a shared role inbox is the right first contact for a company, and it keeps named people's addresses out of your CRM. Keep checkEmailMx on, because Step 4 uses mxValid. Set defaultCountry to where most of the businesses are, so local phone numbers are read correctly. Starting from a Google Maps dataset? Pass its ID in datasetId (see enrich a Google Maps Apify dataset); the business name then arrives as sourceItems/0/title and becomes the company name.
When the run finishes, open the dataset, choose Export → CSV and download it.
Step 2: import it as text into a tab called raw, and add COL
- Google Sheets: rename the first tab to
raw, then File → Import → Upload, Replace current sheet, and untick "Convert text to numbers, dates, and formulas" so+914000000001keeps its plus sign. - Excel: Data → From Text/CSV → Transform Data, set the
phones/…/phonecolumns to Text, and load into a sheet namedraw.
Then add a helper that picks a column by its header name (column order can change between runs). Google Sheets: Data → Named functions → Add new function, name COL, argument h, definition:
=IFERROR(XLOOKUP(h, raw!$1:$1, raw!$2:$5000), "")
Excel 365: Formulas → Name Manager → New, name COL, refers to:
=LAMBDA(h, IFERROR(XLOOKUP(h, raw!$1:$1, raw!$2:$5000), ""))
Already built the call sheet? It uses the same raw tab and COL, so add the tabs below to that file.
Step 3: one tab per email slot (e0 and e1)
Add a tab called e0. Type the headers in row 1 and each formula in row 2. In Excel 365 you can leave out ARRAYFORMULA; the results spill on their own.
| Col | Header | Formula in row 2 |
|---|---|---|
| A | company | =ARRAYFORMULA(IF(COL("sourceItems/0/title")<>"", COL("sourceItems/0/title"), COL("domain"))) |
| B | domain | =COL("domain") |
| C | website | =COL("startUrl") |
| D | phone | see below |
| E | =COL("socials/linkedin/0/url") | |
| F | =COL("emails/0/email") | |
| G | found on | =COL("emails/0/foundOn") |
| H | status | =COL("status") |
| I | mx | =ARRAYFORMULA(UPPER(COL("emails/0/mxValid")&"")) |
| J | same domain | =ARRAYFORMULA(UPPER(COL("emails/0/sameDomain")&"")) |
D: company phone (a clickable tel: link first, otherwise the first number)
=ARRAYFORMULA(IF(COL("phones/0/source")="tel-link", COL("phones/0/phone"),
IF(COL("phones/1/source")="tel-link", COL("phones/1/phone"), COL("phones/0/phone"))))
Now duplicate the tab, rename the copy e1, and change emails/0/ to emails/1/ in columns F, G, I and J. That picks up each site's second inbox. Sites often publish more; repeat with e2 and emails/2/ if your scrape has those columns.
UPPER(…&"") makes the true/false columns read the same whether your sheet imported them as text or as TRUE/FALSE. If a column does not exist in this run (say no site had a second email), COL returns a blank and the tab simply stays empty.
Step 4: the crm_import tab (stack, check, de-duplicate)
Add a tab called crm_import. Type these headers in row 1, A to H: Company name, Company domain name, Website URL, Company phone, LinkedIn company page, Email, Found on, Contact name. In A2:
=ARRAYFORMULA(LET(all, VSTACK(e0!A2:J5000, e1!A2:J5000),
keep, (CHOOSECOLS(all,6)<>"") * (CHOOSECOLS(all,8)="ok")
* (CHOOSECOLS(all,9)<>"FALSE") * (CHOOSECOLS(all,10)="TRUE"),
UNIQUE(CHOOSECOLS(FILTER(all, keep), 1, 2, 3, 4, 5, 6, 7))))
It keeps a row only when there is an email, the site was read normally, the email's domain can receive mail (blank counts as "not checked" and is kept), and the email is on the company's own domain. UNIQUE drops exact repeats, for example when you merged two runs. Added an e2 tab? Add e2!A2:J5000 inside VSTACK.
In H2, give each inbox a plain contact name (Pipedrive needs a name for every person):
=ARRAYFORMULA(IF(F2:F="", "", PROPER(LEFT(F2:F, FIND("@", F2:F) - 1)) & " inbox"))
That turns sales@example.com into "Sales inbox". It says what the contact really is: a shared company mailbox, not a named person. When you later learn who reads it, rename the contact in the CRM.
Last check: in any empty cell, =ROWS(F2:F) - COUNTBLANK(F2:F) - COUNTUNIQUE(F2:F) (Excel: =ROWS(A2#) - ROWS(UNIQUE(CHOOSECOLS(A2#, 6)))) should be 0. Anything else means one email appears under two companies; look it up and keep the right row.
The check_first tab
Everything left out of the import, with the reason visible in columns H to J:
=ARRAYFORMULA(LET(all, VSTACK(e0!A2:J5000, e1!A2:J5000),
FILTER(all, (CHOOSECOLS(all,6)<>"") * ((CHOOSECOLS(all,8)<>"ok")
+ (CHOOSECOLS(all,9)="FALSE") + (CHOOSECOLS(all,10)<>"TRUE")))))
Open the "found on" page for each one. An email on another domain can be the right address (some firms use a group domain or a free mailbox), or it can be an agency's. Add it by hand only once you are sure.
Step 5: download the CSV
- Google Sheets: open the
crm_importtab, then File → Download → Comma-separated values (.csv). Only the current tab is downloaded. - Excel: copy the
crm_importtab, Paste Values into a new workbook, then File → Save As → CSV UTF-8.
Step 6a: import into HubSpot (contacts and companies, one file)
- Go to Data Management → Data Integration (or click Import on the Contacts page), then Import data → Advanced imports (all objects).
- Select Contacts and Companies, then Single file. For each, choose Create and update.
- Upload the CSV and map the columns:
| Your column | HubSpot object | HubSpot property |
|---|---|---|
| Contact | Email (unique identifier, key icon) | |
| Company domain name | Company | Company domain name (unique identifier) |
| Company name | Company | Company name |
| Website URL | Company | Website URL |
| Company phone | Company | Phone number |
| LinkedIn company page | Company | LinkedIn company page |
| Contact name | Contact | First name |
| Found on | Contact | a single-line text property you create first (for example "Found on"), or don't import |
HubSpot needs Email to create a contact and Name or Company domain name to create a company. Using Email and Company domain name as identifiers means a second import updates the same records instead of creating duplicates. Rows that share a domain become one company with several contacts. Company domain name only keeps the part up to the top-level domain, which is why the file uses the bare domain column, not the full URL.
If the import asks whether to set the new contacts as marketing contacts, leave that off unless you have a lawful basis to send them marketing email.
Step 6b: import into Pipedrive (people and organizations)
- Pipedrive accepts CSV, XLS or XLSX with a header row (one tab per import, up to 200,000 rows and 50 MB). Start an import from a spreadsheet and, at the File info step, select People and Organizations as the data types.
- Map Contact name → Person name, Email → Person email, Company name → Organization name. Map the phone, website and LinkedIn columns to matching fields, or create custom fields first under Personal preferences → Data fields.
- Every person needs a name and every organization needs a name; rows without them land in the skip file. People and the organization in the same row are linked.
If you let Pipedrive merge duplicates, it matches people by name and email, and organizations by name and address. Because the file has no address, two different companies that share a name (two "City Dental" clinics) could merge; when that happens, use the domain as the organization name for one of them.
Other CRMs with a CSV import, such as Zoho CRM, work the same way: map Company name to the account or company, Email to the contact, and use the domain as the de-duplication key if the CRM offers one.
What not to do
- Do not guess personal addresses such as firstname@company. Import what the business published.
- Do not add these contacts to a bulk newsletter or sequence. Write genuine, relevant one-to-one emails, and follow your local rules on direct marketing (for example the EU's GDPR and ePrivacy rules, the UK's PECR, India's DPDP Act, or the US CAN-SPAM Act).
- Keep the Found on column. It shows where each address was published, so you can answer anyone who asks where you got it, and delete the record promptly if they ask you to.
This guide is general information, not legal advice. You are responsible for how you use the data and for following each site's terms and your local law. The Apify Actor mentioned reads only public pages, uses an honest bot User-Agent, follows robots.txt, never logs in and never gets around blocks.
Partner programme opening soon
Partner programme opening soon: join the free waitlist. Planned terms are 50% of each referred sale (75% once you have 5+ paying customers), single level, paid through Gumroad only from revenue received.
Sources
- HubSpot Knowledge Base: Import records for multiple objects and Format import files (required properties, unique identifiers, Company domain name format).
- Pipedrive Knowledge Base: Importing data with spreadsheets and Importing: mandatory fields.
- Field names (
emails/N/email,mxValid,sameDomain,foundOn,phones/N/source,sourceItems) are from the Website Contact Scraper's published input schema and output, 20 Oct 2026.
Related
- Clean an Apify contact scrape into a call sheet (same raw tab; for phone-first outreach)
- Find public role emails (sales@, press@, support@) on a company website
- Enrich a Google Maps Apify dataset with emails and phones
- Copy-paste inputs for the Contact Details and Product scrapers
- Selling to online shops? Related only: Shopify & WooCommerce Product + Price Tracker (also an Unadhi Apify product) lists a shop's public products and prices, so your CRM note can say what they sell.
Unadhi Guides are free, independent explainers. We correct errors as soon as we find them: write to mininglife7@gmail.com.