# LinkedIn Lead Generation | Apollo & ZoomInfo Alternative (`b2bdata/linkedin-lead-generation`) Actor

Build LinkedIn lead lists by job title, seniority, location and company size. No LinkedIn login required. A real Apollo & ZoomInfo alternative — pay only for delivered leads, not a per-seat subscription.

- **URL**: https://apify.com/b2bdata/linkedin-lead-generation.md
- **Developed by:** [Eugene Volper](https://apify.com/b2bdata) (community)
- **Categories:** Lead generation, Social media
- **Stats:** 1 total users, 1 monthly users, 66.7% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.99 / 1,000 verified leads

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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

## LinkedIn Lead Generation | Apollo & ZoomInfo Alternative

Describe your ideal customer — job title, seniority, location, company size — and get back a ready-to-send lead list: person, current company, and a work email. No LinkedIn login, no cookies.

### B2B lead generation, built on a real profile database

Most "LinkedIn lead generation" actors scrape live pages behind Sales Navigator cookies, which breaks the moment LinkedIn changes its markup. This actor answers from a licensed profile and company database instead — no login, no scraping, no session to babysit.

### LinkedIn people search by title, seniority, location

Filter by job title (`VP Sales`, `Head of Growth`, …), seniority (C-level / VP / Director / Manager / Founder), country or city, and company size. Combine filters — country + title is enough to start; add company size and industry to narrow further.

### A work email for every lead we can find one for

Every candidate email is first matched to the target company's own domain (rejects stale prior-employer or personal addresses), then checked at the SMTP level. Every matching lead is delivered — we don't hold results back on a quality gate. The row's `emailStatus` column tells you honestly whether that specific email passed the SMTP check or not, and pricing follows: confirmed-valid emails are billed at the standard rate, anything else at a lower rate.

### Only leads with a phone number

Set `requirePhone: true` to deliver only leads that also have a phone number on file — they come back in the `phoneNumbers` column. About 1 in 6 people in the database has one, so the actor scans many more candidates (up to 50× `maxLeads`) and may deliver fewer than `maxLeads`; people without a phone are skipped before any paid email step. Pricing per lead is unchanged. Numbers are as listed in the database — not verified, and some can be company switchboard lines. `phoneNumbers` is `null` when `requirePhone` is off.

### Do I need a LinkedIn account? No.

This actor never touches linkedin.com. All data comes from a database API — no login, no cookies, no CAPTCHA risk to your account.

### Export a ready-to-send lead list

Every run's dataset is a flat table: name, title, company, domain, work email, city, country, LinkedIn URL. Download as CSV/JSON/Excel directly from the run, or pull it via the API.

### Sample input

```json
{
    "jobTitles": ["VP Sales"],
    "countries": ["us"],
    "companySizeMin": 51,
    "companySizeMax": 200,
    "maxLeads": 100
}
```

### Sample output row

| fullName | jobTitle | companyName | companyDomain | email | emailStatus | city | country |
|---|---|---|---|---|---|---|---|
| Bill McLoone | VP Sales | Amerex Group LLC | amerexgroup.com | bmcloone@amerexgroup.com | valid | Princeton Junction | us |

Full column list: `fullName, firstName, lastName, jobTitle, seniority, headline, linkedinUrl, city, country, companyName, companyDomain, companyLinkedinUrl, companySize, industry, email, emailStatus, phoneNumbers, personalEmailAvailable, tenureStartYear, profileId, sourceQuery, scrapedAt`.

`companyLinkedinUrl` is populated via an extra per-company lookup (cached across leads at the same company) when `includeCompanyDetails` is on. It's `null` when that lookup can't confidently match the company — domains are sometimes shared by unrelated companies, and this actor only fills the field when there's exactly one unambiguous name match, never a best guess. `emailStatus` reflects the real SMTP check result for that email (`valid` when confirmed deliverable; another value when it isn't) — every row has an email either way.

### Use cases

- **SDR outbound** — build a targeted call/email list by ICP in minutes, no manual LinkedIn scrolling.
- **Agency lead lists** — deliver client-ready lead lists without a per-client Sales Navigator seat.
- **Recruiting** — find people by title and seniority at target companies for outreach.

### Free preview before you spend anything

Set `previewOnly: true` to see the estimated audience size for your filters — this charges nothing. Confirm your ICP is big enough before running for real.

### An Apollo / ZoomInfo alternative

Like Apollo, ZoomInfo, Lusha and RocketReach, this actor turns an ICP into a contact list with emails. Unlike a Sales Navigator scraper (Apollo's and most competitors' underlying data source for LinkedIn), it needs no login and can't get your LinkedIn account flagged. Unlike a per-seat SaaS subscription, you pay only for delivered leads.

### Avoid paying twice for the same lead

Pass `excludeProfileIds` (the `profileId` column from a previous run) on your next pull to skip leads you already have.

### FAQ

**Do I need a LinkedIn account or Sales Navigator?** No — this actor never logs into LinkedIn.

**Is this GDPR / CAN-SPAM compliant?** The actor returns B2B professional contact data. You are responsible for using it in a lawful, compliant manner for your jurisdiction and use case (opt-out handling, consent basis, etc.) — this actor does not provide legal advice.

**How fresh is the data?** Profile records refresh on the upstream provider's own cadence (typically weeks, not real-time). `scrapedAt` records when your run pulled the row, not when the underlying profile last changed.

