# People Search - B2B Decision Makers by Title & Industry (`leadproof/people-search`) Actor

B2B people search: find founders, heads of sales and other decision makers by job title, industry keyword and country. Each person's company website is read to confirm the industry, with the confirming line quoted. Optional verified work emails. No LinkedIn login, no data vendor.

- **URL**: https://apify.com/leadproof/people-search.md
- **Developed by:** [Lead Proof](https://apify.com/leadproof) (community)
- **Categories:** Lead generation, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $30.00 / 1,000 people

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

## B2B Decision-Maker Finder: Titles, Industry, Country, With Evidence

Give it **job titles**, **industry keywords** and **countries**. It returns the people who
hold those roles at companies in that industry, and for every person it shows **why**: the
line from their public profile that names the role, and the line from their company's own
website that confirms what the company does.

> Example: *"Founders and Heads of Sales at digital marketing agencies in the United
> Kingdom, 100 people, with verified work emails."*

You do not need a company list, a LinkedIn account or a data vendor. The Actor searches
Google's index of public profiles, reads each company's website, and returns only the
people every source supports. It never pads the list.

### What you get

A spreadsheet-ready row per person:

| Column | Example |
| --- | --- |
| `fullName` | Jane Example |
| `rawTitle` | Founder & CEO |
| `companyName` | Example Digital Ltd |
| `companyWebsite` | https://exampledigital.co.uk/ |
| `linkedInUrl` | https://www.linkedin.com/in/jane-example |
| `personLocationRaw` | London, England, United Kingdom |
| `email` | jane@exampledigital.co.uk (with Personal emails on) |
| `emailVerificationStatus` | ok |
| `qualification.conditions.industry.evidence` | "Example Digital - SEO & PPC Agency for Local Businesses" (from the company's home page) |

Also in every run:

- **Candidates** (a separate dataset, never charged): people who were found but not
  returned, each with what was missing: another place, a title further than asked, or a
  company whose website could not confirm the industry.
- **Companies** (a separate dataset): every company checked, its website, the industry and
  selling-to-businesses readings with their quotes, and any website set aside because it
  belonged to a different company.
- **A run summary** (`SUMMARY` in the key-value store): counts, what was searched, what was
  charged, and why the run stopped.

### How a person is accepted

1. **The role.** The title on the person's public profile, as Google indexed it, matches a
   requested title or a close synonym at the same level (Head of Sales also finds Sales
   Director; Title matching controls this).
2. **The place.** The profile's location line is in a requested country, region or city.
3. **The company.** The company's own website is found and read (home page and a few pages it
   links to). An AI model that only answers structured questions decides whether the
   company offers the requested keyword, and which lines of the site say so; that line is
   quoted in the row. A website that turns out to belong to another company is set aside
   and the next one tried.
4. **Optional: sells to businesses.** With *Companies must sell to businesses* on, the
   company's pages must also show business customers, again with a quote.

A person who passes 1 and 2 but whose company could not be confirmed either way is a
**candidate**, not a result. When the company's website cannot be checked (none found, or
the run's company limit reached), the keyword may still be confirmed by the company's name or
the person's own profile line; the row then has no `companyWebsite`, its evidence names that
source, and `qualification.companyCheck` says why the site was not read.

### Input

The main fields:

| Field | What it does |
| --- | --- |
| Job titles | Up to 10 roles, for example Founder, Managing Director, Head of Sales. |
| Locations | Up to 5, each a country code with an optional region or city. Large countries are searched region by region. |
| Industry keywords | Up to 20, for example "digital marketing agency", "dental supplies", "cybersecurity". Each is searched and confirmed on its own. |
| Maximum people | How many to return. Search effort and run time grow with it. |
| Personal emails | Find and verify a personal work email for each person returned. |
| Companies must sell to businesses | Keep only B2B companies. |

The **Advanced** section holds filters (industries, companies to include or exclude),
strictness (how far a title may be from the requested one, whether keywords are required)
and limits (searches, companies, time). The defaults suit most requests.

Example input:

```json
{
  "titles": ["Founder", "Managing Director", "Head of Sales"],
  "locations": [{ "countryCode": "GB" }],
  "keywords": ["digital marketing agency", "SEO agency", "web design agency"],
  "maxPeople": 100,
  "personalEmails": true
}
```

### Personal emails

With *Personal emails* on, each person returned gets a work email when one can be verified:
first one the company publishes beside the person's name, else the common patterns built from
the name and the company's domain (first.last@, first@, flast@ and others), each checked
for deliverability until one accepts mail.

- Only an address whose mailbox accepts mail is returned.
- A domain that accepts every address (catch-all) returns nothing, because no address on it
  can be verified.
- Company mailboxes such as info@ or sales@ are never put in the person's email.

### Pricing

You pay per event, and only for what you get:

- **Person**: each person written to the results dataset.
- **Personal email**: each verified personal email found (only with Personal emails on).

Candidates, the companies dataset and the summary are free. The prices are on the Pricing
tab. Your run's maximum charge is respected: when it is reached the run stops and the rest
of the people it found go to the candidates dataset.

### How many people to expect

The Actor returns the people public sources support, not a fixed number. A narrow industry
in a small country may yield a few dozen; a broad one in a large country, hundreds. In a
test for UK agency founders and heads of sales it returned 62 confirmed people and 125
more whose company could not be read either way, in 25 minutes. A request can finish with
fewer people than asked for; the summary's `stopReason` says why (for example
`query_plan_exhausted`: every planned search ran).

