# Founder Email Finder by Domain — Hunter.io Alternative $1.5/1k (`intropi/company-contacts-tech-lookup`) Actor

Domain or website in: the founder or owner's name and email out (labelled published on site or guessed), plus the best company email, phone and LinkedIn. Also reads a Google Maps dataset, or finds companies by industry in the Singapore (or UK) registry. $1.50 per 1,000 with contacts.

- **URL**: https://apify.com/intropi/company-contacts-tech-lookup.md
- **Developed by:** [Md. Saquib Jawed](https://apify.com/intropi) (community)
- **Categories:** Lead generation, Automation
- **Features:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $1.50 / 1,000 company with contacts (from your websites or a google maps dataset)s

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Founder Email Finder by Domain — Hunter.io Alternative $1.5/1k

Give it a domain or website, and get **the founder's or owner's name and email** (labelled *published on site* or *guessed*), plus the best company email, phone numbers and LinkedIn: one row per company. It also reads a **Google Maps dataset** (the websites your Maps scraper found) and can **find companies by industry in the Singapore registry** (ACRA; UK Companies House too).

**Use this when** you want a Hunter.io-style founder email finder by domain without a subscription, when your Google Maps scrape came back with no emails or only info@ addresses, when you want a contact details scraper that returns one clean row per company, or when you need a Singapore company list by industry with contacts.

How it differs from Hunter.io: it reads the company's own website (home, contact, about, team pages) for the people it names and the addresses it shows; it does not have a database of emails collected elsewhere, and it never checks a guess by sending mail.

### Price per 1,000, compared honestly

| | Price | What you pay for |
|---|---|---|
| **This Actor, websites or Google Maps dataset** | **$1.50 per 1,000 companies with contacts** | Only rows with an email, phone or social profile. Sites that fail or have nothing are free. Owner/founder name and email included |
| **This Actor, find by industry (Singapore, UK registry)** | **$5 per 1,000 companies with contacts** | Registry search, verified website and contacts; registry rows without contacts are free |
| Google Maps email add-ons (e.g. aurelix/google-maps-scraper) | $1.50 per 1,000 contact results (plus $0.85 per 1,000 places) | Emails and socials found on the site; no owner or founder field advertised |
| compass/crawler-google-places, contacts add-on | $2 per 1,000 places enriched, on top of $4 per 1,000 places | Company emails and socials; owners only through a $100 per 1,000 leads add-on |
| Most-used contact details scraper (vdrmota/contact-info-scraper) | $2 per 1,000 pages read; about $30 per 1,000 websites in our test (15 pages each) | Every email on every page, one row per page |

Same price band as the cheapest Google Maps email extractors, but with the owner or founder's name and email when the site names them.

#### Measured, not promised

On 63 real small-business websites (dentists in London, plumbers in Austin, agencies in Pune, tax advisers in Munich, bakeries in Toronto), next to the most-used contact scraper in the Store on its default settings: **emails for 57 vs 51**, **phones for 54 vs 40**, **emails belonging to other organisations 5 vs 22**, **81 seconds vs 416 seconds**. Founder emails, checked against 189 real people: **70% exactly right**, **76% when the site shows its email format**; every one is labelled published or guessed, and guesses are never sent mail.

Three ways in:

1. **Paste websites, get contacts.** You already have a list (a directory, a CRM export, a spreadsheet).
2. **Google Maps dataset.** You ran a Google Maps scraper and got places with websites but no emails. Give its dataset ID; the place name, phone, address and category are kept on each row.
3. **Find companies by industry and country.** You don't have a list yet. Pick an industry and a country; the Actor searches the official company registry (Singapore ACRA, UK Companies House), finds each company's verified website and reads it for the same contacts.

Filter on `ownerFound` (true/false) to keep only the companies where an owner, founder or director was named.

### 1. Paste websites, get contacts

Fill **Websites**, one per line. Each line can be a domain (`acme.com`), a full link (`https://www.acme.co.uk/about`) or an email (`jane@acme.io`, its domain is used). Duplicates are removed.

```json
{
  "websites": ["uventures.com.sg", "decodehr.com", "plausible.io", "buffer.com", "biggsplumbing.com"],
  "maxPagesPerSite": 5,
  "checkEmailDomains": true,
  "includeTechnologies": true
}
```

#### What you get

| Field | Example |
|---|---|
| `companyName`, `domain`, `website` | Biggs Plumbing Company, biggsplumbing.com |
| `bestEmail` | sales@biggsplumbing.com: a shared inbox on the company's own domain is picked first; never noreply@, privacy@ or jobs@ |
| `emails`, `emailDetails` | Each email with `type` (`role` = info@, sales@…; `personal` = jane.smith@…), `onCompanyDomain`, `domainAcceptsMail` and the page it was found on |
| `phones`, `phoneDetails` | +15128375955, with the number as written and its country |
| `linkedinUrl`, `xUrl`, `facebookUrl`, `instagramUrl`, `youtubeUrl`, `tiktokUrl`, `githubUrl`, `pinterestUrl`, `threadsUrl`, `whatsappUrl`, `telegramUrl` | The company's own profiles, not share buttons, single posts or the web agency's account |
| `address`, `country`, `language` | 1 High Street, London, EC1A 1BB, GB |
| `foundedYear`, `foundedYearSource` | 2015, `UEN 201531216Z`: from the site's schema.org data, a Singapore company registration number (UEN) printed on the site, or wording like "Established in 2015" or "Since 2015". Copyright years are never used. Empty when the site does not say |
| `founderName`, `founders`, `ownerFound` | Jane Tan; each person the site names as founder, CEO, managing director, director, owner or principal, with their `title` and the page it was found on. On German, Austrian and Swiss sites the Impressum is read for the legal representative (`Geschäftsführer (Managing Director)`, `Inhaber (Owner)`, `Vertretungsberechtigt (Legal representative)`, `Vorstand`, `Gesellschafter`): a name for 44 of 48 small-business sites tested, against 2 before. Client testimonials are skipped; empty rather than a guess. `ownerFound` is true when a name was found, for filtering |
| `founderSource`, `npiNumber` | `website` or `US NPI Registry`: where `founderName` came from. For US dental and medical practices the Actor also checks the free federal NPI Registry (see below); a match is added to `founders` with the registry's title (Owner, President, Medical Director…), `source: "US NPI Registry"` and the practice's `npiNumber` (1952809964), and is used as `founderName` when the website names nobody |
| `npiProviders`, `doctorsOnSite` | When no organisation record matches: individual dentists or doctors registered in the NPI Registry at the practice's own street address (`source: "US NPI Registry (individual)"`, their own `npiNumber`, `specialty`, `practiceAddress`). `role` is `owner` only when the website names them owner or the practice is named after them; otherwise `dentist at practice`, and they are not used as `founderName`. `doctorsOnSite` lists the doctors the website names ("Dr. Jane Doe") |
| `founderEmail`, `founderEmailStatus`, `founderEmailAlternatives`, `emailPattern` | jace@uventures.com.sg, `guessed (common format)`. The founder's own address when the site publishes it (`published on site`; a short form counts, so will@ is William's); when the site names no founder but shows one or two personal addresses on its own domain, the first of them (`published on site (owner not named)`, founderName left empty); otherwise a guess from the company's email format seen on the site (`guessed from site email format`) or the most common small-business format (`guessed (common format)`), with other likely addresses. Guesses are not checked by sending mail; only for companies that use their own email domain |
| `founderEmailConfidence`, `founderEmailReason` | `low`, "No personal address on the website shows the company's email format, so this is the most common small-business format…". How far to trust `founderEmail`: `high` = shown on the website for this person; `medium` = built from the format other people's addresses on the site use (right about 72% of the time in testing), or a personal address on the site whose owner is not named; `low` = no format seen on the site, so the most common format (first name only; about 1 in 5 right, and one of the three addresses including the alternatives about 2 in 3). The reason says in one sentence where the address came from, and names the short-form address to try for a William or a Katherine |
| `technologies`, `technologyDetails` | Shopify, Klaviyo, Google Tag Manager, Meta Pixel, Intercom… each with a category |
| `contactPageUrl`, `pagesVisited` | Where the details came from |
| `mapsPlaceName`, `mapsPhone`, `mapsAddress`, `mapsCategory`, `mapsPlaceId`, `mapsUrl` | Only for websites from a Google Maps dataset: the place as Maps lists it |
| `status`, `error` | `ok`, `no_contacts_found`, `unreachable` (with the reason) or `invalid_input` |