**Do you verify emails?** Every candidate is domain-matched against the target company first (rejects stale prior-employer or personal addresses), then SMTP-checked. We deliver every domain-matched email we find — the `emailStatus` column tells you honestly whether that specific one came back confirmed-deliverable or not, and it's priced accordingly.

**Why did I get fewer leads than `maxLeads`?** Only candidates with a real, domain-matched email count as a lead — not every profile in your ICP has a discoverable email. The scan budget isn't unlimited either: the actor scans up to 15× `maxLeads` candidates (50× with `requirePhone`) before stopping, to keep runs bounded. Widen your filters if you're hitting that ceiling often.

### Related actors on this account

- [LinkedIn People Search](https://apify.com/b2bdata/linkedin-people-search) — point lookups by name/company
- [LinkedIn Person Search](https://apify.com/b2bdata/linkedin-person-search)
- [LinkedIn Employee Search](https://apify.com/b2bdata/linkedin-employee-search) — full roster of a specific company
- [LinkedIn Company Search](https://apify.com/b2bdata/linkedin-company-search) — find company pages

This actor is for **discovering new leads from an ICP description**, not looking up a specific person or company you already know — use the actors above for that.

### Compliance

This actor returns B2B professional contact data intended for lawful business outreach. You are responsible for complying with applicable law (GDPR, CAN-SPAM, and similar) in how you use the data.

# Actor input Schema

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

Current job title contains any of these (e.g. "VP Sales", "Head of Growth"). At least one of jobTitles, seniority, countries, cities or companyDomains is required. Leave empty if you're targeting specific companies via companyDomains instead.

## `titleMatch` (type: `string`):

Broad = substring match. Exact words = whole-word match only (fewer false positives, e.g. avoids "VP Sales" matching "VP of Sales Development Representative Sourcing").

## `seniority` (type: `array`):

Filter by seniority level. Leave empty to skip this filter.

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

Lowercase ISO country codes, max 5 (e.g. "us", "gb", "de"). Upstream data gap: many profiles have this field blank even when their city clearly shows the country — combining this with companyDomains on a specific company can silently return 0 real matches. Leave empty when targeting specific companies via companyDomains.

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

City or region name, substring match (e.g. "San Francisco").

## `companySizeMin` (type: `integer`):

Leave blank for no minimum.

## `companySizeMax` (type: `integer`):

Leave blank for no maximum.

## `includeUnknownSize` (type: `boolean`):

Only applies when a min/max company size is set.

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

Exact LinkedIn industry name, case-insensitive (e.g. "Computer Software", "Information Technology and Services", "Financial Services", "Marketing and Advertising") — not free text. A wrong name (e.g. "Software") fails the run with no charge. The filter matches the employer's industry classification, which can differ from the `industry` column in the results. Test first with Preview only (free). Leave empty to skip.

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

Restrict to these companies (bare domains, e.g. "stripe.com"). Leave empty for open discovery.

## `excludeTitleWords` (type: `array`):

Drop candidates whose title contains any of these (e.g. "Intern", "Assistant").

## `excludeHeadlineWords` (type: `array`):

Drop candidates whose headline contains any of these (e.g. "retired", "open to work").

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

Maximum number of leads to deliver (a lead has a real, company-domain-matched email). Capped at 2000 per run.

## `maxPerCompany` (type: `integer`):

Cap how many leads come from the same company, so one large employer doesn't dominate the list. Leave blank for no cap.

## `includeCompanyDetails` (type: `boolean`):

Look up each company's LinkedIn URL (one extra lookup per unique company, cached across leads at the same company). Off = companyLinkedinUrl stays blank, fewer upstream calls, same price.

## `requirePhone` (type: `boolean`):

Only deliver leads that also have a phone number on file (roughly 1 in 6 people do), returned in the phoneNumbers column. Same price per lead, but the run scans many more people, so it takes longer and may deliver fewer than Max leads. Numbers are as listed in the database — not verified, and some may be company switchboard lines.

## `excludeProfileIds` (type: `array`):

Profile IDs to exclude — paste the profileId column from a previous run to avoid re-paying for leads you already have.

## `previewOnly` (type: `boolean`):

Return only the estimated audience size for these filters. Charges nothing. Use this to size your list before running for real.

## Actor input object example

```json
{
  "jobTitles": [
    "VP Sales"
  ],
  "titleMatch": "broad",
  "seniority": [],
  "countries": [
    "us"
  ],
  "cities": [],
  "includeUnknownSize": false,
  "industries": [],
  "companyDomains": [],
  "excludeTitleWords": [],
  "excludeHeadlineWords": [],
  "maxLeads": 10,
  "includeCompanyDetails": true,
  "requirePhone": false,
  "excludeProfileIds": [],
  "previewOnly": false
}
```

# Actor output Schema

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

One row per qualified lead: person, current company, and email (verified per your emailMode setting)

# 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": [
        "VP Sales"
    ],
    "countries": [
        "us"
    ],
    "maxLeads": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("b2bdata/linkedin-lead-generation").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": ["VP Sales"],
    "countries": ["us"],
    "maxLeads": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("b2bdata/linkedin-lead-generation").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": [
    "VP Sales"
  ],
  "countries": [
    "us"
  ],
  "maxLeads": 10
}' |
apify call b2bdata/linkedin-lead-generation --silent --output-dataset

```

## MCP server setup

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

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/lQLG2LobPzXOjrTpC/builds/Pck9TddNNfb9sMUDw/openapi.json
