# Company Name to Domain Finder – Bulk Website Lookup (`gazidev/company-domain-finder`) Actor

Find the official website domain for a list of company names. Builds candidate domains, checks DNS, verifies each homepage (site name, schema.org, copyright, city, phone) and returns the best domain with a confidence score. No search engines. Misses are free.

- **URL**: https://apify.com/gazidev/company-domain-finder.md
- **Developed by:** [Cemal Atakli](https://apify.com/gazidev) (community)
- **Stats:** 1 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.50 / 1,000 domain founds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## Company Name to Domain Finder – Bulk Website Lookup

**Find the official website domain for a list of company names.** Paste names from a CRM export, an event attendee list or a company registry, and get back `acme.com` with a confidence score. Use it before email finding, enrichment or CRM deduplication. Bulk company-to-domain lookups cost **$0.30 per 1,000 domains found**. Companies we cannot match are free.

The Actor does **not** scrape Google, Bing or DuckDuckGo. It builds candidate domains from each name and checks them in DNS. Then it opens each live homepage and checks that the website really belongs to that company.

### What it does

For every company name:

1. **Builds candidate domains.** It removes the legal form (Inc, LLC, Ltd, GmbH, SA, BV, AB and about 100 more) and tries the joined name, a hyphenated version, a version without generic words, the first two words and an acronym. These go on `.com`, the country domain (`.de`, `.co.uk`, `.com.au`...), `.net`, `.org`, `.co` and `.io`. German names also get umlaut spellings (`müller` → `mueller` / `muller`).
2. **Filters by DNS.** Domains that do not resolve are dropped. It also checks for an MX record, so you know whether the domain receives email.
3. **Verifies the homepage.** It reads the page title, `og:site_name`, schema.org Organization name, the copyright line and the page text, and compares them with the input name. If you supplied a city or phone number, it looks for that too.
4. **Rejects bad matches.** Parked or for-sale pages, registrar placeholders, empty pages and redirects to directories or marketplaces (Facebook, Yelp, Amazon, Booking...) are all thrown out. Redirects are followed, so `acme.co.uk` → `acme.com` returns `acme.com`.
5. **Returns the best domain** with a 0–100 confidence score, the signals that fired, alternatives and the rejected candidates with reasons.

### Use cases

- **Lead generation and cold email.** Turn a list of company names into domains, then run an email finder or [contact finder](https://apify.com/gazidev/website-contact-finder) on them.
- **Registry and government data.** Business-registry exports (UK Companies House, French SIRENE, US state filings, FMCSA carriers) have names but no websites.
- **CRM hygiene.** Fill the empty `website` field on accounts and deduplicate by domain.
- **Events and trade shows.** Get domains for exhibitor or attendee company names you are allowed to use.
- **AI agents.** Give an agent a "name → domain" tool with a confidence score, so it can decide when to trust the answer.

### Input

The simplest input is a list of names. Add `| country | city | phone` to make matching more precise.

```json
{
  "companies": [
    "Hawkshead Relish Company | GB",
    "Fairmount Bagel | CA | Montreal",
    "Puccini Bomboni | NL | Amsterdam",
    "Stripe, Inc. | US"
  ],
  "minConfidence": 50
}
```

Other ways to give input:

- **`bulkText`**: paste a CSV with a header row. The Actor reads the `name`/`company`/`company_name`/`organization`, `country`, `city` and `phone` columns. You can also paste one name per line.
- **`sourceFileUrl`**: a public TXT/CSV link, for example a Google Sheets "publish to web" CSV.
- **API objects**: `{"name": "Acme GmbH", "country": "DE", "city": "Berlin", "phone": "+49 30 1234567"}`.
- **`defaultCountry`**: the country for rows that have none.
- **`extraTlds`**: extra domain endings to try, such as `ai`, `app` or `com.au`.

| Option | Default | Meaning |
|---|---|---|
| `minConfidence` | 50 | Score needed to return (and charge) a domain. Use 70+ for strict matching. Use 35–45 to also accept exact-name domains that we could not read because of bot protection. |
| `maxCandidatesPerCompany` | 30 | Candidate domains checked in DNS |
| `maxWebsitesPerCompany` | 6 | Live homepages downloaded and scored (best first; stops early on a strong match) |
| `includeRejected` | true | Adds `rejectedCandidates` with the reason for each one |
| `respectRobotsTxt` | true | Skips homepages that robots.txt disallows |

### Output

One row per input company:

```json
{
  "inputIndex": 1,
  "companyName": "Fairmount Bagel",
  "country": "CA",
  "city": "Montreal",
  "phone": null,
  "found": true,
  "domain": "fairmountbagel.com",
  "website": "https://fairmountbagel.com/",
  "confidence": 97,
  "confidenceLevel": "high",
  "siteName": "Fairmount Bagel",
  "pageTitle": "Fairmount Bagel | Montreal Bakery | Hand made bagels",
  "signals": ["domainMatchesName", "nameIn:copyright", "nameIn:ogSiteName", "nameIn:schemaWebsiteName", "nameIn:title", "nameInPageText", "cityMatch", "hasMx"],
  "hasMx": true,
  "redirectedFrom": null,
  "alternatives": [],
  "candidatesGenerated": 10,
  "candidatesResolving": 2,
  "candidatesChecked": 2,
  "rejectedCandidates": [
    {"domain": "fairmount.com", "status": "low-score", "confidence": 0, "finalDomain": "fairmount.com", "siteName": "Fairmount Funds"}
  ],
  "notFoundReason": null
}
```

When nothing passes the threshold, the row has `found: false` and a `notFoundReason`. If there was a weak candidate, it is shown as `bestGuess`. These rows are **free**.

**Confidence levels:** `high` ≥ 80, `medium` 60–79, `low` < 60.

**Signals you may see:**

- `domainMatchesName`: the domain is the company name.
- `nameIn:title` / `ogSiteName` / `schemaName` / `copyright`: the name was found in that part of the page.
- `cityMatch` / `phoneMatch`: your city or phone number appears on the page.
- `countryTldMatch`: the domain ending matches your country.
- `locationNotConfirmed`: you gave a city or phone, but the homepage does not mention it. The score is capped at 49, because a same-name company elsewhere is likely.
- `unverifiedBlockedSite`: the site blocks bots, so only the domain name was scored (max 40).
- `viaRedirect`: the candidate redirected to the returned domain.

### Pricing (pay per event)

| Event | Price |
|---|---|
| Actor start | $0.00005 |
| **Domain found** (row with `found: true`) | **$0.0005** ($0.50 / 1,000) |
| Not found, errors, rejected | free |

| Store actor | Price per result |
|---|---|
| haketa/bulk-company-domain | $0.0009 |
| wildorigins | $0.001 |
| hydrafetch | $0.008 |
| **this Actor** | **$0.0005, and misses are free** |

Prices come from the public Apify Store, October 2026. The **Maximum cost per run** setting is respected: the Actor stops cleanly when the next result would exceed it.

### Limits (read before buying)

- **Generic names miss more often than with search-engine tools.** We do not use search engines. If a company's website is not named after the company (`Joe's Plumbing` → `joesplumbingatx.com`), we will not find it. These rows are free, but expect a lower hit rate on very small local businesses than on brands. In our test of 20 well-known European and US brands, 17 were found. 2 of those were questionable (a regional or brand-alias site instead of the main one), so check `alternatives`.
- **Same-name companies.** "Delta Plumbing" exists in many cities. Give a city or phone to make the match safe. Without one, a name-only match can be another company with the same name.
- **Bot-protected websites** (Cloudflare, Akamai, AWS WAF) cannot be read. When the exact-name domain resolves but blocks us, it is reported as a `bestGuess` with up to 40 points.
- **Names in non-Latin scripts** (Japanese, Chinese, Arabic, Cyrillic) are not transliterated. Add the Latin/English company name.
- Only the homepage is read (one request per candidate site, plus robots.txt), so we are not crawling anyone's site.

### FAQ

**Is it legal?** It uses public DNS and each candidate's public homepage. It sends one request per site, honors robots.txt and identifies itself with a contact address in the User-Agent. It does not scrape search engines.

**How fast is it?** About 20 companies in 30 seconds at 16 in parallel on 256 MB.

**Can I get the LinkedIn page or emails too?** Chain the found domains into [Website Contact Finder](https://apify.com/gazidev/website-contact-finder).

### Use with AI agents (Apify MCP)

This Actor works as a tool in the [Apify MCP server](https://mcp.apify.com). An agent can call `gazidev/company-domain-finder` with `{"companies": ["Acme GmbH | DE | Berlin"]}` and read `domain`, `confidence` and `signals` from the dataset. Ask the agent to accept only `confidenceLevel: "high"` for unattended workflows.

# Actor input Schema

## `companies` (type: `array`):

One company per line. Optionally add country, city and phone separated by `|`, e.g. `Fairmount Bagel | CA | Montreal | +1 514-272-0667`. Country and city make matches much more precise. Via API you can also pass objects: `{"name": "...", "country": "DE", "city": "...", "phone": "..."}`.

## `bulkText` (type: `string`):

Paste a list (one company per line, `|` for extra fields) or a CSV export with a header row. Recognized columns: `name`/`company`/`company_name`/`organization`, `country`, `city`, `phone`.

## `sourceFileUrl` (type: `string`):

Public URL of a .txt or .csv file in the same format as the bulk list (e.g. a Google Sheets 'publish to web' CSV link).

## `defaultCountry` (type: `string`):

Two-letter country code (or country name) used for rows without a country, e.g. `DE`, `GB`, `US`. It adds the country domain (.de, .co.uk...) to the candidates and boosts matching websites.

## `minConfidence` (type: `integer`):

A domain is returned (and charged) only when its confidence score reaches this value. 50 = balanced. Use 70+ for strict matching, 35-45 to also accept unverified exact-name domains.

## `extraTlds` (type: `array`):

Additional domain endings to try, e.g. `ai`, `app`, `com.au`. By default: .com, the country domain, .net, .org, .co, .io.

## `maxCandidatesPerCompany` (type: `integer`):

How many generated domains are checked in DNS per company. DNS checks are cheap.

## `maxWebsitesPerCompany` (type: `integer`):

How many live candidate homepages are downloaded and scored per company (best candidates first; stops early on a strong match).

## `includeRejected` (type: `boolean`):

Add `rejectedCandidates` to every row: checked domains that lost, with the reason (parked, redirect to a directory, blocked, low score...).

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

Stop after this many companies (0 = no limit). Useful to test a big list cheaply.

## `respectRobotsTxt` (type: `boolean`):

Skip homepages that the site's robots.txt disallows. Recommended.

## `maxConcurrency` (type: `integer`):

How many companies are processed at the same time. Each company fetches at most 3 different websites at once (one request per website).

## `requestTimeoutSecs` (type: `integer`):

Per-request timeout for homepage downloads.

## Actor input object example

```json
{
  "companies": [
    "Hawkshead Relish Company | GB",
    "Fairmount Bagel | CA | Montreal",
    "Puccini Bomboni | NL | Amsterdam",
    "Stripe, Inc. | US"
  ],
  "minConfidence": 50,
  "maxCandidatesPerCompany": 30,
  "maxWebsitesPerCompany": 6,
  "includeRejected": true,
  "maxCompanies": 0,
  "respectRobotsTxt": true,
  "maxConcurrency": 16,
  "requestTimeoutSecs": 12
}
```

# Actor output Schema

## `overview` (type: `string`):

No description

## `results` (type: `string`):

No description

# 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 = {
    "companies": [
        "Hawkshead Relish Company | GB",
        "Fairmount Bagel | CA | Montreal",
        "Puccini Bomboni | NL | Amsterdam",
        "Stripe, Inc. | US"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("gazidev/company-domain-finder").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 = { "companies": [
        "Hawkshead Relish Company | GB",
        "Fairmount Bagel | CA | Montreal",
        "Puccini Bomboni | NL | Amsterdam",
        "Stripe, Inc. | US",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("gazidev/company-domain-finder").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 '{
  "companies": [
    "Hawkshead Relish Company | GB",
    "Fairmount Bagel | CA | Montreal",
    "Puccini Bomboni | NL | Amsterdam",
    "Stripe, Inc. | US"
  ]
}' |
apify call gazidev/company-domain-finder --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,gazidev/company-domain-finder"
        }
    }
}
```

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/g7mzzmEUeDvGnbBtC/builds/Xy65imKQQJ9vZ57Xq/openapi.json