#### Why this one

- **One row per company.** Ready to import into a CRM or a spreadsheet; no merging page-by-page results.
- **Clean emails.** Image names like `logo@2x.png`, template placeholders, tracking ids and other companies' addresses are removed, even when the site shows nothing else: the web designer's credit, a site builder's address left in a template (contact@framer.com), vendors and partners. Gmail-style inboxes are kept when the business uses them (its only address, a mailto link, or one carrying the brand), not when they appear in a product demo. Hidden addresses are recovered: `name [at] company [dot] com` and Cloudflare-protected emails.
- **Emails that can receive mail.** Domains without mail servers are dropped (turn off with *Drop emails whose domain cannot receive mail*).
- **Real phone numbers only.** Numbers are checked against each country's numbering rules and formatted internationally; fax numbers and order numbers are skipped.
- **Tech stack included.** Filter leads by the tools they use: find Shopify stores without Klaviyo, WordPress sites without a chat widget, companies running Google Ads.
- **Predictable cost.** $1.50 per 1,000 companies with contacts, whatever the number of pages read.

#### How well it works

Measured on 63 real small-business websites (dentists in London, plumbers in Austin, marketing agencies in Pune, tax advisers in Munich, bakeries in Toronto), run on Apify in November 2026, next to the most-used contact scraper in the Store on its default settings:

| Same 63 websites | This tool | Most-used contact scraper |
|---|---|---|
| Websites with an email | **57 (90%)** | 51 (81%) |
| Websites with a phone number | **54 (86%)** | 40 (63%) |
| Websites with a LinkedIn page | **22** | 19 |
| Emails belonging to other organisations (trade bodies, template filler like `info@mysite.com`) | **5** | 22 |
| Run time | **81 seconds** | 416 seconds |
| Cost of the run | **$0.09** at today's price (60 companies with contacts) | $1.92 (958 pages) |

When a site's main pages show no email, the tool also reads its privacy, complaints or cookie page, where small businesses often put the only address.

### Google Maps dataset: emails and owners for your Maps places

Google Maps scrapers give you each place's website but rarely an email, and almost never the owner. Put the scraper run's dataset ID (from its **Storage** tab, or the whole dataset link) in **Google Maps dataset ID**:

```json
{
  "datasetId": "aBc123XyZ",
  "onlyWithContacts": true
}
```

- Works with any Apify dataset that has a website column: compass/crawler-google-places, aurelix/google-maps-scraper, other Maps and directory scrapers. The website is taken from `website`, `site` or `domain`, or from `url` when it is not a Google Maps link.
- One row per website domain: chains and repeated listings are read once. Places without a website, or with only a Facebook or Instagram page, are skipped (counted in the run log and the `SUMMARY` record).
- The place's own details are kept on the row: `mapsPlaceName`, `mapsPhone`, `mapsAddress`, `mapsCategory`, `mapsPlaceId`, `mapsUrl`.
- Same price as pasted websites: $1.50 per 1,000 companies with contacts. You can combine it with **Websites**.

**Chain it automatically.** Open your Google Maps scraper's task (or Actor), go to **Integrations**, add **Apify Actor**, pick this Actor, choose the event **Run succeeded**, and set the input to:

```json
{ "datasetId": "{{resource.defaultDatasetId}}", "onlyWithContacts": true }
```

Every time the Maps run succeeds, this Actor runs on its places and your contacts land in its dataset. The same works from Make, Zapier, n8n or the API: pass the Maps run's `defaultDatasetId` as `datasetId`.

### 2. Find companies by industry and country

Leave **Websites** empty and fill the section **Or find companies by industry**: a country, industry keywords and/or industry codes, and optional filters for age, location, officer count and status. If **Websites** has entries, this section is ignored (the run log says so).

Countries:

- **Singapore**: ACRA, the official company registry, published on data.gov.sg.
- **United Kingdom**: Companies House (new: speed and website hit rate are still being measured).

Coming next: France, Norway (both publish employee counts).

#### Who it is for

- **Agencies** (marketing, web, IT, recruitment) looking for Singapore or UK SMEs in a given industry.
- **B2B sellers** building a list of accounting firms, clinics, logistics companies, renovation contractors and so on.
- Anyone who wants a list that starts from the official registry, not from whoever happens to be on Google Maps.

#### What each registry gives

| | Singapore (ACRA) | United Kingdom (Companies House) |
|---|---|---|
| Industry codes | SSIC: a primary and sometimes a secondary code | SIC 2007: up to four codes |
| The company's own words for what it does | Yes, when it filled them in (about 29% of live companies) | No |
| Registration date and status | Yes | Yes (incorporation date) |
| Officers (directors, secretaries) | Yes, a count for every company | Only when you set "Maximum number of officers" (looked up company by company, so slower) |
| Address | Registered address | Registered office address, with town and postcode |
| Registration number on the website | Many Singapore sites show the UEN | UK companies must show their company number on their website, which helps us confirm the right site |

