# Email Finder API - Work Emails, Phone Numbers, Profiles (`nabeelbaghoor/email-finder-api`) Actor

Find work emails, personal emails and phone numbers for professionals by job title, seniority, employer, skills and location, and search companies by firmographics, tech stack and buying signals. Pay per result. Bring your own API key.

- **URL**: https://apify.com/nabeelbaghoor/email-finder-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

$15.00 / 1,000 contact or company 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

## Email Finder API - Work Emails, Phone Numbers, Profiles

Find the people you want to reach and the companies they work for, then export the results as clean rows. Search a database of professional profiles by job title, seniority, department, employer, previous employer, skills, school and location, resolve work emails, personal emails and phone numbers for the people you name, and search companies by firmographics, tech stack, competitors and buying signals. You pay only for the records you actually receive.

This actor is built for outbound prospecting, recruiting and sourcing, building account and contact lists, alumni targeting, and enriching a CRM with verified contact details.

### What you can do with it

- Build a targeted contact list by title, seniority and department at companies matching your ideal customer profile.
- Resolve work emails and phone numbers for a list of LinkedIn URLs you already hold.
- Source candidates by skills, school, degree and previous employer.
- Catch people who just changed jobs, which is the strongest timing signal in outbound.
- Build a competitive displacement list by asking for the competitors of a company you name.
- Search companies by the technology they run, their hiring signals and their news signals.
- Export everything as JSON, CSV or Excel, or push it into a CRM or warehouse.

### Features

- All four of the provider's endpoints in one actor, chosen with a single mode field: people search, people lookup, company search and company lookup.
- The difference between searching and looking up is made explicit rather than left as a surprise. Search returns profiles without contact details; lookup is what resolves emails and phone numbers. Every row carries `hasContactDetails` so a list is never mistaken for a contactable one.
- The lookup list accepts whatever you happen to have: a LinkedIn URL, an email address, a numeric provider id, a company domain, a ticker, or a name with the employer after a pipe. Each line is routed to the right identifier automatically.
- Paging is done the provider's way, by a record offset counted from 1 rather than a page number, following the next offset the provider returns and stopping at its 10,000-record depth limit, which is logged rather than hit silently.
- A successful search answers with HTTP 201 rather than 200, which the client handles, so no run fails on a status code that actually means success.
- Emails and phone numbers are normalised into value, grade and type triples, with counts, alongside the provider's own recommended email picks, so you can filter on deliverability grade before sending.
- Job history, education and skills are kept intact for sourcing work.
- A lookup with no match comes back as a row marked `matched: false` rather than vanishing, and it is never charged for.
- Duplicate people and companies are removed across pages, keyed on provider id then LinkedIn URL or domain then name, before anything is charged.
- The untouched provider payload is kept under `raw`, so nothing is lost.
- Rate limits are handled with retry-after aware backoff, and a run that hits a limit keeps everything already collected.
- Pay-per-result pricing with a hard result cap so a run can never surprise you.

### Input

| Field | Type | Description |
| --- | --- | --- |
| `mode` | string | Find people, get emails and phones, find companies, or enrich companies. |
| `lookups` | array | One lookup per line for the lookup modes. LinkedIn URL, email, id, domain, or name with employer after a pipe. |
| `names` / `currentTitles` / `previousTitles` | array | Person name and title filters. |
| `managementLevels` / `departments` | array | Seniority and department filters. |
| `employers` / `previousEmployers` | array | Current and past employer filters. |
| `skills` / `allSkills` | array | Match any skill, or require every skill. |
| `schools` / `degrees` / `majors` / `yearsExperience` | array | Education and experience filters. |
| `locations` / `states` | array | Where the person is based. |
| `jobChangeSignals` / `jobChangeRangeDays` | array | Target recent job changers. |
| `contactMethods` / `emailGrade` | array, string | Require a contact method, or a minimum email deliverability grade. |
| `companyNames` / `companyDomains` / `companyIndustries` | array | Company identity and industry filters. |
| `companySizes` / `companyRevenues` | array | Headcount and revenue bands, written as the provider documents them. |
| `companyNaicsCodes` / `companySicCodes` | array | Classification codes. |
| `companyTechstack` / `companyCompetitors` | array | Technology and competitive filters. |
| `companyIntent` / `companyJobPostingSignals` / `companyNewsSignals` / `companyGrowth` | array | Buying, hiring, news and growth signals. |
| `orderBy` | string | Relevance, popularity or score. |
| `skipUnmatched` | boolean | Drop unresolvable lookups instead of returning them marked unmatched. |
| `rawQuery` | object | Any extra filter, merged last into the provider's query object. |
| `maxResults` | integer | Stop after this many records (1 to 10,000). |
| `pageSize` | integer | Records per search call (1 to 100). |
| `apiKey` | string | Your API key. Required, stored securely. |

A search mode needs at least one filter. A lookup mode needs at least one line in the lookup list.

