# B2B Contact Database API - Company and Contact Search (`nabeelbaghoor/b2b-contact-database-api`) Actor

Search a B2B database of companies and their employees by industry, employee band, revenue band, location, job title, department, seniority and installed technology, then reveal verified work emails and phone numbers. Pay per result. Bring your own API key.

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

## Pricing

$12.00 / 1,000 company or contact 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/platform/actors/running/actors-in-store#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 Database API - Company and Contact Search

Search a B2B database of companies and the people who work at them, and export the matches as clean rows. Filter companies by industry, sub-industry, employee band, revenue band and location, filter contacts by job title, department, seniority, employer and the technology their company runs, and reveal verified work emails and phone numbers for the people you decide to keep. You pay only for the records you actually receive.

This actor is built for outbound prospecting, account and contact list building, technographic targeting, and enriching a CRM with firmographics and verified contact details.

### What you can do with it

- Build a company list by industry, size band, revenue band and location.
- Find the right people inside those companies by title, department and seniority.
- Strip juniors and assistants out of a senior list with an exclude-title filter, rather than cleaning it afterwards.
- Cap how many people come back from any one company, so a single large employer cannot swallow the list.
- Target companies by the technology they run.
- Reveal verified emails and phone numbers only for the contacts you kept, instead of paying for the whole search.
- Export everything as JSON, CSV or Excel, or push it into a CRM or warehouse.

### Features

- Three modes in one actor: company search, contact search, and revealing contact ids you already hold.
- Employee and revenue bands are dropdowns, not free text. The provider accepts a fixed set of labels spelled exactly, spaces and currency symbols included, so `25 - 100` works and `25-100` does not. Picking from a list removes the whole class of silently-ignored filter.
- Revealing contact details is off by default and has to be asked for explicitly, because it is a separate and more expensive action on your provider plan than searching. The log says so plainly when it is turned on.
- Reveal calls are batched at the provider's documented maximum of 50 ids per call, and a batch that fails is reported and skipped rather than losing the run.
- Revealed detail is merged over the search preview rather than replacing it, so fields that only appear in the preview survive.
- Cursor pagination is walked the provider's way, following the cursor it returns, and a cursor that comes back unchanged is treated as the end of the result set rather than looping forever.
- The remaining credit balance is read off the response headers on every call and reported at the end of the run.
- Every contact row carries `revealed`, so a preview is never mistaken for a contactable record.
- Contact rows are shaped defensively across several candidate field names, and the untouched provider payload is always kept under `raw`, so nothing is lost.
- Duplicate companies and contacts are removed across pages, keyed on contact id or domain then name, before anything is charged.
- Request pacing is built for the provider's published limit of 250 requests per minute, with retry-after aware backoff on top.
- Pay-per-result pricing with a hard result cap so a run can never surprise you.

### Input

| Field | Type | Description |
| --- | --- | --- |
| `mode` | string | Search companies, search contacts, or reveal known contact ids. |
| `companyNames` / `companyDomains` | array | Company identity filters, used by both searches. |
| `headCounts` | array | Employee bands, picked from the provider's fixed labels. |
| `revenues` | array | Revenue bands, picked from the provider's fixed labels. |
| `industries` / `subIndustries` | array | Industry classification filters. |
| `cities` / `states` / `countries` | array | Company location filters. |
| `titles` / `excludeTitles` / `titleExactMatch` | array, array, boolean | Job title targeting, including exclusions and exact matching. |
| `departments` / `levels` | array | Department and seniority filters. |
| `contactNames` / `firstNames` / `lastNames` | array | Person name filters. |
| `technologies` | array | Only contacts at companies running these technologies. |
| `locationPreference` | string | Where the contacts should be based. |
| `requiredFields` | array | Only contacts carrying these fields, for example an email. |
| `minimumEmailDeliverabilityScore` | integer | Drop contacts whose email scores below this. |
| `maxContactPerCompany` | integer | Cap the contacts returned per company. |
| `dontDisplayOwnedContact` | boolean | Hide contacts already saved in your provider account. |
| `revealContacts` | boolean | Request verified emails and phones after searching. |
| `contactIds` | array | Contact ids to reveal, for the reveal mode. |
| `rawFilters` | object | Any extra search field, merged last. |
| `maxResults` | integer | Stop after this many records (1 to 25,000). |
| `pageSize` | integer | Records per API call (1 to 100). |
| `apiKey` / `accountEmail` | string | Your API key and the account's registered email. Both required. |

A search mode needs at least one filter. The reveal mode needs at least one contact id.

### Example output

Each dataset item is one company or one contact, marked by `recordType`. Fields are populated where the provider has data, and the untouched payload is kept under `raw`:

```json
{
  "recordType": "contact",
  "contactId": "88213470",
  "name": "Alex Example",
  "firstName": "Alex",
  "lastName": "Example",
  "title": "Head of Marketing",
  "department": "marketing",
  "level": "Director",
  "email": "alex@example.com",
  "emailDeliverabilityScore": 92,
  "phone": "+1 617 555 0142",
  "linkedin": "https://www.linkedin.com/in/example",
  "companyName": "Example Corp",
  "companyDomain": "example.com",
  "companyIndustry": "Computer Software",
  "companyHeadCount": "100 - 250",
  "companyRevenue": "$10 - 50M",
  "city": "Boston",
  "state": "MA",
  "country": "United States",
  "revealed": true,
  "raw": { "contactId": 88213470, "title": "Head of Marketing" }
}
```

A company row looks like this instead:

```json
{
  "recordType": "company",
  "name": "Example Corp",
  "website": "https://www.example.com",
  "domain": "example.com",
  "phoneNumber": "+1 617 555 0100",
  "street": "88 Example Avenue",
  "city": "Boston",
  "state": "MA",
  "zipcode": "02110",
  "country": "United States",
  "industry": "Computer Software",
  "subIndustry": "Marketing Automation",
  "headCount": "100 - 250",
  "revenue": "$10 - 50M",
  "numberOfContacts": 143,
  "linkedin": "https://www.linkedin.com/company/example-corp",
  "raw": { "name": "Example Corp" }
}
```

### Pricing and the credit model

This actor uses pay-per-result pricing. You are charged a fixed price for each company or contact delivered to the dataset, after duplicates are removed. A search that matches nothing costs nothing. Set `maxResults` to cap how many rows a run can collect, and set a run spending limit as a second safety net.

Your own provider plan meters separately, and it distinguishes searching from revealing: a search spends search credits, while revealing a verified email or phone number spends the more expensive purchase credits. The response header reporting your remaining balance is read on every call and logged at the end of a run. Search first, narrow with `requiredFields`, `minimumEmailDeliverabilityScore` and `maxContactPerCompany`, then reveal only the rows you decided to keep.

### Bring your own API key

This actor connects to a third-party B2B company and contact data provider (Adapt.io) on your behalf and requires your own provider credentials. This provider authenticates with two values rather than one: the API key from your account settings, and the email address the account is registered under. Paste both into the `apiKey` and `accountEmail` inputs. They are stored securely and never logged, and the actor never ships with or shares credentials.

### Frequently asked questions

#### What data does this actor return?

For a company: name, website with a normalised domain, phone number, the full address split into street, city, state, postal code and country, industry and sub-industry, employee band, revenue band, how many contacts the provider holds for that company, and links to LinkedIn, Twitter and Facebook. For a contact: contact id, name split into first and last, job title, department, seniority level, email with its deliverability score, phone, LinkedIn URL, the employer's name, domain, industry, employee band and revenue band, and location. Every record also carries the untouched provider payload.

#### Why are employee count and revenue dropdowns rather than numbers?

Because the provider accepts a fixed set of band labels rather than free numeric ranges, and the labels have to be spelled exactly, spaces and currency symbols included. `25 - 100` is valid and `25-100` is not. A mistyped band is not an error, it is silently ignored, which is the worst kind of failure. Offering them as a dropdown makes that impossible.

#### What is the difference between searching and revealing?

Searching returns contact previews: who the person is, what they do and where they work, but not their email address or phone number. Revealing is the separate call that returns those, and it spends the more expensive purchase credits on your provider plan. That is why `revealContacts` is off by default. Every contact row carries `revealed` so you can always tell which kind you are looking at.

#### How do I keep the reveal bill down?

Search first and narrow hard before revealing. Use `requiredFields` so only contacts that actually carry an email come back, set `minimumEmailDeliverabilityScore` so you are not paying to reveal an address that will bounce, set `maxContactPerCompany` so one large employer cannot fill the list, and turn on `dontDisplayOwnedContact` so you do not pay twice for people already in your account. Then run the reveal mode over the ids you kept.

#### How many contacts can be revealed at once?

The provider accepts 50 ids per reveal call, so the actor batches automatically at that size. A batch that fails is logged and skipped rather than ending the run, so one bad id does not cost you the rest.

#### How does pagination work here?

By cursor, not by page number or offset. Each response carries a cursor which is sent back on the next request. When the cursor comes back unchanged, the result set is finished, and that is how the walk ends. This is why there is no page number input.

#### Can I see how many credits I have left?

Yes. The provider reports the remaining balance and the credit type on every response header, and the actor reads it on every call and logs the final figure at the end of the run.

#### How does the actor handle rate limits?