#### What you get

One row per company: the contact fields from mode 1 (`bestEmail`, `emails`, `phones`, the social profiles, `founderName`, `founders`, `founderEmail`, `founderEmailStatus`, `founderEmailConfidence`, `founderEmailReason`, `founderEmailAlternatives`, `emailPattern`, `foundedYear`, `technologies`, `contactPageUrl`) plus:

| Field | Example |
|---|---|
| `companyName`, `name` | ACME PAYROLL PTE. LTD. (both the registered name) |
| `country`, `registrationNumber` | SG, 201531216Z (the UEN in Singapore, the company number in the UK) |
| `entityStatus`, `entityType` | Live Company, Local Company (as published by the registry; UK: active, ltd) |
| `registeredOn`, `yearsOperating` | 2015-03-01, 11 |
| `industryCode`, `industryDescription` | 69201, Accounting services (the main industry code) |
| `industryCodes` | Every industry code with its description, main one first |
| `activityDescription` | Singapore only: the company's own words, "PAYROLL AND BOOKKEEPING SERVICES" |
| `officers` | 2 (directors, secretary and similar; see "Good to know"). Empty when unknown |
| `employees` | Empty for now: neither registry publishes headcount |
| `address`, `postalCode`, `locality` | Registered address; `locality` is the town (UK) |
| `formerNames` | Earlier registered names |
| `matchedBy` | Why it matched: `industry-code`, `secondary-code` or `keyword` |
| `website`, `domain` | https://www.acmepayroll.com.sg, acmepayroll.com.sg |
| `websiteConfidence`, `websiteMethod` | `high` (the site shows the registration number or the registered name) or `medium` (the brand plus a sign of the right country), and how it was found |
| `status`, `error` | See below |

`status` is one of:

- `ok`: website found and contacts read (or you did not ask for websites/contacts).
- `no_website_found`: no website we could verify.
- `website_unreachable`: website found but it was down or blocked automated visits; `error` says why.
- `no_contacts_found`: website found but it had no email, phone or social profile.

#### How the filters work

- **Country**: Singapore (default) or United Kingdom.
- **Industry codes** are the reliable way to search. Every company has one. Use the codes of the country you search; a code can be shortened to its first digits: `692` covers all of `692xx`. Common ones:

  | Industry | Singapore (SSIC) | United Kingdom (SIC 2007) |
  |---|---|---|
  | Accounting and auditing | 69201 | 69201 |
  | Bookkeeping | 69202 | 69202 |
  | Management consultancy | 70201 | 70229 (other than financial management) |
  | HR consultancy | 70204 | |
  | Marketing and PR consultancy | 70205 | |
  | HR management, outsourcing, provision | 78300 | 78300 |
  | Employment / placement agencies | 78104 | 78109 |
  | Software development | 62011 | 62012 (business and domestic software) |
  | IT consultancy | 62021 | 62020 |
  | Advertising | 73100 | 73110 (advertising agencies) |

  The UK codes are checked against the official SIC 2007 list. Payroll has no code of its own; try accounting and HR codes.