### Example output

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

```json
{
  "recordType": "person",
  "personId": "12345678",
  "status": "complete",
  "name": "Alex Example",
  "currentTitle": "VP of Engineering",
  "currentEmployer": "Example Corp",
  "currentEmployerDomain": "example.com",
  "currentEmployerIndustry": "Computer Software",
  "linkedinUrl": "https://www.linkedin.com/in/example",
  "location": "San Francisco, California, United States",
  "city": "San Francisco",
  "region": "California",
  "country": "United States",
  "countryCode": "US",
  "recommendedEmail": "alex@example.com",
  "recommendedProfessionalEmail": "alex@example.com",
  "currentWorkEmail": "alex@example.com",
  "emails": [
    { "value": "alex@example.com", "grade": "A", "type": "professional" },
    { "value": "alex.example@gmail.com", "grade": "B", "type": "personal" }
  ],
  "emailCount": 2,
  "phones": [{ "value": "+1 415 555 0100", "type": "mobile" }],
  "phoneCount": 1,
  "hasContactDetails": true,
  "jobHistory": [{ "title": "Director of Engineering", "company_name": "Example Corp", "start_date": "2019-03" }],
  "education": [{ "school": "Example University", "degree": "BSc Computer Science" }],
  "skills": ["distributed systems", "Go", "Kubernetes"],
  "raw": { "id": 12345678, "name": "Alex Example" }
}
```

### Pricing and the credit model

This actor uses pay-per-result pricing. You are charged a fixed price for each person or company delivered to the dataset. Duplicates and unmatched lookups are not charged, and 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 the two modes cost very differently there: search results are cheap because they carry no contact details, while a lookup that resolves an email or a phone number is what actually spends a lookup credit. Search first to build and filter the list, then look up only the rows you decided to keep.

### Bring your own API key

This actor connects to a third-party professional contact data provider (RocketReach) on your behalf and requires your own provider API key. Create a key in the provider's platform and paste it into the `apiKey` input. It is 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 person: provider id and status, name, current title and employer with the employer's domain, website, industry and LinkedIn page, LinkedIn URL and whether it is still active, profile picture, location broken into city, region, country and country code with coordinates, connection count, the provider's recommended email picks, every email and phone number with its deliverability grade and type, job history, education, skills, tags and the last update time. For a company: provider id, name, domain, email and website domains, ticker, industry and industry keywords, description, year founded, headcount, revenue, funding investors, SIC and NAICS codes, tech stack, competitors, departments, growth, phone and fax, the full address, and social links. Every record also carries the untouched provider payload.

#### What is the difference between search and lookup?

Search discovers people and returns their profile, but not their email address or phone number. Lookup takes a person you have already identified and resolves their contact details. This is the provider's own split, and it is also the cost split: searching is cheap, resolving contact details is what spends the credit. The normal workflow is search first, filter, then look up the rows you kept.

#### How do I look up someone if I only have their name?

Add the employer after a pipe: `Alex Example | Example Corp`. A name on its own is ambiguous and will often match the wrong person. Name plus employer is the most reliable combination short of a LinkedIn URL, which is better still.

#### Why do employee count and revenue take text rather than numbers?

Because the provider models them as bands rather than as ranges, and every search filter it exposes is a list of strings. A headcount filter is a band label such as `51-200`, not a minimum and a maximum. Write them exactly as the provider documents them, or the filter is quietly ignored.

#### Can I filter out low-quality email addresses?

Yes, two ways. Set `emailGrade` to a minimum grade so the provider only returns people whose email meets it, and check the per-email `grade` on each returned row before sending. Both matter: the first narrows the search, the second lets you pick between several addresses on the same person.

#### How do I find people who just changed jobs?

Use `jobChangeSignals` together with `jobChangeRangeDays` to set how far back a change still counts. New starters in a role are the single most responsive segment in outbound, because budgets and tooling decisions reopen when someone new takes over.

#### How do I build a competitive displacement list?

Use `companyCompetitors` in company search mode with the name of the incumbent you want to displace, or `companyTechstack` with the product they run. The first finds companies the provider treats as competitors, the second finds companies actually running the tool.

#### Why does the run stop at 10,000 records?

The provider caps how deep search paging can go, at a 10,000-record offset, regardless of how many records match. The log says so explicitly when the limit is reached. Split the search, for example one industry or one location at a time, to reach past it.

#### What happens to a lookup with no match?

It comes back as a row with `matched: false` and the original input under `query`, so you can see exactly which inputs failed, and it is not charged. Turn on `skipUnmatched` if you would rather they disappeared.

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

It throttles requests, honours the retry-after signal on a rate-limited response, and backs off exponentially. A response saying the account is out of credits or that the plan does not include API access 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 and unmatched lookups are not charged 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