The provider allows 250 requests per minute. The actor paces its own requests to stay under that by design, honours the retry-after signal on a rate-limited response, and backs off exponentially. A response saying the account is out of credits stops the run early and keeps everything already collected.

#### How am I charged?

You pay a fixed price per record returned to the dataset. Duplicates are removed before charging and empty runs cost nothing. Use `maxResults` and the run spending limit to control cost.

#### What output formats are available?

The dataset can be exported as JSON, CSV, Excel, HTML or RSS, or read through the API for pushing into a CRM or warehouse.

### Keyword map

b2b contact database api, company and contact search api, b2b prospecting api, work email api, contact enrichment api, firmographic company search, job title search api, department seniority filter api, technographic contact targeting, sales prospecting data api, lead list building api, account based marketing data, crm data enrichment api, verified business email data, direct dial phone data api, company revenue band search, employee count company search, b2b lead generation database, bulk contact export api, sales intelligence data api.

# Actor input Schema

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

What this run does. Company search returns firmographics. Contact search returns people, as previews unless you turn on reveal. Contact reveal takes contact ids you already hold and returns their verified details.

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

Company names to match. Applies to both the company search and the contact search.

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

Company domains to match. The most reliable company key, because it is already standardised.

## `headCounts` (type: `array`):

Headcount bands. These are fixed labels, not free ranges, which is why they are a dropdown. Company search only.

## `revenues` (type: `array`):

Annual revenue bands. Fixed labels rather than free ranges. Company search only.

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

Industries to match. Company search only.

## `subIndustries` (type: `array`):

Narrower industry classification. Company search only.

## `cities` (type: `array`):

Cities to match. Company search only.

## `states` (type: `array`):

States or regions to match. Company search only.

## `countries` (type: `array`):

Countries to match. Company search only.

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

Job titles to match.

## `excludeTitles` (type: `array`):

Job titles to leave out, which is the fastest way to strip assistants and interns out of a senior list.

## `titleExactMatch` (type: `boolean`):

Match the job title exactly rather than as a partial phrase.

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

Departments to match, for example marketing, engineering or finance.

## `levels` (type: `array`):

Seniority bands, using the provider's own level names.

## `contactNames` (type: `array`):

Full names to match.

## `firstNames` (type: `array`):

First names to match.

## `lastNames` (type: `array`):

Last names to match.

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

Only return contacts at companies running these technologies.

## `locationPreference` (type: `string`):

Location the contacts should be in, using the provider's own location format.

## `requiredFields` (type: `array`):

Only return contacts that carry these fields, for example an email or a direct dial, so you do not pay for rows you cannot use.

## `minimumEmailDeliverabilityScore` (type: `integer`):

Drop contacts whose email scores below this, which keeps bounce rates down.

## `maxContactPerCompany` (type: `integer`):

Cap how many people come back from any one company, so a single large employer cannot fill the whole list.

## `dontDisplayOwnedContact` (type: `boolean`):

Leave out contacts already saved in your provider account, so you do not pay twice for the same person.

## `revealContacts` (type: `boolean`):

After searching, request the verified email addresses and phone numbers for the contacts found. This is a separate and more expensive action on your provider plan than searching, so it is off by default.

## `contactIds` (type: `array`):

Contact ids you already hold, used by the reveal mode. Sent to the provider in batches of 50, which is its documented maximum.

## `rawFilters` (type: `object`):

Any additional search field, merged over the ones above. Use this for a filter the provider adds after this actor was published.

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

Stop after this many records (1 to 25,000). You are charged per result returned.

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

How many records to request per API call (1 to 100). Larger pages mean fewer calls against your provider rate limit of 250 requests per minute.

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

Your own API key for the data provider. Required. Stored securely and never logged.

## `accountEmail` (type: `string`):

The email address your provider account is registered under. Required, and sent alongside the key on every request.

## Actor input object example

```json
{
  "mode": "company-search",
  "titleExactMatch": false,
  "dontDisplayOwnedContact": false,
  "revealContacts": false,
  "rawFilters": {},
  "maxResults": 100,
  "pageSize": 100
}
```

# 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 = {
    "rawFilters": {},
    "maxResults": 100,
    "pageSize": 100
};

// Run the Actor and wait for it to finish
const run = await client.actor("nabeelbaghoor/b2b-contact-database-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 = {
    "rawFilters": {},
    "maxResults": 100,
    "pageSize": 100,
}

# Run the Actor and wait for it to finish
run = client.actor("nabeelbaghoor/b2b-contact-database-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 '{
  "rawFilters": {},
  "maxResults": 100,
  "pageSize": 100
}' |
apify call nabeelbaghoor/b2b-contact-database-api --silent --output-dataset

```

## MCP server setup

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