# Leadership Contact Finder (`globalapi/leadership-contact-finder`) Actor

Crawl corporate websites to discover leadership contacts. Extracts names, titles, emails, LinkedIn URLs, phone numbers, and infers department, seniority, and location for each decision maker.

- **URL**: https://apify.com/globalapi/leadership-contact-finder.md
- **Developed by:** [GlobalAPI](https://apify.com/globalapi) (community)
- **Categories:** Lead generation, Automation, Agents
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

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

## 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

## Leadership Contact Finder

An Apify Actor that crawls corporate websites, discovers leadership contacts (executives, founders, VPs, directors, etc.), generates their likely email addresses from detected domain patterns, and optionally verifies them via SMTP.

### Features

- **Targeted page discovery** — Sitemap + homepage navigation scanning for `/about`, `/team`, `/leadership`, `/people`, `/founders`, `/management`, and similar pages.
- **Multi-strategy person extraction**
  - Schema.org JSON-LD `Person` objects (highest confidence)
  - DOM pattern detection for team-member containers
  - Text regex fallback for title + name patterns
- **Email pattern detection** — Learns common patterns (`first.last`, `f.last`, `first`, `firstlast`, `first_last`, etc.) from emails found on the site.
- **SMTP email verification** — Async batch verification with MX lookup, connection reuse, conservative rate limits, and catch-all domain detection.
- **Company enrichment** — Extracts company name, industry keywords, location, and social profiles.
- **Title inference** — Infers department, function, and seniority from job titles.
- **Anti-bot infrastructure** — Scrapling-based stealth browser with adaptive proxy escalation: rotates datacenter IPs first, then escalates to residential only when a domain keeps blocking.
- **Clean output** — Names, titles, phones, and locations are whitespace-normalized to avoid broken values like embedded `\n` characters.

### Input

```json
{
  "domain": "example.com",
  "rolesPreset": "decision_makers",
  "customRoles": ["CMO", "Managing Director"],
  "maxItems": 50,
  "verifyEmails": true,
  "useProxy": {
    "useApifyProxy": false,
    "apifyProxyGroups": []
  },
  "useStealth": true,
  "solveCloudflare": false,
  "includeContactText": true,
  "stayOnDomain": true,
  "maxConcurrency": 10
}
```

Either `domain` or `startUrls` is required. `maxItems` controls how many leadership contacts you want back; the actor automatically crawls enough pages (up to `maxItems × 15`, capped at 500) to find them. `maxConcurrency` lives under the **Advanced** section. `concurrentRequests` is accepted as a backward-compatible alias for `maxConcurrency`.

#### Roles presets

| Preset | Covers |
|---|---|
| `decision_makers` | CEO, CTO, Founder, COO, VP, Director, President, Chief, Head, Manager, Lead, Owner, Partner, Executive |
| `c_suite` | CEO, CTO, CFO, COO, CMO, President, Chief |
| `founders` | Founder, Co-Founder |
| `vp_directors` | VP, Director, Head, Senior Manager |
| `all_leadership` | Union of all presets |
| `custom` | Uses the keywords you provide in `customRoles` |

### Output

The actor pushes **one flat item per discovered leadership contact**:

```json
{
  "name": "Kate Bueker",
  "firstName": "Kate",
  "lastName": "Bueker",
  "email": "kbueker@hubspot.com",
  "emailConfidence": "verified",
  "phone": "+18884827768",
  "linkedinUrl": "http://www.linkedin.com/in/kate-bueker-25b0231",
  "title": "Chief Financial Officer",
  "department": "c_suite, finance",
  "functions": "finance",
  "seniority": "c_suite",
  "headline": "Chief Financial Officer at HubSpot",
  "positionHistory": [],
  "location": "Boston, Massachusetts, United States",
  "city": "Boston",
  "country": "United States",
  "companyName": "HubSpot",
  "domain": "hubspot.com",
  "sourceUrl": "https://hubspot.com/about",
  "extractionMethod": "schema_org",
  "extractionConfidence": "high",
  "catchAllDomain": false,
  "companyInfo": {
    "industry": ["software", "technology", "saas"],
    "location": "Cambridge, MA",
    "socials": {
      "linkedin": "https://linkedin.com/company/hubspot",
      "twitter": "https://twitter.com/hubspot"
    }
  }
}
```

#### Field notes

- `department` combines inferred seniority and function.
- `functions` is the normalized business function (finance, marketing, sales, engineering, etc.).
- `seniority` is one of: `c_suite`, `founder`, `owner`, `vp`, `director`, `manager`, `senior`.
- `positionHistory` is currently empty (`[]`) because the actor does not scrape full career history.
- `phone` uses the first company phone discovered on the domain when a direct line is not available.
- `sourceUrl` is the page where the contact was discovered.
- `companyInfo` includes industry keywords, employee-count estimate, location, and social profile links discovered on the homepage.

### Confidence Levels

- `verified` — SMTP server accepted the email recipient.
- `pattern_match` — Generated from the site's most common email pattern but not SMTP-verified.
- `catch_all` — Domain accepts all addresses; verification is unreliable.
- `failed` — SMTP server rejected the generated candidate.
- `unknown` — Verification disabled or could not be completed.

### Best Practices

1. **Use a proxy** — SMTP verification and stealth browsing both benefit from a proxy. The actor auto-enables a datacenter proxy when stealth is on. Residential proxy is used automatically only after repeated blocks on the same domain, or when you explicitly select a residential proxy group.
2. **Disable verification for speed** — If you only need candidate emails, set `verifyEmails: false` to skip SMTP checks.
3. **Tune roles** — Use the `custom` preset with industry-specific keywords (e.g., `CMO`, `Principal`, `Managing Director`).
4. **Respect rate limits** — The actor uses conservative delays between domains and emails; avoid aggressive concurrency on the same mail server.

### Limitations

- SMTP verification may be blocked or rate-limited by recipient mail servers.
- Catch-all domains make verification unreliable; the actor flags these explicitly.
- Some websites hide team data behind JavaScript or require authentication; stealth mode helps but cannot guarantee access.
- Position history is not extracted from websites.

# Actor input Schema

## `domain` (type: `string`):

The company domain to crawl (e.g. example.com). Either domain or startUrls is required.

## `startUrls` (type: `array`):

Optional explicit start URLs. If domain is also provided, these override the default https://domain/.

## `rolesPreset` (type: `string`):

Which set of leadership titles to look for. Choose Custom to enter your own keywords.

## `customRoles` (type: `array`):

Job-title keywords used when Leadership Roles is set to Custom (e.g. CMO, Principal, Managing Director).

## `maxItems` (type: `integer`):

Maximum number of leadership contacts to return. The actor automatically crawls enough pages to find them.

## `verifyEmails` (type: `boolean`):

When enabled, generated candidate emails are verified against the target domain's mail server. Disable to speed up crawling when verification is not needed.

## `stayOnDomain` (type: `boolean`):

Only follow links that point to the same domain as the start URLs.

## `useStealth` (type: `boolean`):

Automatically escalate to a stealth browser when a page blocks the fast HTTP session.

## `solveCloudflare` (type: `boolean`):

Automatically solve Cloudflare Turnstile and Interstitial challenges. Requires useStealth=true.

## `includeContactText` (type: `boolean`):

Scan visible page text for email addresses used in pattern detection.

## `useProxy` (type: `object`):

Use Apify Proxy to avoid IP bans. By default the actor uses datacenter IPs and rotates them on blocks. Residential proxies are used only when a domain repeatedly fails or when you explicitly select a residential proxy group.

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

Number of pages to fetch in parallel.

## Actor input object example

```json
{
  "domain": "example.com",
  "startUrls": [
    {
      "url": "https://example.com/"
    }
  ],
  "rolesPreset": "decision_makers",
  "customRoles": [],
  "maxItems": 50,
  "verifyEmails": true,
  "stayOnDomain": true,
  "useStealth": true,
  "solveCloudflare": false,
  "includeContactText": true,
  "useProxy": {
    "useApifyProxy": false,
    "apifyProxyGroups": []
  },
  "maxConcurrency": 10
}
```

# Actor output Schema

## `dataset` (type: `string`):

Flat leadership contact records pushed to the default dataset.

# 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 = {
    "domain": "example.com",
    "startUrls": [
        {
            "url": "https://example.com/"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("globalapi/leadership-contact-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 = {
    "domain": "example.com",
    "startUrls": [{ "url": "https://example.com/" }],
}

# Run the Actor and wait for it to finish
run = client.actor("globalapi/leadership-contact-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 '{
  "domain": "example.com",
  "startUrls": [
    {
      "url": "https://example.com/"
    }
  ]
}' |
apify call globalapi/leadership-contact-finder --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,globalapi/leadership-contact-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/ijXSSD51xmdKGERX3/builds/GQKBpHtDZOentrNn6/openapi.json