Runs get faster and cheaper over time for common requests, because profile lines and
company pages collected by earlier runs are reused within their age limits and judged again.

### Languages and countries

Any country can be searched. The best results today are in English-language markets (United
States, United Kingdom, Canada, Australia, Ireland and similar), because titles and industry
keywords are matched as written. Elsewhere, write the titles and keywords the way people and
companies there write them, for example "Geschäftsführer" and "Zahnarztbedarf" for Germany.

### What it will not tell you

- **Everyone in a role.** Only people whose public profile Google has indexed with the title
  and place.
- **That a role is current today.** The indexed profile presents it as current; that is the
  basis given, not a verified employment record.
- **Who approves a purchase.** A title names a function, not authority.
- **A home address or a personal phone.** Only what professional sources publish.

### Fair use

Only public pages are read. Profiles are not opened, no account or cookie is used, and no
login wall or challenge is bypassed. You are responsible for how you contact the people
returned and for the privacy and marketing rules that apply to you, such as GDPR and
CAN-SPAM.

# Actor input Schema

## `titles` (type: `array`):

The roles to find, written the way people are titled, for example "Founder", "Head of Sales" or "Purchasing Manager". Up to 10. Related titles at the same level are found too (see Title matching).

## `locations` (type: `array`):

Where the people work. Each entry is an object with countryCode (two letters, required) and optionally region and city, for example {"countryCode": "GB"} or {"countryCode": "US", "region": "TX"}. Up to 5. A large country is searched region by region as the list grows.

## `keywords` (type: `array`):

What the company should do, for example "digital marketing agency", "dental supplies" or "cybersecurity". Each keyword is searched on its own, and each person's company website is read to confirm it, with the confirming line quoted in the row. Up to 20. Leave empty to find the titles in any industry.

## `maxPeople` (type: `integer`):

How many people to return, across all titles and locations. Only people whose title, place and company are all supported by a quoted source are returned, so a run can return fewer than this; it never pads the list. Search effort and time grow with this number.

## `personalEmails` (type: `boolean`):

Find a personal work email for each person returned: one the company publishes beside the person's name, else the common patterns from the name and the company's domain (first.last@, first@, flast@ and others), each checked for deliverability until one accepts mail. Only a mailbox that accepts mail is returned. A domain that accepts every address (catch-all) returns nothing. Company mailboxes such as info@ are never returned. Each verified email is charged as its own event.

## `requireB2bEvidence` (type: `boolean`):

Return only people whose company's own pages show it sells to businesses (wholesale, distributors, professional customers, agencies, clinics and the like), with the line quoted. Off: selling to businesses is still read and reported, but not required.

## `titleMatchMode` (type: `string`):

related (default): the requested titles and their synonyms at the same level, for example Head of Sales also finds Sales Director. exact: only the requested titles, after normalizing case, punctuation and abbreviations.

## `linkedInMode` (type: `string`):

How people are found: Google's index of public profiles, searched by title, keyword and place; the profiles themselves are never opened and no account is used. The only method offered.

## `locationScope` (type: `string`):

organization\_location (default in this preview): the person is proven to work for an organization (role, affiliation and current status as strict as ever), and the organization's own pages prove it is located in the place: its structured address, a head-office statement, or its only published address. A branch or office of the organization in the place is not enough, because it does not place every employee there. Each row says which basis it matched on and never copies the organization's location into the person's. person\_work\_location: only a statement about the person (their own entry, their structured work location, or a roster of their branch) places them; the organization's location never counts.

## `maxTitleCloseness` (type: `string`):

profile\_search only. close allows a synonym, a seniority word or a scope qualifier; broad also a level equivalent or a combined or adjacent function. A person whose title stands further away is a candidate (title\_further\_than\_asked), never a result. titleMatchMode exact stays the strictest.

## `industries` (type: `array`):

Optional. Only companies whose own pages support one of these industries: agriculture, manufacturing, healthcare, education, government, financial\_services, construction, retail, hospitality, logistics, energy, telecommunications, nonprofit. A company whose industry is unknown does not qualify and is listed as a candidate.

## `excludeIndustries` (type: `array`):

Optional. Leave out companies whose own pages support one of these industries. Same names as above. Exclusion wins over inclusion.

## `includeCompanies` (type: `array`):

Optional. Only these companies. Each entry is a name, a domain, or {"name": "Acme Ltd", "domain": "acme.co.ke"}. A domain is the most reliable identifier. A company matches only as the same entity: a parent company does not match its subsidiaries.

## `excludeCompanies` (type: `array`):

Optional. Leave out these companies, in the same shape as above. A company both included and excluded is refused. A company that only shares part of its name with an excluded one is held back as a candidate rather than returned.

