# B2B Contact Search API - Find People, Emails and Phones (`nabeelbaghoor/b2b-contact-search-api`) Actor

Search a B2B database of people and companies by job title, seniority, department, industry, headcount, revenue and funding, or enrich leads you already have from a LinkedIn URL or email. Returns the person, their company firmographics, and optionally a verified email and phone number.

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

## Pricing

$20.00 / 1,000 contact returneds

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 Contact Search API - Find People, Emails and Phones

Build a targeted B2B prospect list by job title, seniority, industry, headcount, revenue and funding stage, or enrich the leads you already have from nothing but a LinkedIn URL.

### What it does

- **Two ways to work, one output shape.** Search pages through everybody matching your filters and builds a new list. Enrich takes leads you already have and fills them in. Both produce the same columns, so they compose.
- **Filters that match how territories are actually drawn.** Job title, seniority band, department, person location, company name, domain, industry, headcount band, revenue band, company type and B2B or B2C focus.
- **Reveals verified emails and direct phone numbers.** Both are opt in, because both spend credits on top of the lookup, and both are off until you ask.
- **Flags Do Not Call numbers.** When your account has DNC screening enabled, numbers on a Do Not Call list are marked on the row rather than left for you to discover later.
- **Returns company firmographics on every contact.** Industry, headcount, estimated revenue, funding total and stage, founded year, technologies detected and LinkedIn URL, so a contact row is usable without a second enrichment pass.
- **Handles the slow lookups properly.** A reveal that has not finished is not a failure at this provider, it is a wait. The actor waits out the documented retry window instead of throwing the lead away.
- **Takes messy lead lists.** A bare LinkedIn URL, a bare email address, or a spreadsheet row with `full_name` and `company` columns all work.
- **Keeps the misses.** A lead nobody could match still produces a row saying so, and is not charged for, so you can see which part of your list is unknown.
- **Reaches filters this form does not show.** An additional filters object passes anything else the provider supports straight through under its own name.

### Input

#### Search mode: build a list

```json
{
  "mode": "search",
  "personTitles": ["VP of Sales", "Head of Revenue"],
  "personSeniorities": ["VP", "C-Suite"],
  "personLocations": ["United States"],
  "companyIndustries": ["Software Development"],
  "companySizes": ["201-500 employees", "501-1000 employees"],
  "companyRevenue": ["$10M-$100M"],
  "maxResults": 500
}
```

#### Enrich mode: fill in a list you already have

```json
{
  "mode": "enrich",
  "leads": [
    { "linkedin_url": "https://www.linkedin.com/in/example-person" },
    { "email": "person@example.com" },
    { "name": "Jane Doe", "title": "CEO", "company_domain": "example.com" }
  ],
  "revealEmail": true,
  "revealPhoneNumbers": false
}
```

### Example output

One row per person.

```json
{
  "found": true,
  "id": "84d31ab0-bac0-46ea-9a8b-b8721126d3d6",
  "name": "Jane Doe",
  "firstName": "Jane",
  "lastName": "Doe",
  "title": "VP of Sales",
  "headline": "VP Sales at Example",
  "email": "jane.doe@example.com",
  "linkedinUrl": "https://www.linkedin.com/in/example-person",
  "location": "San Francisco, California, United States",
  "phoneNumbers": ["+1 4155550123"],
  "phoneTypes": ["mobile"],
  "doNotCall": false,
  "companyName": "Example",
  "companyWebsite": "https://example.com",
  "companyLinkedinUrl": "https://www.linkedin.com/company/example",
  "companyIndustry": "Software Development",
  "companySize": "201-500 employees",
  "companyEmployees": 500,
  "companyLocation": "San Francisco, California, US",
  "companyFoundedYear": 2015,
  "companyType": "Privately Held",
  "companyRevenue": "$10M-$100M",
  "companyLatestFundingStage": "series_b",
  "companyTechnologies": ["Salesforce"],
  "companyIsB2b": true,
  "companyIsB2c": false
}
```

### Frequently asked questions

#### What is the difference between search mode and enrich mode?

Search mode takes filters and returns everybody in the database who matches them, paging until it hits your row limit. Use it to build a prospect list from scratch. Enrich mode takes leads you already have, one identifier each, and returns the full record for each one. Use it to fill in a CRM export or a list of LinkedIn URLs. The two read different parts of the input form and produce identical columns.

#### Can I get email addresses?