email finder api, work email lookup api, find email by linkedin url, contact data api, b2b contact database api, phone number lookup api, professional profile search api, people search api, lead generation contact data, recruiting sourcing api, candidate search api, job change signal data, company search api, technographic company search, competitor list api, buying intent data api, crm contact enrichment, prospecting email api, bulk email finder, verified business email data.

# Actor input Schema

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

What this run does. Searching discovers profiles and companies but does not resolve contact details. Looking up is what returns email addresses and phone numbers for people you name.

## `lookups` (type: `array`):

One lookup per line, used by the two lookup modes. Accepts a LinkedIn URL, an email address, a numeric provider id, a company domain, or a name. For a person you can add the employer after a pipe, for example "Alex Example | Example Corp", which makes the match far more reliable.

## `names` (type: `array`):

Names to match.

## `currentTitles` (type: `array`):

Match on the title a person holds right now.

## `currentOrPreviousTitles` (type: `array`):

Match a title whether the person holds it now or held it before.

## `previousTitles` (type: `array`):

Match on a title a person used to hold.

## `managementLevels` (type: `array`):

Seniority bands, for example C-Level, VP or Director. Use the provider's own level names.

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

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

## `employers` (type: `array`):

Company names the person currently works for.

## `previousEmployers` (type: `array`):

Company names the person used to work for. Useful for alumni targeting.

## `skills` (type: `array`):

Match a person carrying any of these skills.

## `allSkills` (type: `array`):

Match only people carrying every one of these skills.

## `schools` (type: `array`):

Universities or schools attended.

## `degrees` (type: `array`):

Degrees held.

## `majors` (type: `array`):

Fields of study.

## `yearsExperience` (type: `array`):

Experience bands, written the way the provider documents them.

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

Where the person is based.

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

States or regions the person is based in.

## `jobChangeSignals` (type: `array`):

Target people who recently changed roles, which is the strongest timing signal in outbound.

## `jobChangeRangeDays` (type: `array`):

How far back a job change counts, in days, written as the provider documents it.

## `contactMethods` (type: `array`):

Only return people with a given contact method on file, for example a professional email or a mobile number.

## `emailGrade` (type: `string`):

The provider grades email deliverability. Set a minimum grade to keep bounces down.

## `keyword` (type: `string`):

Free-text keyword searched across the profile.

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

Company names to match. Used by both the people search and the company search.

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

Company domains to match. The most reliable company key.

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

Industries to match.

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

Headcount bands, written exactly as the provider documents them. These are strings, not numbers.

## `companyRevenues` (type: `array`):

Revenue bands, written exactly as the provider documents them. These are strings, not numbers.

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

Where the company is based. Used by the company search.

## `companyGeos` (type: `array`):

Geographic filter for the company search.

## `companyCountryCodes` (type: `array`):

Two-letter country codes of the company's location. Used by the people search.

## `companyNaicsCodes` (type: `array`):

NAICS classification codes.

## `companySicCodes` (type: `array`):

SIC classification codes.

## `companyTechstack` (type: `array`):

Technologies the company runs. Company search only.

## `companyCompetitors` (type: `array`):

Find companies the provider treats as competitors of these, which is the fastest way to build a displacement list.

## `companyIntent` (type: `array`):

Buying-intent topics the company is showing activity on.

## `companyJobPostingSignals` (type: `array`):

Signals derived from what the company is hiring for.

## `companyNewsSignals` (type: `array`):

Signals derived from company news, for example a funding round or a leadership change.

## `companyGrowth` (type: `array`):

Growth bands or signals. Company search only.

## `companyTotalFunding` (type: `array`):

Total funding raised, in the provider's own bands. Company search only.

## `companyFundingMin` (type: `array`):

Funding floor, written as the provider documents it. People search only.

## `companyFundingMax` (type: `array`):

Funding ceiling, written as the provider documents it. People search only.

## `companyPubliclyTraded` (type: `array`):

Restrict to publicly traded companies, or to private ones, using the provider's own values.

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

Free-text keywords matched against the company. Company search only.

## `companyWebsiteUrls` (type: `array`):

Match on full website URLs rather than bare domains.

## `orderBy` (type: `string`):

How the provider orders search results. Leave on relevance unless you have a reason not to.

## `skipUnmatched` (type: `boolean`):

Drop a lookup the provider cannot resolve instead of returning a row marked as unmatched. Unmatched rows are never charged either way.

## `rawQuery` (type: `object`):

Any additional search filter, merged over the ones above and sent inside the provider's query object. Use this for a filter the provider adds after this actor was published.

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

Stop after this many records (1 to 10,000). The provider limits search paging to 10,000 records deep. You are charged per result returned.

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

How many records to request per search call (1 to 100). Larger pages mean fewer calls against your provider rate limit.

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

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

## Actor input object example

```json
{
  "mode": "person-search",
  "orderBy": "relevance",
  "skipUnmatched": false,
  "rawQuery": {},
  "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 = {
    "rawQuery": {},
    "maxResults": 100,
    "pageSize": 100
};

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

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

```

## MCP server setup

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