# B2B Lead Pipeline — Companies, Decision-Makers & Emails (`inovaflow/b2b-lead-pipeline`) Actor

One call from an ICP to a ready lead list: find the companies that match your industry, location and size, find the decision-makers at each, and attach a verified work e-mail per person — deduplicated, scored A–D, with optional tech stack and buying signals on every row.

- **URL**: https://apify.com/inovaflow/b2b-lead-pipeline.md
- **Developed by:** [inovaflow](https://apify.com/inovaflow) (community)
- **Categories:** Lead generation, Business, AI
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $10.00 / 1,000 leads

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 Lead Pipeline — Companies, Decision-Makers & Emails

**Building a lead list is four jobs, and most tools do one.** Find the companies that match your ICP. Find
the right person at each one. Get their work e-mail. Work out which of them is worth writing to first. Doing
that by stitching four tools together is a morning's work — and you do it again next week.

This is the four jobs in **one call**. Describe who you sell to, say which titles matter, and get back a
finished list: **one row per person, with a work e-mail, deduplicated and scored A–D.**

***

### What one row looks like

| | |
| --- | --- |
| **Company** | `company`, `domain`, `website`, `industry`, `companySize`, `headquarters`, `country` |
| **Person** | `person`, `firstName`, `lastName`, `title`, `seniority`, `department`, `personLinkedinUrl`, `matchedTitle` |
| **Contact** | `email`, `emailStatus`, `emailConfidence`, `emailVerification`, `emailPattern`, `catchAll` |
| **Context** *(optional)* | `techStack`, `signals`, `signalsPresent`, `signalScore`, `whyNow`, `sellTo` |
| **Verdict** | `leadScore`, `leadGrade`, `scoreReasons` — in English, so you can argue with it |

***

### Who it is for

- **Founders and small sales teams** — a real list, from a sentence describing your customer, in one run.
- **AI / GTM agents** — one tool call, a flat dataset, no login and no key. Hand it an ICP, get leads.
- **Agencies** — a repeatable pipeline per client instead of a per-client stack of subscriptions.
- **RevOps** — top up a segment without opening four tabs.

***

### How to use it

Describe the customer, name the titles:

```json
{
  "keywords": ["revenue intelligence software"],
  "locations": ["United States"],
  "companySizes": ["51-200", "201-500"],
  "titles": ["VP of Sales", "Head of Revenue Operations"],
  "maxCompanies": 25,
  "maxPeoplePerCompany": 2
}
```

Already have the accounts? Skip the search:

```json
{
  "companies": ["gong.io", "ramp.com", "lemlist.com"],
  "titles": ["Chief Revenue Officer", "VP of Marketing"],
  "requireEmail": true
}
```

The full-context version — every lead carries the company's tech stack and why now is the moment:

```json
{
  "keywords": ["logistics software"],
  "titles": ["Head of Operations"],
  "enrichCompanies": true,
  "attachSignals": true,
  "minLeadScore": 55
}
```

***

### The lead score, in plain terms

Four things decide whether a row is worth a sequence, and each is scored on its own terms before being
weighted: **can you reach them** (35) — a confirmed mailbox beats a learned pattern beats a guess; **are they
the right person** (30) — an explicit title match beats a seniority guess; **is it the right company** (15) —
resolved domain, known industry and size; **is now the right time** (20) — the buying signals, when you
attach them.

The total is **normalised over the parts the run could actually produce**, so a run without signals still
reaches an A and rows stay comparable inside a run. Grades: **A** ≥75, **B** ≥55, **C** ≥35, **D** below.
No model call, no API key, and `scoreReasons` spells out every judgement in English.

***

### Honest about e-mails

`emailStatus` says exactly how much an address has been checked: **verified** (the mailbox itself confirmed),
**found** (a learned or observed pattern on a live mail domain), **guessed** (the common pattern for that
domain), **none**. A confident pattern on a domain that cannot receive mail is never better than a guess.
`emailVerification` carries the raw depth, `catchAll` flags the domains that accept everything, and
`requireEmail` drops anyone without an address.

***

### Built to keep running

- **Every stage degrades on its own.** If the people search comes back empty for a company, that company
  still gets its reachable and role addresses so you have a way in. If an optional layer is unavailable, the
  leads still ship and `stepsSkipped` says what was missing.
- **Deduplicated per person**, keyed on person + company, keeping the record with the better contact data —
  so the same human never appears twice.
- **Capped on purpose.** `maxCompanies`, `maxPeoplePerCompany` and `maxLeads` bound both the output and the
  cost, and `maxSpendUsd` is a hard ceiling on the stages.
- **Finishes on time.** `maxRunSecs` is a wall-clock budget for the whole run; when it is nearly spent the
  remaining stages are shortened or skipped and the leads found so far are delivered, with the cut named under
  `stepsSkipped`. The run's own timeout is honoured the same way. The form starts small — 3 companies, 2
  people each, about three minutes — so the first run is a sample, not a bill; raise the caps once it looks right.
- **Every run reports itself.** The summary carries what each stage produced, e-mail coverage, the grade
  split and what the run cost per lead.

***

### Notes

- **Dataset-only.** No UI, no dashboard, no login. Four ready-made views: leads, contact quality, company
  context, score audit.
- **Public data only.** Nothing behind a login is ever touched.
- Two or three well-chosen contacts per company beat ten. The default is three.

# Actor input Schema

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

What the companies you want do — `revenue intelligence software`, `dental clinics`, `3PL logistics`. One per line.

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

Cities, countries or regions. Empty = anywhere.

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

Empty = every size.

## `companies` (type: `array`):

Skip the search and run the pipeline on this list instead. One per line: a domain (`gong.io`) or a company name (`Gong`). When this is set, the industry and location fields are ignored.

## `maxCompanies` (type: `integer`):

How many companies the pipeline may work through. The single biggest lever on what a run costs and how long it takes: 3 is a two-to-three-minute sample, 25 (the default when unset) a proper list. Raise it once the sample leads look right.

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

Job titles or keywords, one per line. Abbreviations are understood (`CRO`, `VP Sales`, `RevOps`) and matched against every common phrasing of the same role.

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

Keep only people at these levels. Empty = every level the people search returns.

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

Keep only people in these functions. Empty = every function.

## `maxPeoplePerCompany` (type: `integer`):

Two or three well-chosen contacts beat ten. This caps both the output and the cost per company.

## `findEmails` (type: `boolean`):

Get a work e-mail for every person found. Turn off for a people-only list.

## `verifyEmails` (type: `boolean`):

Check that the address actually accepts mail before it goes on the row. Verified addresses score highest.

## `requireEmail` (type: `boolean`):

Drop anyone we could not get an address for.

## `enrichCompanies` (type: `boolean`):

Add firmographics and the live tech stack to every lead: industry, headcount, HQ, and the CMS, analytics, CRM and marketing tools the company actually runs.

## `attachSignals` (type: `boolean`):

Add "why now" to every lead: hiring, tech change, funding, ad activity and executive change, with a combined score, an opening line and who to sell to.

## `signals` (type: `array`):

Only used when buying signals are on. Empty = all five.

## `signalDaysBack` (type: `integer`):

Only used when buying signals are on. How recent a signal has to be to count.

## `maxLeads` (type: `integer`):

Stop after this many leads. Also caps what this Actor charges, since it charges per lead delivered.

## `minLeadScore` (type: `integer`):

Keep only leads scoring at least this, out of 100. 55 ≈ grade B, 75 ≈ grade A.

## `smtpVerification` (type: `boolean`):

Ask the mail server whether the mailbox exists, on top of the domain checks. Slower, and some servers accept everything; off in the sample run because outbound port 25 is blocked on the platform for most accounts, so the check rarely answers.

## `maxSpendUsd` (type: `integer`):

A hard ceiling on what the pipeline stages may charge your account in this run. Leave empty for no ceiling.

## `nestedTimeoutSecs` (type: `integer`):

How long any one stage may take before the pipeline continues without it. The leads still ship, with that stage listed under stepsSkipped.

## `maxRunSecs` (type: `integer`):

A wall-clock budget for the whole run. When it is nearly spent, the remaining stages are shortened or skipped (named under stepsSkipped) and the leads found so far are delivered rather than lost to a timeout. Leave empty for no budget — the run then only respects its own timeout, which it also honours.

## Actor input object example

```json
{
  "keywords": [
    "B2B SaaS",
    "sales enablement"
  ],
  "locations": [
    "United States",
    "Berlin"
  ],
  "companySizes": [],
  "companies": [
    "gong.io",
    "Ramp"
  ],
  "maxCompanies": 3,
  "titles": [
    "VP of Sales",
    "Chief Revenue Officer",
    "RevOps"
  ],
  "seniorities": [],
  "departments": [],
  "maxPeoplePerCompany": 2,
  "findEmails": true,
  "verifyEmails": true,
  "requireEmail": false,
  "enrichCompanies": false,
  "attachSignals": false,
  "signals": [
    "hiring",
    "tech",
    "funding",
    "ads",
    "leadership"
  ],
  "signalDaysBack": 30,
  "maxLeads": 200,
  "minLeadScore": 0,
  "smtpVerification": true,
  "nestedTimeoutSecs": 1800,
  "maxRunSecs": 240
}
```

# Actor output Schema

## `leads` (type: `string`):

The list you send: person, title, company, domain, work e-mail, e-mail status, lead score and grade.

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

How each address was obtained, whether it verified, the company's pattern and whether the domain is catch-all.

## `context` (type: `string`):

Industry, headcount, HQ, tech stack and the buying signals attached to each lead's company.

## `audit` (type: `string`):

The plain-English reasons behind each lead score, which stages answered and which were skipped.

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

Companies found, people found, e-mail coverage, the grade split, the per-stage report and the cost breakdown.

# 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 = {
    "keywords": [
        "revenue intelligence software"
    ],
    "maxCompanies": 3,
    "titles": [
        "Head of Sales"
    ],
    "maxPeoplePerCompany": 2,
    "smtpVerification": false,
    "maxRunSecs": 240
};

// Run the Actor and wait for it to finish
const run = await client.actor("inovaflow/b2b-lead-pipeline").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 = {
    "keywords": ["revenue intelligence software"],
    "maxCompanies": 3,
    "titles": ["Head of Sales"],
    "maxPeoplePerCompany": 2,
    "smtpVerification": False,
    "maxRunSecs": 240,
}

# Run the Actor and wait for it to finish
run = client.actor("inovaflow/b2b-lead-pipeline").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 '{
  "keywords": [
    "revenue intelligence software"
  ],
  "maxCompanies": 3,
  "titles": [
    "Head of Sales"
  ],
  "maxPeoplePerCompany": 2,
  "smtpVerification": false,
  "maxRunSecs": 240
}' |
apify call inovaflow/b2b-lead-pipeline --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,inovaflow/b2b-lead-pipeline"
        }
    }
}
```

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/NrXsb0sL1W0EeqYdD/builds/4C4fcgeoWiCCqCAvL/openapi.json
