Clean an Apify contact scrape into a call sheet: one row per business, best email and phone first
What you get: one Google Sheet (or Excel workbook) called a call sheet: one row per company, with the best email, the best phone, a WhatsApp link if the business publishes one, the website, the page where the contact was found, and a simple score so the most reachable businesses sit at the top. You paste a raw contact CSV into one tab; formulas do the rest. Setup is about 30 minutes, and every later CSV takes two minutes.
The finished call sheet
| Score | Business | Best email | Best phone | Website | Found on | |
|---|---|---|---|---|---|---|
| 3 | example-clinic.in | appointments@example-clinic.in | +914000000001 | wa.me/914000000001 | https://example-clinic.in | …/contact-us |
| 2 | example-studio.com | hello@example-studio.com | https://example-studio.com | …/about | ||
| 1 | example-traders.co.in | +914000000002 | https://example-traders.co.in | …/ |
Score: 3 = email and phone, 2 = email only, 1 = phone only, 0 = nothing usable (kept on a separate "check by hand" list).
Why a raw scrape is not a call sheet yet
- Too many columns. When Apify exports a dataset to CSV it flattens every list, so you get
emails/0/email,emails/1/email,phones/0/phone,phones/0/source,socials/whatsapp/0/urland dozens more. You need about eight of them. - Repeats. If you merge two runs, or your list had both
www.and bare domains, the same company appears twice. You want one row, the better one. - The first phone is not always the best one. Numbers are listed in number order, not by quality. A number in a clickable
tel:link is the one the business wants you to call. - Blocked and failed sites have no contacts at all and only clutter the list.
Step 1: get one contact CSV
Free, a few businesses
Open each company's contact or about page and type what it publishes into a sheet with the same column names used below (domain, status, emails/0/email, phones/0/phone, startUrl). For ten or twenty businesses that is quicker than any tool. If you already have a CSV from another contact tool, rename its columns to these names and skip 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", "example-traders.co.in"],
"maxPagesPerSite": 4,
"includePersonalEmails": false,
"respectRobotsTxt": true,
"respectTermsOfService": true,
"checkEmailMx": true,
"defaultCountry": "IN"
}
Set defaultCountry to the country most of the businesses are in, so local numbers without a country code are read correctly. Leave includePersonalEmails off: the call sheet is built on shared role inboxes such as info@, sales@ and appointments@. Already have a Google Maps dataset? Pass its dataset ID instead; see enrich a Google Maps Apify dataset with emails and phones, and the business name then comes through as sourceItems/0/title.
When the run finishes, open the dataset, choose Export → CSV and download it.
Step 2: import it as text into a tab called raw
- Google Sheets: new sheet, rename the first tab to
raw, then File → Import → Upload, choose Replace current sheet and untick "Convert text to numbers, dates, and formulas". Without that,+914000000001becomes the number 914000000001. - Excel: Data → From Text/CSV, then Transform Data and set every
phones/…/phonecolumn to Text before you load it into a sheet namedraw. - Never edit the
rawtab. Next time, import the new CSV over it and the call sheet refreshes.
Step 3: add a COL helper (one-time)
The column order of a CSV can change between runs, so pick columns by their header name, not their letter.
- 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), ""))
COL("domain") now returns the whole domain column. If a header is missing (for example no site in the run had a second email, so there is no emails/1/email), it returns blank instead of an error.
Step 4: build the rows tab
Add a tab called rows. Type these headers in row 1 and each formula in row 2 of its column. In Google Sheets the formulas already spill down; in Excel 365 they spill without ARRAYFORMULA (you can leave it out).
| Col | Header | Formula in row 2 |
|---|---|---|
| A | business | =COL("domain") |
| B | status | =COL("status") |
| C | name | =COL("sourceItems/0/title") (blank unless you started from a Maps dataset) |
| D | best email | =COL("emails/0/email") |
| E | backup email | =COL("emails/1/email") |
| F | best phone | see below |
| G | =COL("socials/whatsapp/0/url") | |
| H | website | =COL("startUrl") |
| I | found on | see below |
| J | score | see below |
F: best phone (a 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"))))
I: found on (the page where the email, or else the phone, was published)
=ARRAYFORMULA(IF(COL("emails/0/foundOn")<>"", COL("emails/0/foundOn"), COL("phones/0/foundOn")))
J: score (3 email + phone, 2 email only, 1 phone only, 0 none)
=ARRAYFORMULA(IF(A2:A="", "", IF((D2:D<>"")*(F2:F<>""), 3, IF(D2:D<>"", 2, IF(F2:F<>"", 1, 0)))))
The scraper lists same-domain emails first, so emails/0/email is normally the company's own inbox. Want WhatsApp to break ties? Change the 3 to 3+(G2:G<>"")*0.5.
Step 5: one row per business (the call_sheet tab)
Add a tab called sorted. In A2, sort the usable rows best score first, then by business name:
=SORT(FILTER(rows!A2:J, rows!A2:A<>"", rows!B2:B="ok", rows!J2:J>0), 10, FALSE, 1, TRUE)
In K2 of the same tab, number each repeat of a business (1 = its best row):
=ARRAYFORMULA(IF(A2:A="", "", COUNTIFS(A2:A, A2:A, ROW(A2:A), "<="&ROW(A2:A))))
Finally add the call_sheet tab, copy the headers from rows into row 1, and in A2:
=FILTER(sorted!A2:J, sorted!K2:K=1)
That is your call sheet: one row per company, most reachable first. For the "check by hand" list (blocked, failed or empty sites), add one more tab with =FILTER(rows!A2:J, rows!A2:A<>"", (rows!B2:B<>"ok")+(rows!J2:J=0)).
Excel 365: SORT and FILTER work the same. For the de-duplication, put =UNIQUE(sorted!A2:A1000) in call_sheet!A2 and fill each other column with =XLOOKUP($A2#, sorted!$A$2:$A$1000, sorted!D$2:D$1000) (change the last range per column). XLOOKUP returns the first match, which is the best-scored row.
Step 6: check before you call
- Email on another domain? If the best email's domain differs from the website, open the "found on" page. It can be the web designer's or a booking service's inbox, not the business.
- Toll-free and switchboard numbers are fine for a first call, but ask for the right person rather than guessing direct lines.
mxValidFALSE on an email (columnemails/0/mxValidinraw) means its domain cannot receive mail. Use the backup email or the phone.- Blocked by robots or terms (status
blocked-by-robotsorblocked-by-terms) means the site asked bots not to read it. Visit it yourself or leave it out.
What not to do
- Do not guess personal addresses such as firstname@company. Use what the business publishes.
- Do not load the sheet into a bulk cold-email tool. Make genuine one-to-one calls and messages, and follow your local rules on calls and marketing (for example India's DND/TRAI rules, the EU's GDPR and ePrivacy rules, or the US TCPA).
- Do not message a WhatsApp number in bulk. Only use it where the business invites chat and your first message is a real, relevant enquiry.
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.
Related
- Enrich a Google Maps Apify dataset with emails and phones
- Find public role emails (sales@, press@, support@) on a company website
- Find a business website's WhatsApp and social links
- 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 you can open the call knowing their range.
Unadhi Guides are free, independent explainers. We correct errors as soon as we find them: write to mininglife7@gmail.com.