- **Industry keywords** match the industry descriptions and, in Singapore, the company's own description of what it does, not company names. In Singapore about 71% of live companies (over 80% of those registered since 2020) left their own description empty, so **keywords alone miss most firms**. In Singapore, if you give no codes, common keywords (like "accounting") also bring in their matching codes automatically. Codes and keywords are combined: a company matches either.
- **Location** (optional): Singapore: a postal code or its first digits (`079`) or words from the address (`Anson Road`). UK: a town or city (`Manchester`) or the start of a postcode (`EC1`).
- **Minimum / maximum years operating**: counted from the registration date. The default minimum of 2 years skips most brand-new and dormant shelf companies.
- **Maximum number of officers** (optional): a rough way to favour small companies. See below.
- **Only live companies** (default on): Singapore keeps "Live", "Live Company" and "Live (Receiver or Receiver and Manager appointed)"; the UK keeps "active". Struck off, dissolved and similar are skipped.
- **Maximum companies** (default 100): stops after this many registry matches.
- **Find websites** / **Get contacts from the website** (both on by default): turn off to get registry rows only, free.
- **Search the web for websites guessing missed** (default on): when no website is found from the company's name, the Actor searches Google once for it (the name plus the country) and checks the top three results the same strict way. This finds companies that trade under another brand. **It calls Apify's [Google Search Results Scraper](https://apify.com/apify/google-search-scraper) on your Apify account, costing about $0.0045 per company searched, billed by that Actor.** All searches go into one run of it. Turn this off to never search.
- **Maximum web searches** (default: one per company, up to "Maximum companies"): caps how many companies are searched, and so that cost. 0 = no searches.
- **Only companies with a website** (default off): leaves out companies without a verified website.
- The website settings at the top (**Pages to read per website**, **Drop emails whose domain cannot receive mail**, **Detect technologies**, **Only output companies with contacts**) apply here too.

#### Example input

Singapore:

```json
{
  "country": "SG",
  "industryKeywords": ["payroll"],
  "industryCodes": ["69201", "78300"],
  "minYearsOperating": 3,
  "maxOfficers": 5,
  "liveOnly": true,
  "maxCompanies": 100,
  "findWebsites": true,
  "searchForWebsites": true,
  "maxSearches": 50,
  "includeContacts": true,
  "onlyWithWebsite": true
}
```

United Kingdom, accountants in Manchester:

```json
{
  "country": "GB",
  "industryCodes": ["69201", "69202"],
  "location": "Manchester",
  "minYearsOperating": 3,
  "maxCompanies": 100
}
```

The old input name `ssicCodes` still works and means the same as `industryCodes`.

#### How long it takes

- **Registry search, Singapore:** data.gov.sg limits how fast it can be searched. A search capped by "Maximum companies" takes about 20 to 80 seconds; a search for every match takes 2 to 4 minutes.
- **Registry search, United Kingdom:** to be measured. Setting "Maximum number of officers" makes it slower, because officers are looked up company by company.
- **Websites:** about 7 seconds per company in Singapore (UK: to be measured), several at once (see "Websites (or companies) at the same time").
- **Web search:** one search run for all the companies that still need it, about 40 to 60 seconds in total, then a few seconds per company to check the results.

### Pricing

Pay per event, two events, one per way in. Each company row is charged at most once.

- **`website-company`, $0.0015 ($1.50 per 1,000):** pasted websites and Google Maps datasets. You pay for each company row with at least one email, phone number or social profile. Rows for sites that could not be loaded or had nothing are free.
- **`company`, $0.005 ($5 per 1,000):** find by industry. You pay for each company whose verified website gave at least one email or phone number. Registry rows without contacts (no website found, website down, no email or phone on it) are free.

Examples:

- 63 pasted websites, 60 with contacts: **$0.09**.
- 1,000 Google Maps places, 700 with their own website, 600 with contacts: **$0.90**.
- 100 Singapore companies by industry, 50 with contacts: **$0.25**, plus the web search below.
- 1,000 registry rows only (websites off): **free**.

**Web search cost (separate, billed by another Actor).** With "Search the web for websites guessing missed" on, companies whose website was not found from their name are searched with Apify's Google Search Results Scraper (`apify/google-search-scraper`). It runs on your Apify account and bills you itself, about **$0.0045 per company searched** (one results page each). Only companies still without a website are searched, so 100 companies of which 60 need a search add about $0.27. Cap it with "Maximum web searches", or turn the search off.

Set a maximum cost per run in the run options. When it is reached, the run stops cleanly and keeps what it already found.

### Good to know

- It reads only public pages: the home page and the site's own contact, about, imprint and team pages. It does not log in anywhere and does not visit social networks.
- Sites that show a "checking your browser" page or refuse automated visitors are reported as `unreachable` (or `website_unreachable`) with the reason, and not charged.
- **Officers are not employees.** Officers are the directors and company secretaries in the registry; in Singapore about 70% of companies have 1 to 3. It says nothing about headcount, so use "maximum officers" only as a rough size hint.
- **Websites only when we can verify them.** A site is accepted only when it shows the company's registration number (UEN or UK company number) or registered name, or its brand name plus a sign of the right country (such as a local address or phone number). A website from a web search must also be the company's own site (its domain or homepage brand carries the name), not a directory, review site, marketplace or a page that merely mentions it. Tested on 70 real Singapore SMEs (UK: to be measured): the right website for 76% from the name alone and 84% with the web search, a wrong website for none. The rest get `no_website_found`, usually because the site blocks automated visits, the name is too ambiguous to search, or the company has no website at all (common for small registered companies). Those rows are free.
- **Expect a website for roughly 1 in 3 of the smallest firms.** In a live Singapore run of 15 HR consultancies with 5 or fewer officers, 5 had a website we could verify; most of the rest appear to have none. Larger or older firms have websites more often. For about 50 companies with contacts, start with `maxCompanies` of 150 or more.
- **US healthcare owners from the NPI Registry.** For US dentists, orthodontists, clinics, chiropractors, optometrists, therapists and similar practices (not vets: the registry has none), the Actor looks the practice up in the official, free NPI Registry and takes the organisation's authorized official when the title says they own or run it. A record is used only when its registered or "doing business as" name matches the business name and its practice address is in the same city or ZIP code; two records with different people that the street address cannot tell apart are skipped, never guessed. It needs the state, from the Google Maps fields or the address on the site. On 20 Google Maps dentists and orthodontists (Denver, Columbus, Raleigh) an owner was named for 8 instead of 2. Many small practices have no organisation record (the dentist bills under their own individual NPI), so when none matches the Actor looks for individual dentists whose registered practice address is the practice's street address, keeping only the doctors the website names when it names any. The registry does not say who owns such a practice, so these people are labelled "dentist at practice", not owner, unless the website says owner. On the same 20 practices this added a named dentist for 10 more (18 of 20 with a named owner or dentist). Turn off with *Look up US healthcare owners in the official NPI registry*. No extra charge.
- **Founder emails may be guesses, and are labelled.** `founderEmailStatus` says `published on site` when the address appears on the website. Otherwise it is a guess built from the company's email format (`guessed from site email format`) or the most common format (`guessed (common format)`). `founderEmailConfidence` (`high`, `medium`, `low`) and `founderEmailReason` say how far to trust each one. In testing on 189 people with known emails, guesses were right about 70% of the time overall, 72% when the site shows its email format, and about 1 in 5 when it does not (then one of the three addresses given is right about 2 in 3 times; for US, UK and Australian companies the second guess is the jsmith@ style, which they use often). They are never checked by sending mail.
- **Singapore registry data** is a monthly snapshot of ACRA's "Information on Corporate Entities" on data.gov.sg (last updated 2026-10-14, about 624,000 live companies), so changes from the last few weeks may be missing. Contains information from ACRA's Information on Corporate Entities, accessed via data.gov.sg, made available under the terms of the [Singapore Open Data Licence version 1.0](https://data.gov.sg/open-data-licence).
- **UK registry data** comes from the Companies House API and is current to the day. Contains public sector information licensed under the [Open Government Licence v3.0](https://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/).
- Personal emails (`type: personal`) are people's work addresses published on their company's site. If you email the people in the results, follow the rules where you and they are: GDPR in Europe, CAN-SPAM in the US, the PDPA and Spam Control Act in Singapore, UK GDPR and PECR in the UK.

### Use it with

- **Google Maps or directory scrapers:** give the run's dataset ID in "Google Maps dataset ID" (or chain it with an integration, see above) to get owner emails, inboxes and tech stack for each business.
- **Job listings:** companies that are hiring are buying; look up their contacts and tools.
- **Your CRM:** export as CSV or Excel, or call it from Make, Zapier, n8n or the Apify API.

### Feedback and reviews

Did it save you time? An honest review on the Apify Store helps other people find it. Something wrong or missing? Open an issue on the Issues tab. I read every one and usually reply the same day.

### Development

```bash
npm install
npm test          # unit tests: contact extraction on saved pages, both modes with fake registry, website and contact lookups
npm run dev       # local run; input in storage/key_value_stores/default/INPUT.json
npm run build
```

Layout:

- `src/main.ts` picks the mode (`src/mode.ts`): websites or a dataset given = lookup (`src/lookup.ts`, with `src/maps.ts` reading Google Maps datasets), else industry keywords/codes = finder (`src/run.ts`).
- `src/contacts/` reads one website for contacts (both modes). This repo is its only source since 2026-11-14.
- `src/registries/` has one adapter per country (`sg.ts`, `gb.ts`), picked by `getRegistry(country)`; `src/website.ts` and `src/search.ts` find and verify a registered company's website.
- `src/pricing.ts` names the two pay-per-event events.

Registry API keys come from environment variables (set them as secret environment variables in the Apify console):

- `DATA_GOV_SG_API_KEY`: optional; raises the data.gov.sg rate limit.
- `COMPANIES_HOUSE_API_KEY`: required for UK searches; a free key from the [Companies House developer hub](https://developer.company-information.service.gov.uk/). Without it, UK runs stop with a clear message before anything is charged.

Deploy with the Apify CLI: `apify login`, then `apify push`. It updates the existing Actor `company-contacts-tech-lookup`. Pricing is pay-per-event with two events: `website-company` (websites and datasets) and `company` (find by industry). If `website-company` is missing from the Actor's pricing, websites mode charges `company` instead, so code and pricing can be deployed in either order.

# Actor input Schema

## `websites` (type: `array`):

Company websites or domains, one per line: plumber.com, https://www.acme.co.uk, or even an email like jane@acme.io (its domain is used). Duplicates are removed. You get each company's owner or founder name and email when the site names them, plus the best company inbox, phones and LinkedIn. Got a Google Maps scrape instead? Put its dataset ID in "Google Maps dataset ID" below. To find companies by industry, leave this empty and fill the section "Or find companies by industry".

## `datasetId` (type: `string`):

Optional. The ID (or link) of any Apify dataset with a website column, for example the output of a Google Maps scraper (compass/crawler-google-places, aurelix/google-maps-scraper and others) that gave you no emails. The website is taken from the field website, site or domain (or url, unless it is a Google Maps link). Places without a website, with only a Facebook or Instagram page, or with a domain already listed are skipped. The place name, phone, address, category, place ID and Maps link are kept as mapsPlaceName, mapsPhone, mapsAddress, mapsCategory, mapsPlaceId and mapsUrl. Pick the dataset, or paste its ID from the scraper run's Storage tab. Can be combined with "Websites". To chain it automatically, add an integration to your Google Maps task: when the run succeeds, run this Actor with {"datasetId": "{{resource.defaultDatasetId}}"}.

## `maxPagesPerSite` (type: `integer`):

The home page plus the best contact, about and imprint pages. 5 finds nearly everything; raise it for large sites. You are charged per company, not per page. Also used for the websites found by industry.

## `checkEmailDomains` (type: `boolean`):

Checks each email domain for mail servers (MX records) and removes addresses that would bounce for that reason. Applies in both modes.

## `includeTechnologies` (type: `boolean`):

Lists the tools the site uses: Shopify, WordPress, HubSpot, Stripe, Intercom, Google Ads, Meta Pixel and 80 more common tools. Applies in both modes.

## `onlyWithContacts` (type: `boolean`):

Leave off to also get a free row for each site that could not be loaded or had no contacts, with the reason. When finding companies by industry, on keeps only companies whose website gave an email or phone number (the ones you pay for).

## `concurrency` (type: `integer`):

How many websites to read in parallel. Leave empty for the default: 10 when looking up websites, 5 when finding companies by industry (at most 20 there).

## `lookUpNpiOwners` (type: `boolean`):

For US dental, medical, chiropractic, optometry, therapy and similar practices (not vets), looks the practice up in the free federal NPI Registry and adds its registered owner or authorized official (for example "Owner", "President", "Medical Director") to founders, with founderSource "US NPI Registry" and npiNumber. Used as founderName when the website names nobody. Only accepted when the registered name matches the business name and the city or ZIP code matches; unclear matches are skipped, never guessed. When no organisation record matches, also lists individual dentists or doctors registered at the practice's street address in npiProviders (role "owner" only when the website says so, otherwise "dentist at practice"). Needs the state, from the Google Maps fields or the site's address. Free; no extra charge.

## `lookUpArchivedPages` (type: `boolean`):

When the website names no founder or owner today, read older copies of its about and team pages in the Internet Archive (Wayback Machine). Founders found this way are marked founderSource "website (archived copy)" with the copy's date, because people may have changed since. Off by default: it adds time per company.

## `country` (type: `string`):

Which official registry to search. Singapore: ACRA (data.gov.sg), with SSIC industry codes, the company's own description of its business, registration date, status and officer count. United Kingdom: Companies House, with SIC 2007 codes, incorporation date, status and registered office address; officer counts only when you set "Maximum number of officers". Industry codes differ by country, so pick codes from that country's list.

## `industryKeywords` (type: `array`):

Words that describe the business, one per line, e.g. accounting, payroll. Matched against the industry descriptions (and, in Singapore, the company's own description of its business), not company names. Many companies give no description of their own, so keywords alone miss most firms: add industry codes for a full search. Fill this, industry codes, or both. Example: payroll.

## `industryCodes` (type: `array`):

The reliable way to search: every company has one. Use the selected country's codes; a code can be shortened to its first digits ("692" covers all 692xx). Singapore (SSIC): 69201 accounting, 70201 management consultancy, 70204 HR consultancy, 70205 marketing/PR consultancy, 78300 HR management/outsourcing, 78104 employment agencies, 62011 software development, 62021 IT consultancy, 73100 advertising. United Kingdom (SIC 2007): 69201 accounting and auditing, 69202 bookkeeping, 62012 software development, 62020 IT consultancy, 70229 management consultancy, 73110 advertising agencies, 78109 employment placement, 78300 HR provision. A company matches a code or a keyword. Example: 69201 and 78300.

## `location` (type: `string`):

Optional. Only companies registered in this place. Singapore: a postal code or its first digits (e.g. 079 or 018989) or words from the address (e.g. Anson Road). United Kingdom: a town or city (e.g. Manchester) or a postcode start (e.g. EC1). Leave empty for the whole country.

## `minYearsOperating` (type: `integer`):

Only companies registered at least this many years ago. 2 skips most brand-new and shelf companies. 0 = no minimum.

## `maxYearsOperating` (type: `integer`):

Optional. Only companies registered at most this many years ago.

## `maxOfficers` (type: `integer`):

Optional. Officers are the directors and company secretaries in the registry, not employees. Most small companies have 1 to 3, so this is only a rough size hint. In the UK, setting it makes the search slower (officers are looked up per company).

## `liveOnly` (type: `boolean`):

Keep only companies that are trading: in Singapore the statuses Live, Live Company and Live (Receiver or Receiver and Manager appointed); in the UK "active". Skips struck off, dissolved and similar.

## `maxCompanies` (type: `integer`):

Stop after this many companies from the registry. Only companies whose website gave an email or phone number are charged, so this caps the cost. In Singapore a capped search takes about 20 to 80 seconds; a very large one 2 to 4 minutes.

## `findWebsites` (type: `boolean`):

Look for each company's own website from its registered name, about 7 seconds per company. A site is only accepted when it shows the company's registration number (UEN or UK company number) or registered name, or its brand plus a sign of the right country. In Singapore testing it found the right site for 76% of SMEs from the name alone (84% with the web search below) and a wrong one for none.

## `searchForWebsites` (type: `boolean`):

When no website is found from the company name, search Google once for each such company (the name plus the country) and check the top results the same strict way. This finds companies trading under another brand. It runs Apify's Google Search Results Scraper (apify/google-search-scraper) on your Apify account, all searches in one run, costing about $0.0045 per company searched, billed by that Actor on top of this one. Turn off to never search.

## `maxSearches` (type: `integer`):

At most this many companies are searched (one search each, about $0.0045 per search). Leave empty to allow one per company (Maximum companies). 0 = no searches.

## `includeContacts` (type: `boolean`):

Read the website's home, contact and about pages for emails, phone numbers, social profiles, founder and tech stack. Needs "Find websites".

## `onlyWithWebsite` (type: `boolean`):

Leave out companies for which no verified website was found (they are free either way).

## Actor input object example

```json
{
  "websites": [
    "uventures.com.sg",
    "decodehr.com",
    "plausible.io",
    "buffer.com",
    "biggsplumbing.com"
  ],
  "maxPagesPerSite": 5,
  "checkEmailDomains": true,
  "includeTechnologies": true,
  "onlyWithContacts": false,
  "lookUpNpiOwners": true,
  "lookUpArchivedPages": false,
  "country": "SG",
  "minYearsOperating": 2,
  "liveOnly": true,
  "maxCompanies": 100,
  "findWebsites": true,
  "searchForWebsites": true,
  "includeContacts": true,
  "onlyWithWebsite": false
}
```

# Actor output Schema

## `companies` (type: `string`):

One row per company: emails, phones, social profiles, founder and tech stack; for companies found by industry also the registry details and the verified website.

## `summary` (type: `string`):

How many companies had contacts, had none or could not be loaded, and how many were charged.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "websites": [
        "uventures.com.sg",
        "decodehr.com",
        "plausible.io",
        "buffer.com",
        "biggsplumbing.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("intropi/company-contacts-tech-lookup").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "websites": [
        "uventures.com.sg",
        "decodehr.com",
        "plausible.io",
        "buffer.com",
        "biggsplumbing.com",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("intropi/company-contacts-tech-lookup").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "websites": [
    "uventures.com.sg",
    "decodehr.com",
    "plausible.io",
    "buffer.com",
    "biggsplumbing.com"
  ]
}' |
apify call intropi/company-contacts-tech-lookup --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,intropi/company-contacts-tech-lookup"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/Ca8M7JqRZYqOisBmS/builds/MBTGWREfSyC6J2xAL/openapi.json