## `checkCompanies` (type: `boolean`):

Read each person's company website once (home page and a few pages it links to) to confirm the industry keywords and whether it sells to businesses, with quotes. Off: only the company name and the indexed profile line are used, and far fewer people qualify.

## `requireIndustryEvidence` (type: `boolean`):

A person is returned only when a keyword is confirmed by a quoted line from the company name, the profile line or the company's own pages. Off: keywords steer the search but are not required.

## `minB2bStrength` (type: `string`):

profile\_search only, with requireB2bEvidence. strong keeps as results only people whose company has a line naming a trade channel outright (wholesale, distributor, private label, reseller, retailers, key accounts); a line that only says the trade is served (for professionals) leaves them candidates with b2b\_weak.

## `judgeCompanies` (type: `boolean`):

An AI model that answers only structured questions reads each checked company's pages and decides what the company is to the keywords and whom it sells to; the lines it accepts are quoted as the evidence. It also sets aside a website that belongs to another company and tries the next one. Off, or unavailable: fixed word rules read the pages.

## `includeCandidates` (type: `boolean`):

Also write the people found but not returned (another place, a title further than asked, a company that could not be confirmed) to a separate candidates dataset, each with what was missing. Candidates are never charged.

## `maxSearchQueries` (type: `integer`):

Upper bound on searches for the whole run. When empty, twice Maximum people (at least 12).

## `maxOrganizations` (type: `integer`):

Upper bound on company websites checked. When empty, four times Maximum people (at least 20).

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

No new search or page read starts after this many seconds. When empty, twelve seconds per person asked for (at least 15 minutes, at most 6 hours). People are written as they qualify, so the first ones arrive within a minute or two.

## `maxDependencyChargeUsd` (type: `number`):

Upper bound on what the searches and page reads of this run may cost the Actor. When empty, 3 cents per person asked for (at least $0.50, at most $50). It is not what you are charged: you pay only for the people returned and the emails found.

## `cache` (type: `object`):

Reuse what earlier runs collected: result pages (serpDays), company pages (companyDays) and indexed profile lines (profileDays), each within its age limit and judged again by this run. mode: read\_write (default), read\_only, write\_only or off. Faster and cheaper; off always searches afresh.

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

Search interface language, for example en or sw.

## Actor input object example

```json
{
  "titles": [
    "Founder",
    "Head of Sales"
  ],
  "locations": [
    {
      "countryCode": "GB"
    }
  ],
  "keywords": [
    "digital marketing agency"
  ],
  "maxPeople": 20,
  "personalEmails": false,
  "requireB2bEvidence": false,
  "titleMatchMode": "related",
  "linkedInMode": "profile_search",
  "locationScope": "organization_location",
  "maxTitleCloseness": "broad",
  "industries": [],
  "excludeIndustries": [],
  "includeCompanies": [],
  "excludeCompanies": [],
  "checkCompanies": true,
  "requireIndustryEvidence": true,
  "minB2bStrength": "weak",
  "judgeCompanies": true,
  "includeCandidates": true,
  "cache": {
    "mode": "read_write",
    "serpDays": 14,
    "companyDays": 60,
    "profileDays": 30
  },
  "language": "en"
}
```

# Actor output Schema

## `people` (type: `string`):

Accepted people with their evidence.

## `peopleCsv` (type: `string`):

One line per accepted person.

## `candidates` (type: `string`):

Found but not accepted, each with the missing evidence. Not part of the people list.

## `candidatesCsv` (type: `string`):

One line per candidate.

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

profile\_search only: each distinct company once, its website, what its home page says about the keywords and about selling to businesses, with quotes, and the accepted people it names.

## `companiesCsv` (type: `string`):

One line per company checked.

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

Requested and returned counts, coverage per title and location, stop reason, skipped work, source failures and costs.

# 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 = {
    "titles": [
        "Founder",
        "Head of Sales"
    ],
    "locations": [
        {
            "countryCode": "GB"
        }
    ],
    "keywords": [
        "digital marketing agency"
    ],
    "cache": {
        "mode": "read_write",
        "serpDays": 14,
        "companyDays": 60,
        "profileDays": 30
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("leadproof/people-search").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 = {
    "titles": [
        "Founder",
        "Head of Sales",
    ],
    "locations": [{ "countryCode": "GB" }],
    "keywords": ["digital marketing agency"],
    "cache": {
        "mode": "read_write",
        "serpDays": 14,
        "companyDays": 60,
        "profileDays": 30,
    },
}

# Run the Actor and wait for it to finish
run = client.actor("leadproof/people-search").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 '{
  "titles": [
    "Founder",
    "Head of Sales"
  ],
  "locations": [
    {
      "countryCode": "GB"
    }
  ],
  "keywords": [
    "digital marketing agency"
  ],
  "cache": {
    "mode": "read_write",
    "serpDays": 14,
    "companyDays": 60,
    "profileDays": 30
  }
}' |
apify call leadproof/people-search --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,leadproof/people-search"
        }
    }
}
```

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/m584dFqTSacFulEFU/builds/mmSViDipzLpsqaqDJ/openapi.json
