# B2B People Search API - Email and Mobile Number Enrichment (`nabeelbaghoor/b2b-people-search-api`) Actor

Search a B2B database of people and companies by job title, seniority, department, country, industry, headcount, revenue, technologies and funding, then enrich them in bulk with verified work email addresses, mobile phone numbers and job history. Pay per result. Bring your own API key.

- **URL**: https://apify.com/nabeelbaghoor/b2b-people-search-api.md
- **Developed by:** [Nabeel Hassan](https://apify.com/nabeelbaghoor) (community)
- **Categories:** Business, Developer tools, Lead generation
- **Stats:** 2 total users, 1 monthly users, 33.3% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$15.00 / 1,000 results

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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## B2B People Search API - Email and Mobile Number Enrichment

Search a B2B database of people and companies by job title, seniority, department, country, industry, headcount, revenue, technologies and funding, then enrich the results in bulk with verified work email addresses, mobile phone numbers and job history. One flat row per person or company.

### What this actor does

- **Separates finding people from reaching them.** Search tells you who exists and what they do; enrichment appends the addresses and phone numbers. They draw on different credit pools, so searching first and enriching only the people who survive your filters is what keeps a list cheap.
- **Enriches in real batches.** The provider takes up to 10,000 people or 500 companies in a single job, so enriching a large list is a handful of jobs rather than one request per person. The actor submits a batch, waits for it, reports the percentage complete as it goes, and writes the records out when it finishes.
- **Pairs answers back by identifier, never by position.** An enrichment can return fewer records than were submitted. Every record carries the identifier it was sent with, so a dropped record leaves a visible gap instead of shifting one person's email address onto the next person's row.
- **Lists the provider's own filter vocabulary.** Seniorities, departments, industries and technology categories are values this provider defines and updates. Run the filters mode once and every accepted value lands in a dataset, so your filters use the provider's spelling rather than your guess at it.
- **Filters on both halves of the picture.** Person filters cover title, exact title, seniority, department, country, state, previous employer and how recently the person changed job. Company filters cover domain, name, industry, headcount, revenue, technologies, technology categories, keywords, NAICS code, founding year and department headcount, each with an exclude list.
- **Caps how many people come from one company.** A single large employer can otherwise fill an entire result set, so results per company is a first class filter.
- **Returns ranked contact details, not just the first one.** Addresses arrive with a type and a validation status, mobile numbers with a confidence score. The best of each is lifted into a leading column and the full ranked lists are kept, because the second address is often the one that works when the first bounces.
- **Never charges for a miss.** Only rows carrying a resolved person or company are billed.

### Input

| Field | What it does |
| --- | --- |
| `mode` | People search, company search, people enrichment, company enrichment, filters or credits. |
| `jobTitles`, `exactJobTitles` | Loose or literal title matching. |
| `seniorities`, `departments` | Provider vocabulary, listed by the filters mode. |
| `personCountries`, `personStates` | Where the person is, as `us` or `us:CA`. |
| `previousCompanyDomains` | People who used to work somewhere. |
| `jobChangePeriodInDays` | Only people who changed job recently. |
| `peoplePerCompany` | Cap results from any one employer. |
| `companyDomains`, `companyNames`, `companyCountries` | Which companies to search or enrich. |
| `industries`, `naicsCodes` | Industry filters, provider labels or official codes. |
| `employeeCountFrom/To`, `revenueFrom/To`, `yearFoundedFrom/To` | Firmographic ranges. |
| `technologies`, `technologyCategories` | Technographic filters, with exclusions. |
| `departmentSizes` | Named department headcount, as `sales:10-50`. |
| `profileUrls`, `fullNames` | Who to enrich. |
| `includeEmail`, `includeMobile`, `includeProfileUrl`, `includeJobHistory` | What enrichment returns. |
| `acceptedEmailType` | Professional or personal addresses. |
| `skipMobileWithoutEmail` | Do not spend a mobile credit on a thin record. |
| `pageSize`, `batchSize`, `jobTimeoutSeconds` | Paging and batching behaviour. |
| `requestsPerMinute` | Pace the run under your plan's rate limit. |
| `skipNotFound` | Leave unresolved inputs out of the dataset. |
| `maxResults` | Hard cap on rows, and therefore on spend and run time. |
| `apiKey` | Your own API key. Stored as a secret. |

### Example output

```json
{
  "found": true,
  "mode": "peopleEnrich",
  "query": "https://www.linkedin.com/in/example",
  "firstName": "Jane",
  "lastName": "Doe",
  "fullName": "Jane Doe",
  "jobTitle": "Head of Sales",
  "seniorities": ["Director"],
  "departments": ["Sales"],
  "email": "jane.doe@example.com",
  "emailType": "professional",
  "emailValidationStatus": "VALID",
  "mobilePhone": "+14155550123",
  "mobileConfidence": 0.92,
  "linkedinUrl": "https://www.linkedin.com/in/example",
  "city": "San Francisco",
  "state": "California",
  "country": "United States",
  "companyName": "Example Inc",
  "companyDomain": "example.com",
  "jobHistory": [
    {
      "companyName": "Example Inc",
      "jobTitle": "Head of Sales",
      "startDate": "2023-02-01",
      "endDate": null
    }
  ],
  "recordStatus": "COMPLETED",
  "error": null,
  "raw": { }
}
```

### Frequently asked questions

#### What is a B2B people search API?

A B2B people search API is a query interface over a database of working professionals and their employers. You describe the people you want with filters such as job title, seniority, department, country, and the size, industry, revenue or technology stack of the company they work for, and the API returns everyone who matches. This actor exposes those filters as actor input and writes one row per person to a dataset. Contact details are a separate step: search returns who someone is and where they work, and enrichment returns how to reach them.

#### How do I get verified work email addresses and mobile numbers?

Run the people enrichment mode with a list of profile URLs, or with full names paired with a company domain. Turn on `includeEmail` for work addresses and `includeMobile` for mobile numbers. Each address comes back with a type and a validation status, and each mobile number with a confidence score, so you can drop anything below your own threshold before sending. Email and mobile draw on separate credit pools, which is why mobile is off by default: turn it on only when someone will actually dial.

#### How many records can I enrich in one run?

The provider accepts up to 10,000 people or 500 companies per enrichment job, and the actor batches automatically up to whichever ceiling applies to the mode you are running. Larger batches are cheaper in requests and slower to come back, so the actor polls with a widening interval and reports the percentage complete rather than going quiet. Set `batchSize` smaller if you would rather see rows land sooner, and raise `jobTimeoutSeconds` if you are enriching tens of thousands of records.

#### Why do my seniority or industry filters return nothing?

Because those filters take the provider's own vocabulary, not free text. A value the provider does not recognise matches nothing rather than being ignored. Run the `filters` mode once: it writes every accepted seniority, department, industry, technology category, headcount band and revenue band to the dataset, one row per value. Copy the spellings from there. This actor deliberately does not hard code those lists, because the provider updates them and a stale dropdown would start rejecting values that are still valid.

#### What is the difference between job titles and exact job titles?

`jobTitles` is matched loosely: the provider expands it by acronym and by meaning, so "VP Sales" also reaches "Vice President of Sales" and similar variants. That is usually what you want, since title text is written differently at every company. `exactJobTitles` matches the literal string only. Use it when the loose match is pulling in adjacent roles, for example when "head of sales" keeps returning sales operations and sales enablement people you do not sell to.

#### Can I find companies rather than people?

Yes. Company search takes the firmographic and technographic half of the filter set on its own and returns accounts with headcount, revenue, industries and NAICS classification. Company enrichment goes the other way: give it domains you already have and it appends the description, headcount, revenue, industry, founding year, headquarters, social profiles, parent organisation, stock listing and the full funding round history, with the most recent round lifted into its own columns. A common two step run is to search for accounts, take the domains out of the dataset, and feed them back in as the company domain filter on a people search.

#### Do I need my own API key?

Yes. This actor is bring your own key: it calls the people and company data API with credentials from your own account, so your plan governs your credits, your rate limit and your data rights. Put the key in the `apiKey` input, where it is stored as a secret, or supply it as the `DATA_API_KEY` environment secret. The actor's own charge is separate and is per row resolved, and rows that resolved to nothing are never charged for.

### Keyword map

B2B people search API, contact enrichment API, work email finder, mobile phone enrichment, bulk lead enrichment, company search API, firmographic filters, technographic filters, job change signals, NAICS industry search, sales prospecting data, ICP account list building.

# Actor input Schema

## `mode` (type: `string`):

People search finds the people matching your filters. Company search finds the accounts. People enrichment takes people you already have and appends verified work email addresses and mobile numbers. Company enrichment takes domains and appends firmographics and funding. Filters lists the values this provider accepts for the list filters below. Credits reports the balance left on your key.

## `jobTitles` (type: `array`):

Job titles to match, one per line. The provider expands these by acronym and meaning, so "VP Sales" also reaches "Vice President of Sales". Use exact job titles below when you need the literal string instead.

## `exactJobTitles` (type: `array`):

Job titles to match literally, one per line, with no expansion by acronym or meaning. Use this when the loose match pulls in adjacent roles you do not want.

## `seniorities` (type: `array`):

Seniority levels to keep, one per line, such as Founder, C-Level or Director. This provider serves its own list of accepted values: run the filters mode once to write them all to a dataset, then copy the ones you need.

## `departments` (type: `array`):

Departments to keep, one per line. Run the filters mode to see every value this provider accepts.

## `personCountries` (type: `array`):

Two letter country codes for where the person is, one per line, such as us, gb or fr. Case does not matter.

## `personCountriesExcluded` (type: `array`):

Two letter country codes to rule out, one per line.

## `personStates` (type: `array`):

States or regions the person is in, one per line, written as country:code, for example us:CA. The country prefix is required because a bare state code means different places in different countries.

## `personStatesExcluded` (type: `array`):

States to rule out, one per line, written the same way as above.

## `previousCompanyDomains` (type: `array`):

Only people who used to work at these company domains, one per line. Alumni of an account you already sell to are usually the warmest route into a new one.

## `jobChangePeriodInDays` (type: `integer`):

Only people who changed job inside this many days. Someone ninety days into a new role is still choosing their tools, which is why this filter outperforms most others on reply rate.

## `peoplePerCompany` (type: `integer`):

Cap how many people come back from any one company, up to 40. This is what stops a single large employer filling the whole result set.

## `companyDomains` (type: `array`):

Company domains, one per line. In the searches this restricts results to these companies. In people enrichment it identifies the employer alongside a name. In company enrichment it is the list of companies to enrich. A pasted URL is reduced to the bare hostname.

## `companyDomainsExcluded` (type: `array`):

Company domains to rule out, one per line, such as your own and your existing customers.

## `companyNames` (type: `array`):

Company names, one per line, for when you know the employer but not the domain.

## `companyCountries` (type: `array`):

Two letter country codes for where the company is, one per line. Use this rather than person country when the buying decision sits at headquarters.

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

Industries to keep, one per line. Run the filters mode to see every value this provider accepts.

## `industriesExcluded` (type: `array`):

Industries to rule out, one per line.

## `employeeCountFrom` (type: `integer`):

Smallest company headcount to include.

## `employeeCountTo` (type: `integer`):

Largest company headcount to include.

## `revenueFrom` (type: `integer`):

Smallest company revenue to include, as a plain number.

## `revenueTo` (type: `integer`):

Largest company revenue to include, as a plain number.

## `technologies` (type: `array`):

Only companies running these technologies, one per line. Technographics are what turn a generic list into one where the first line of the email is about something the buyer already owns.

## `technologiesExcluded` (type: `array`):

Rule out companies running these technologies, one per line, such as a competitor they already bought.

## `technologyCategories` (type: `array`):

Whole categories of technology rather than named products, one per line. Run the filters mode to see the accepted values.

## `technologyCategoriesExcluded` (type: `array`):

Technology categories to rule out, one per line.

## `companyKeywords` (type: `array`):

Words that must appear in the company's description, one per line.

## `companyKeywordsExcluded` (type: `array`):

Words that must not appear in the company's description, one per line.

## `naicsCodes` (type: `array`):

NAICS industry codes to include, one per line. Use these when you need an official classification rather than the provider's own industry labels.

## `naicsCodesExcluded` (type: `array`):

NAICS industry codes to rule out, one per line.

## `yearFoundedFrom` (type: `integer`):

Earliest founding year to include.

## `yearFoundedTo` (type: `integer`):

Latest founding year to include.

## `departmentSizes` (type: `array`):

Size of a named department, one per line, written as department:min-max, for example sales:10-50. A company with fifty engineers and two marketers is a different buyer from the reverse, even at the same total headcount.

## `profileUrls` (type: `array`):

Profile URLs to enrich, one per line. This is the most accurate input for people enrichment, because it identifies exactly one person rather than a name that several people share. Tracking parameters are stripped automatically.

## `fullNames` (type: `array`):

Full names to enrich, one per line. A name alone cannot be looked up, so each name is paired with each company domain or company name above: ten names and one company is ten records. The last word is treated as the surname.

## `includeEmail` (type: `boolean`):

Ask the provider for work email addresses. On by default, since an enrichment with no address is rarely what anyone wanted.

## `includeMobile` (type: `boolean`):

Ask the provider for mobile phone numbers. These come out of a separate credit pool from email, so leave this off unless you will call.

## `includeProfileUrl` (type: `boolean`):

Ask the provider to resolve each person's profile URL.

## `includeJobHistory` (type: `boolean`):

Ask the provider for each person's previous roles, with company, title and dates. This is how you tell a two month tenure from a ten year one before you write the first line.

## `acceptedEmailType` (type: `string`):

Restrict enrichment to professional or to personal addresses. Leave empty to accept whatever the provider finds. Contacting people at a personal address needs a lawful basis of its own.

## `skipMobileWithoutEmail` (type: `boolean`):

Do not spend a mobile credit on a person whose email address could not be found. Failing to find an address usually means the record is thin, and the mobile lookup rarely rescues it.

## `pageSize` (type: `integer`):

How many records the provider returns per search request, up to 200. Larger pages mean fewer requests against your rate limit.

## `batchSize` (type: `integer`):

How many records go into one enrichment job. The provider accepts up to 10000 people or 500 companies per job, and the ceiling for the mode you are running is applied automatically. Leave empty to use the maximum.

## `jobTimeoutSeconds` (type: `integer`):

How long to wait for one enrichment job to finish before giving up. Large batches take minutes, so this needs headroom; the actor polls with a widening interval and reports the percentage complete as it waits.

## `requestsPerMinute` (type: `integer`):

Pace the run under your plan's rate limit. This provider allows a small number of requests per second, so the actor spaces them evenly rather than sending bursts.

## `skipNotFound` (type: `boolean`):

Leave inputs that resolved to nothing out of the dataset. They are kept by default, because knowing which people the provider has nothing on is what makes a run auditable. Unresolved rows are never charged for either way.

## `maxResults` (type: `integer`):

Hard cap on rows, and therefore on spend and run time.

## `apiKey` (type: `string`):

Your own API key for the people and company data API, taken from your provider account. Stored as a secret and never written to a log. You can also supply it as the DATA\_API\_KEY environment secret instead.

## Actor input object example

```json
{
  "mode": "peopleSearch",
  "jobTitles": [
    "head of sales"
  ],
  "includeEmail": true,
  "includeMobile": false,
  "includeProfileUrl": false,
  "includeJobHistory": false,
  "acceptedEmailType": "",
  "skipMobileWithoutEmail": false,
  "pageSize": 200,
  "jobTimeoutSeconds": 900,
  "requestsPerMinute": 300,
  "skipNotFound": false,
  "maxResults": 100
}
```

# Actor output Schema

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

People and companies found or enriched, one row each.

# 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 = {
    "jobTitles": [
        "head of sales"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("nabeelbaghoor/b2b-people-search-api").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 = { "jobTitles": ["head of sales"] }

# Run the Actor and wait for it to finish
run = client.actor("nabeelbaghoor/b2b-people-search-api").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 '{
  "jobTitles": [
    "head of sales"
  ]
}' |
apify call nabeelbaghoor/b2b-people-search-api --silent --output-dataset

```

## MCP server setup

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

```

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/hSQ9XIhghRBn8HU0q/builds/u5aX7IQF6vfvrVnVD/openapi.json