Yes, in enrich mode, by turning on the reveal email option. It is off by default because revealing an email spends email credits at the provider on top of the lookup itself. Search mode returns profile and company data without contact details, so the usual pattern is to search first, look at what you got, then run the results back through enrich mode with reveals turned on.

#### Can I get direct phone numbers?

Yes, in enrich mode, by turning on the reveal phone numbers option. Each number comes back with its type, so a mobile can be told from a switchboard line. If your account has Do Not Call screening enabled, numbers on a DNC list are flagged on the row.

#### What can I filter by?

Job title, exact or partial, seniority band, department, person keywords from the headline and about section, person location and excluded locations, company name, company domain, industry, headcount band, revenue band, company type, B2B or B2C focus, company location and company description keywords. Filters this form does not show, such as last funding round, headcount growth, open job postings, recent news and investor overlap, go in the additional filters object under the provider's own names.

#### Why are seniority and company size dropdowns instead of free text?

Because the provider only accepts its own vocabulary for them, exactly as written, and an unrecognised value is ignored rather than rejected. A typo would silently widen the search instead of failing, which is the worst outcome, so those filters are dropdowns of the real values.

#### What identifiers can I enrich a lead from?

A LinkedIn profile URL, an email address, or a person's name together with either a company name or a company domain. A lead with none of those is refused before it is sent, so it costs nothing, and it still appears in the output with the reason.

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

Yes. This actor is bring your own key: it calls the contact data API using your own account key, so you keep your own credit balance, your own rate limits and your own data agreement. Generate the key in the provider's dashboard under its API section.

#### What happens if the provider has no record of a lead?

The lead still produces a row, with `found` set to false and a note saying no match exists. That row is not charged for. Knowing which of your leads are unknown is usually worth as much as the ones that resolved.

#### How much does a run cost?

Charging is per row, and only rows carrying a real person are charged. Leads with no match, leads refused before sending and leads the provider would not process are all free. Note that revealing emails and phone numbers also spends credits on your own account at the provider, separately from what is charged here. Set `maxResults` to cap the rows a run can produce.

#### What are the rate limits?

The provider allows 300 search requests a minute and 350 lookups a minute. This actor paces itself at 120 a minute by default, which leaves headroom on a shared account. Raise it if the account is yours alone, lower it if you are running several jobs at once.

### Keyword map

B2B contact search API, B2B data API, lead enrichment API, contact enrichment API, email finder API, find work email, direct dial phone number API, LinkedIn URL to email, prospect list building, sales prospecting data, firmographic data API, company enrichment API, job title search, seniority filter, headcount filter, revenue band filter, technographic data, funding stage data, ICP list building, outbound lead lists

# Actor input Schema

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

Search builds a new list: give it filters and it pages through everybody who matches. Enrich fills in a list you already have: give it leads and it looks each one up. The two read different parts of this form, so fields belonging to the other mode are ignored rather than causing an error.

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

Your own account's API key for this provider, generated in its dashboard under the API section. Bring your own key: nothing is shared between runs and the key is never written to the dataset.

## `personTitles` (type: `array`):

Job titles to match, one per line. Matches any of them. Partial titles work, so Engineer matches Software Engineer and Engineering Manager unless exact title matching is turned on.

## `personSeniorities` (type: `array`):

Seniority bands to match, using this provider's own vocabulary. Matches any of them.

## `personDepartments` (type: `array`):

Departments to match, one per line, using this provider's own department names. Check the provider's supported departments list before using this, because an unrecognised name is ignored rather than rejected and quietly widens the search.

## `personLocations` (type: `array`):

Where the person is, one per line, written the way a profile writes it, for example United States, or San Francisco, California, United States. Matches any of them.

## `personExcludeLocations` (type: `array`):

Locations to leave out, one per line. Useful for trimming a region you cannot sell into out of an otherwise good filter.

## `personKeywords` (type: `array`):

Words to look for in the person's headline and about section, one per line. Matches any of them. This is how you find people who describe themselves a certain way even when their job title does not say it.

## `personTitleExactMatch` (type: `boolean`):

Require the job title to match exactly rather than as a partial. Turn this on when a broad title such as Director is pulling in every Director of everything.

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

Companies to match by name, one per line. Matches any of them.

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

Companies to match by website domain, one per line, without the scheme. This is the most reliable way to target a specific account list, because a domain is unambiguous where a company name is not.

## `companyIndustries` (type: `array`):

Industries to match, one per line, using this provider's own industry names, for example Software Development or Financial Services. Matches any of them.

## `companySizes` (type: `array`):

