# Company People Finder - Owners, Staff & Verified Emails (`leadproof/company-people-finder`) Actor

Find the owners, executives and staff of any company from its domain, each with the line on the company's own site that names their role. Add verified work emails and LinkedIn employees. Businesses where nobody is found are free.

- **URL**: https://apify.com/leadproof/company-people-finder.md
- **Developed by:** [Lead Proof](https://apify.com/leadproof) (community)
- **Categories:** Lead generation, Business
- **Stats:** 20 total users, 20 monthly users, 74.3% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $20.00 / 1,000 person 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 People Finder - Owners, Staff & Verified Emails

Paste company websites, pick the roles you want, get the people. Every person comes with the line on
the company's own site that names their role, so you can check it in one click. Add verified work
emails and LinkedIn employees when you need them.

**Companies where nobody is found are free.**

### What you get

For each company:

- **The people you asked for**: owners, founders, executives, purchasing, operations, sales and
  more, or **everyone** the company lists.
- **Proof for every person**: their title as published and the page and line that say so.
- **Verified work emails** (optional): built from the company's own address pattern and confirmed
  with the mailbox. Only confirmed addresses come back, clearly marked as found this way. A generic
  `info@` is never passed off as a person's address.
  With LinkedIn sources enabled, selected people still missing a verified address get a
  targeted profile email search, followed by our mailbox verification. These addresses
  have `origin: provider`; deliverability does not prove ownership. Profile and email-check
  limits apply. Personal, unrelated-domain and catch-all addresses are excluded.
- **LinkedIn employees** (optional): people from LinkedIn employee listings, merged with the site's
  people, each with a profile link.
- **A clear reason when the list is empty**: the company publishes nobody, its site could not be
  read, or a limit was reached. Never a silent blank.

Example person:

```json
{
  "fullName": "Jeff Cohn",
  "matchedRoles": ["founder", "executive"],
  "associations": [{
    "rawTitle": "Founder and CEO",
    "evidence": [{ "sourceUrl": "https://cohnmarketing.com/agency/", "quote": "Jeff Cohn — Founder and CEO" }]
  }],
  "contacts": [{ "kind": "email", "value": "jeff.cohn@cohnmarketing.com", "origin": "inferred", "deliverability": "ok" }]
}
```

### How to use it

1. **Company websites**: one domain per line, like `acme.com`.
2. **Who do you want?** Pick roles, or **Everyone** for the whole team.
3. **Where to look**: Company website, LinkedIn, or both.
4. Tick **Find verified work emails** if you want emails.
5. Run it on a few companies first.

Everything else has a safe default and sits in the folded sections of the input.

### Failures and recovery

A failed company does not discard other companies in the batch. Its row has
`status: failed`, `outcome: processing_failed` and a diagnostic `error`; an empty
failed row is not charged. Optional source or email-stage failures preserve people
already supported by evidence and mark the row `partial`, with the failed stage
in `errors` and `sourceFailures`. A stage timeout also preserves earlier results.

`SUMMARY.status` describes result quality: `succeeded`, `partial` or `failed`, with
failed/partial/unavailable business counts. Once all processed rows and the summary
are durably saved, `SUMMARY.executionStatus` is `completed`. Handled source and
company errors finish the Actor normally, even if every company is unavailable;
the run screen says **Completed with errors**, and the failed rows and
`SUMMARY.status: failed` remain visible. A green platform run alone does not mean
people were found. Integrations should check the summary and each row's outcome.

Reads and same-key writes have bounded retries. A lost write acknowledgement is
reconciled against the exact saved value. A missing redundant `RESULT` checkpoint
after a confirmed dataset write is reported in `storageWarnings`; restart recovers
that row from the dataset and the previously saved write marker. Dataset appends
and charges are never blindly repeated. Unconfirmed storage or billing operations
still stop safely, as do invalid input and unsafe pricing configurations.

Failed page reads are not reused across companies. Work observes both the input
time budget and Apify's actual timeout, reserving time for results and cleanup.
Cancellation attempts to abort outstanding children concurrently; any abort that
cannot be confirmed is reported in the summary. Platform outages, forced aborts
and container termination cannot be guaranteed recoverable.

Diagnostics contain exception types and code locations, not provider response
bodies or local values. Resume uses this run's original input and storage; it does
not charge or push completed rows again. A completed failed row stays failed on
resume; retry that company in a fresh run after correcting its cause.

### Pricing

One price list. Page reads, searches and email checks are included. The verified-email
event also covers a verified work email found by the profile fallback; an identity is
charged once for its email.

| What | Price |
| --- | --- |
| Person found | $20 / 1,000 ($0.02 each) |
| Verified work email (optional) | $30 / 1,000 ($0.03 each) |
| LinkedIn employee (optional) | $10 / 1,000 ($0.01 each) |
| Company where nobody is found | Free |

Typical costs per company:

| Request | Typical |
| --- | --- |
| Owners and executives (3 people) | $0.06 |
| The same with verified emails | $0.15 |
| Everyone from the website (20 people) | $0.40 |
| Everyone from the website and LinkedIn (25 people) | $0.65 |

The optional AI name filter (on by default) costs about $0.0006 per company, billed by Apify's AI
proxy. Set a **maximum charge** in the run options and the run never goes past it.

### Output

One row per company, in your order. The fields you will use most:

| Field | What it is |
| --- | --- |
| `people[]` | The people: `fullName`, `matchedRoles`, `associations` (title, evidence URL and quote), `contacts`, `profileUrls` |
| `outcome` | `people_found`, or why not: `no_supported_role_holder_published`, `sources_were_inaccessible`, `stopped_at_a_limit`, `business_not_identified` |
| `candidates[]` | People found but not returned, with the reason (another role, the per-company limit) |
| `businessContacts[]` | Company phones and mailboxes like `info@` |
| `coverage` | Roles found, roles not found, and what limited the search |

A person named on several pages comes back as one row, with the evidence from every page, and is
charged once.

### Good to know

- **Not every company publishes its people.** Small companies often list nobody; the row says so.
- **A title is a title.** "Purchasing Manager" says what someone does, not that they sign orders.
- **LinkedIn people are self-reported.** They come from LinkedIn listings, are marked as such, and
  rank below people the company publishes itself.
- **Catch-all domains** accept every address, so no email can be confirmed there and none is
  returned.

### Fair use

Only public pages are read, with no login. You are responsible for how you contact the people in
the results and for the privacy and marketing rules that apply to you, such as GDPR and CAN-SPAM.

### Related

- [Business Enrichment](https://apify.com/leadproof/business-enrichment) - a company's website,
  emails and phones from its name.
- Ready-made, verified local business lead lists: [leadproof.co](https://leadproof.co).

#### Recovery from transient storage errors

Reads and same-key checkpoint writes retry transient network errors, HTTP 408/429,
and HTTP 5xx responses up to three times, with 1s/2s backoff and a 20s total
window. Child-run polling retains its existing, longer deadline. Recovery covers
input/checkpoint reads, checkpoint and summary writes, and completed child dataset
pages. Cancellation and permanent errors propagate immediately.

Charges, child starts, and dataset appends are not blindly retried: a lost response
may mean the operation already succeeded. After a transient append error, the
actor reads its dataset: an exact match at the same input position confirms that
the result is durable, so processing continues without another append or charge.
`SUMMARY.recoveredPushAcknowledgements` counts these recoveries. An absent,
different, or unreadable row leaves the original error in place. Existing
checkpoints and restart reconciliation handle unresolved outcomes. Persistent outages
can still fail a run; recovery does not relabel failures as success.

# Actor input Schema

## `domains` (type: `array`):

One company domain per line, like acme.com. That is all a row needs.

## `targetRoles` (type: `array`):

Pick the roles, or Everyone for the whole team. Left empty: owners, founders and executives.

## `sources` (type: `array`):

Company website: the people the company publishes, each with the line that proves the role. LinkedIn: adds people from LinkedIn employee listings ($0.01 each). Pick both for the fullest list.

## `inferEmails` (type: `boolean`):

Works out each person's work email from the company's own address pattern and checks the mailbox. Only confirmed addresses are returned, $0.03 each. Never a generic info@.

## `maxPeoplePerBusiness` (type: `integer`):

Left empty: 5, or 100 when you pick Everyone.

## `businesses` (type: `array`):

One object per company: {inputId, name, website, city, region, country}. A row needs a website, or a name with a city, region or country.

## `findProfileLinks` (type: `boolean`):

One search per person without a profile link. Included in the price.

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

Checks that the addresses printed on the site accept mail. Included in the price.

## `includeFormer` (type: `boolean`):

Off: people a page describes as former are left out.

## `judgePeople` (type: `boolean`):

Removes testimonial authors, product names and headings, and reads the role of people the page names without a title. About $0.0006 per company, billed by Apify's AI proxy.

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

Two letters, like US or GB. Optional.

## `language` (type: `string`):

Like en or de.

## `renderMode` (type: `string`):

HTTP is fastest. Use Browser for sites built with JavaScript.

## `maxPagesPerBusiness` (type: `integer`):

Pages of each company site to read.

## `maxSearchesPerBusiness` (type: `integer`):

Used only when the site names nobody you asked for. 0 turns search off.

## `maxEmailChecksPerBusiness` (type: `integer`):

Most mailbox checks per company when finding emails.

## `maxProviderEmployees` (type: `integer`):

Most people asked from LinkedIn listings per company.

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

Rows still come back in your order.

## `maxSecondsPerBusiness` (type: `integer`):

A company that hits this returns what it found so far.

## `maxRunSeconds` (type: `integer`):

The run stops starting new companies after this long.

## `searchUnresolvedRoles` (type: `boolean`):

Off: one search only when the site named nobody. On: a search per role the site did not answer.

## `linkedinStaffSearch` (type: `boolean`):

Override. Left empty: on when LinkedIn is a source.

## `employeeProvider` (type: `string`):

Override. Left empty: Auto when LinkedIn is a source. Auto uses the company's LinkedIn page when the site links it, otherwise the domain.

## `maxProfileSearchesPerBusiness` (type: `integer`):

Most LinkedIn link searches per company.

## `maxPagesTotal` (type: `integer`):

Pages across the whole run.

## `maxSearchesTotal` (type: `integer`):

Searches across the whole run. 0 turns search off.

## Actor input object example

```json
{
  "domains": [
    "indsupply.com"
  ],
  "targetRoles": [
    "owner",
    "founder",
    "executive"
  ],
  "sources": [
    "website"
  ],
  "inferEmails": false,
  "maxPeoplePerBusiness": 3,
  "findProfileLinks": false,
  "verifyEmails": false,
  "includeFormer": false,
  "judgePeople": true,
  "language": "en",
  "renderMode": "http",
  "maxPagesPerBusiness": 3,
  "maxSearchesPerBusiness": 1,
  "maxEmailChecksPerBusiness": 12,
  "maxProviderEmployees": 50,
  "concurrency": 4,
  "maxSecondsPerBusiness": 180,
  "maxRunSeconds": 3600,
  "searchUnresolvedRoles": false,
  "maxProfileSearchesPerBusiness": 10,
  "maxPagesTotal": 200,
  "maxSearchesTotal": 60
}
```

# Actor output Schema

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

Full rows: people with their evidence and attributable contacts, candidates with reasons, business channels, coverage and source statuses.

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

One line per business: outcome, people selected, direct contacts, candidates, roles found and not found.

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

Counts by outcome, stop reason and dependency spend.

# 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 = {
    "domains": [
        "indsupply.com"
    ],
    "targetRoles": [
        "owner",
        "founder",
        "executive"
    ],
    "maxPeoplePerBusiness": 3,
    "maxPagesPerBusiness": 3,
    "maxSearchesPerBusiness": 0
};

// Run the Actor and wait for it to finish
const run = await client.actor("leadproof/company-people-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 = {
    "domains": ["indsupply.com"],
    "targetRoles": [
        "owner",
        "founder",
        "executive",
    ],
    "maxPeoplePerBusiness": 3,
    "maxPagesPerBusiness": 3,
    "maxSearchesPerBusiness": 0,
}

# Run the Actor and wait for it to finish
run = client.actor("leadproof/company-people-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 '{
  "domains": [
    "indsupply.com"
  ],
  "targetRoles": [
    "owner",
    "founder",
    "executive"
  ],
  "maxPeoplePerBusiness": 3,
  "maxPagesPerBusiness": 3,
  "maxSearchesPerBusiness": 0
}' |
apify call leadproof/company-people-finder --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,leadproof/company-people-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/l6u89JvfFa6CkSgsA/builds/jZhn0dA9CG1cBUZwl/openapi.json