Headcount bands to match. These are the provider's own bands and cannot be given as a free range.

## `companyRevenue` (type: `array`):

Estimated annual revenue bands to match, in US dollars. These are the provider's own bands.

## `companyTypes` (type: `array`):

Legal or ownership type to match, using this provider's own vocabulary. Useful for excluding government and education from a commercial list.

## `companyFocuses` (type: `array`):

Whether the company sells to businesses, to consumers, or both. Leave empty for either.

## `companyLocations` (type: `array`):

Where the company is, one per line. Matches any of them. Different from the person's location, which is where that individual sits.

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

Words to look for in the company description, one per line. Matches any of them. This is how you find companies doing a particular thing when their industry label is too broad to help.

## `filters` (type: `object`):

Any other filter this provider supports, passed through untouched under its own name. This is the escape hatch for the filters this form does not show, such as company\_last\_funding, headcount\_growth, job\_openings, news, company\_investors, company\_founded\_date, company\_web\_traffic\_rank and linkedin\_networks. Anything set here overrides the fields above.

## `leads` (type: `array`):

Used by enrich mode only. One object per lead, using this provider's own field names: linkedin\_url, email, name, title, company\_name and company\_domain. Everyday names work too, so linkedin, full\_name, job\_title, company and domain are all accepted. A plain string is read as a LinkedIn URL, an email or a name. Each lead needs a LinkedIn URL, an email, or a name together with a company.

## `revealEmail` (type: `boolean`):

Ask the provider to find each person's email address. Enrich mode only. This spends email credits on top of the lookup itself, so it is off by default. A reveal can take a while, and this actor waits out the provider's documented retry window rather than giving up on it.

## `revealPhoneNumbers` (type: `boolean`):

Ask the provider to find each person's phone numbers. Enrich mode only. This spends phone credits on top of the lookup itself, so it is off by default. Numbers on a Do Not Call list are flagged on the row when your account has that screening enabled.

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

How many people to ask for per search request, up to the provider's ceiling of 100. Lower it only if you are hitting a rate limit; it does not change what a run costs, because charging is per row.

## `baseUrl` (type: `string`):

Overrides the host outright. Leave empty unless your account was issued a dedicated hostname.

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

Stop after this many rows. Rows are charged individually, so this is the ceiling on what a run can cost.

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

How fast to call the provider. The provider's own ceiling is 300 a minute on search and 350 on lookups, so the default leaves room. Lower it if your account has a tighter limit.

## Actor input object example

```json
{
  "mode": "search",
  "personTitles": [
    "VP of Sales"
  ],
  "personSeniorities": [],
  "personTitleExactMatch": false,
  "companySizes": [],
  "companyRevenue": [],
  "companyTypes": [],
  "companyFocuses": [],
  "filters": {},
  "leads": [
    {
      "linkedin_url": "https://www.linkedin.com/in/example-person"
    },
    {
      "email": "person@example.com"
    },
    {
      "name": "Jane Doe",
      "title": "CEO",
      "company_domain": "example.com"
    }
  ],
  "revealEmail": false,
  "revealPhoneNumbers": false,
  "pageSize": 100,
  "maxResults": 100,
  "requestsPerMinute": 120
}
```

# Actor output Schema

## `contacts` (type: `string`):

One row per person, alongside the search or lead the person was matched from.

# 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 = {
    "personTitles": [
        "VP of Sales"
    ],
    "filters": {},
    "leads": [
        {
            "linkedin_url": "https://www.linkedin.com/in/example-person"
        },
        {
            "email": "person@example.com"
        },
        {
            "name": "Jane Doe",
            "title": "CEO",
            "company_domain": "example.com"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("nabeelbaghoor/b2b-contact-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 = {
    "personTitles": ["VP of Sales"],
    "filters": {},
    "leads": [
        { "linkedin_url": "https://www.linkedin.com/in/example-person" },
        { "email": "person@example.com" },
        {
            "name": "Jane Doe",
            "title": "CEO",
            "company_domain": "example.com",
        },
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("nabeelbaghoor/b2b-contact-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 '{
  "personTitles": [
    "VP of Sales"
  ],
  "filters": {},
  "leads": [
    {
      "linkedin_url": "https://www.linkedin.com/in/example-person"
    },
    {
      "email": "person@example.com"
    },
    {
      "name": "Jane Doe",
      "title": "CEO",
      "company_domain": "example.com"
    }
  ]
}' |
apify call nabeelbaghoor/b2b-contact-search-api --silent --output-dataset

```

## MCP server setup

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