# LinkedIn People Search — 16 Filters ✅ $1.50/1k, No Login (`b2bsearch/people-database-search`) Actor

LinkedIn people search across 800M+ professional profiles: filter by job title, seniority, employer, size, industry, past employer and city. $1.50 per 1,000 people delivered — never per search; full profiles $3.20, with emails and phones $8. Require email, phone or GitHub; the rest are skipped free.

- **URL**: https://apify.com/b2bsearch/people-database-search.md
- **Developed by:** [B2B Enrich Search](https://apify.com/b2bsearch) (community)
- **Categories:** Lead generation, Social media, AI
- **Stats:** 1 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.50 / 1,000 person delivereds

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

> **$1.50 per 1,000 people delivered. You pay per person, never per
> search.** Repeats, people skipped by your `mustHave` list and the count
> preview cost nothing.

Search a database of over 800 million professional profiles by country,
job title, seniority, employer, employer size and industry, past employer,
career length and city, and see how big the segment is for free before you
take a row. Take plain rows, full profiles or profiles with emails and
phones.

This is a database search, not a live scrape: results in seconds, a
freshness stamp on every row, no 2,500-row cap.

### Free count before you buy

A run returns people. To size a segment first, set `previewOnly: true`: the
Actor answers with one free row, the number of matching people, and a note
saying so. Tune the filters, then turn preview off to fetch the rows. The
count does not apply `mustHave`.

### Input

```json
{
  "countries": ["nl", "de"],
  "titleKeywords": ["founder", "cto"],
  "seniority": ["cxo", "founder"],
  "employeeCountMin": 50,
  "employeeCountMax": 500,
  "profileDetail": "row",
  "mustHave": ["personalEmail"],
  "previewOnly": false,
  "maxResults": 500
}
```

**Who they are**

- `countries`: required, two-letter codes, up to 5.
- `titleKeywords` / `headlineKeywords`: any of the words in the current
  title or headline, up to 10 each.
- `seniority`: `cxo`, `vp`, `director`, `manager`, `founder`, `other`,
  assigned from the title ("CTO", "Chief Technology Officer" and "Head of
  Technology" all land in `cxo`).
- `excludeTitleKeywords` / `excludeHeadlineKeywords`: drop rows you never
  want (junior, intern, student).
- `localityKeywords`: city or region inside the countries.
- `connectionsMin`: a floor on network size.

**Where they work**

- `companyDomains`: currently at these companies, by site domain (up to
  200\).
- `employerIndustries`: exact labels, as in the `companyIndustry` column.
- `employeeCountMin` / `employeeCountMax`: employer headcount band.

**Where they have been**

- `pastEmployerDomains`: people who used to work somewhere (up to 20). A
  domain claimed by many businesses resolves to the largest employer on it.
  People still working there are dropped (an internal promotion also closes
  a position); set `stillThereCounts` to keep them.
- `leftPastEmployerAfterYear`: recent leavers, together with the above.
- `pastTitleKeywords`: people who used to hold a title.
- `careerStartYearMin` / `careerStartYearMax`: experience band by first
  recorded position.
- `currentRoleStartedAfterYear`: recent job changers.

**What to return and run control**

- `profileDetail`: `row` (default, $1.50 per 1,000), `profile` ($3.20 per
  1,000, adds the whole career card) or `contacts` ($8 per 1,000 people
  with a working contact, adds emails and phones). Richer tiers cap a run
  at 10,000 people.
- `mustHave`: see below. (`requireEmail`, the old toggle, still works and
  means `personalEmail`.)
- `requireCurrentPosition`: drop rows with no known current employer.
- `excludeLowSignal`: drop self-declared profiles before they are counted.
- `previewOnly` (default false) and `maxResults`, a hard cap on rows and
  spend.

A country alone is refused outside preview: it matches tens of millions of
people.

With `contacts`, the $8 price applies only to a person with a contact that
reaches them today (a personal mailbox, an address on their current
employer's domain, or a phone the service labels direct). Everyone else
ships at the $3.20 profile price. Phone labels arrive with the next data
rebuild; until then numbers ship unlabelled and never earn the contacts
price by themselves.

How often that is, measured on 30 September 2026 over 200 employed
professionals per country: the contacts price applied to 53% of people in
the US, 42% in India, 40% in France, 33% in the UK and 32% in Germany.
Everyone else shipped at the lower price.

### Only people who have what you need: `mustHave`

List what a person must have to be delivered: `email`, `personalEmail`,
`workEmail`, `currentWorkEmail`, `phone`, `github`, `twitter`, `facebook`.
People who do not qualify are skipped for free, and one `skipped_must_have`
row at the end says how many.

- `email`, `personalEmail`, `workEmail` work on every tier: the rows carry
  those flags already.
- `github`, `twitter`, `facebook` need the profile tier.
- `phone` and `currentWorkEmail` need the contacts tier.

A requirement the chosen tier cannot check is refused before the run
starts. Skipped people are free, so the walk reads ahead of what it
delivers, up to a ceiling; if it stops there, a `skipped_scan_limit` row
says so. Narrow the filters to reach further.

### What you get

| Group | Fields |
|---|---|
| Identity | full name, headline, `profileUrl`, slug |
| Current role (flat columns) | `jobTitle`, `companyName`, `companyDomain`, `companyIndustry`, `companyEmployeeCount` |
| All current positions | title, seniority bucket, start year, each with a live employer card (domain, country, headcount, industry, funding, founding year) |
| Location | city, `countryCode` |
| Email flags | `hasPersonalEmail`, `hasWorkEmail` |
| Signal | `lowSignal`, connection count, first position year |
| Richer tiers | the career card (`profile`), plus `email`, `emailType`, `employerMatch`, `workEmails`, `personalEmails`, `emailCount`, `phone`, `phones` (`contacts`); `_detail` says which tier the row carries |
| Freshness | `_freshness` bucket and exact `updatedAt` |

On the richer tiers the job title and employer columns come from the live
profile: when someone moved since the search data was built, you see the
new employer, not the old one.

A search row (anonymized sample):

```json
{
  "_status": "found",
  "_freshness": "fresh_90d",
  "jobTitle": "CTO & Co-founder",
  "companyName": "Acme Analytics",
  "companyDomain": "acme-analytics.example",
  "companyIndustry": "Software Development",
  "companyEmployeeCount": 85,
  "fullName": "Jane Doe",
  "headline": "CTO & Co-founder at Acme Analytics",
  "countryCode": "nl",
  "locality": "Amsterdam",
  "connectionsCount": 2214,
  "hasPersonalEmail": true,
  "hasWorkEmail": true,
  "currentPositions": [
    {
      "title": "CTO & Co-founder",
      "seniority": "cxo",
      "startYear": 2019,
      "company": { "name": "Acme Analytics", "domain": "acme-analytics.example", "employeeCount": 85, "founded": 2018 }
    }
  ],
  "profileUrl": "https://www.linkedin.com/in/jane-doe-example"
}
```

The **Output tab** has two views: *Overview* and *Current positions*.

### Pricing: per person delivered

| Event | Price | When |
|---|---|---|
| Person delivered | $0.0015 | one distinct person, search row |
| Person delivered (full profile) | $0.0032 | `profile`, or `contacts` without a working contact |
| Person delivered (profile + contacts) | $0.008 | `contacts` and a contact that reaches the person today |
| Count preview, repeats, `mustHave` skips | **$0** | always free |

The store keeps historical snapshots of a person under separate ids (26% of
rows on a full segment we measured). This Actor remembers who it has
delivered: a repeat never reaches your dataset or your bill. Up to 50,000
rows per run.

### FAQ

**How do I get emails for these people?** Set `profileDetail` to
`contacts`, or add `mustHave: ["personalEmail"]` to keep only people with a
personal mailbox on record at the row price.

**Why was my single-country run refused?** A country alone matches tens of
millions of people. Add a title, headline or company filter, or run the free
preview first.

### Use LinkedIn People Search with AI agents and MCP

This Actor works as a tool for AI agents. Add it to Claude, ChatGPT, Cursor or any other MCP client through the Apify MCP server:

```
https://mcp.apify.com?tools=b2bsearch/people-database-search
```

- **Fast enough for a tool call.** A small request finishes in seconds, so the agent gets its answer inside one call.
- **Free count first.** `"previewOnly": true` returns the number of matches and charges nothing — let the agent size the segment before it buys rows.
- **The agent pays only for results.** Prices are per result (see the pricing section above) and every miss is a free row. Cap what one call may spend with `maxTotalChargeUsd`.
- **Rows explain themselves.** Each row has a `_status`; a row that is not a result says why in `_error`, so the agent can decide what to do next without guessing.
- **Compact rows for a context window.** `"compact": true` returns a short row — about 2 KB for a full profile instead of 10+ KB: identity, current role, the 5 latest positions, education, top skills and any contacts. Same price; leave it off to get the complete record.
- **Bounded cost.** `maxResults` limits how many rows one call can deliver and bill.

Input an agent can send as is:

```json
{
  "countries": [
    "nl"
  ],
  "titleKeywords": [
    "founder"
  ],
  "maxResults": 25,
  "profileDetail": "row",
  "excludeLowSignal": false,
  "compact": true
}
```

The same Actor is available as a tool in LangChain, CrewAI and the OpenAI Agents SDK, and as a step in n8n, Make and Zapier through the Apify integrations.

### Which actor in this family?

One database, ten doors. Misses are free on every one of them.

| Actor | Input → output |
|---|---|
| [Profile Lookup](https://apify.com/b2bsearch/profile-lookup) | profile URL → full career profile |
| [Reverse Email Lookup](https://apify.com/b2bsearch/reverse-email-lookup) | email → person, profile URL and employer |
| [Name to Profile](https://apify.com/b2bsearch/name-to-profile) | name + company domain → profile |
| [Social Handle Lookup](https://apify.com/b2bsearch/social-handle-lookup) | GitHub, X/Twitter or Facebook handle → profile |
| [Bulk People Enrichment](https://apify.com/b2bsearch/bulk-people-enrichment) | CSV of emails, URLs, handles or names → profiles |
| [LinkedIn Email Finder](https://apify.com/b2bsearch/linkedin-email-finder) | profile URL → email addresses on record |
| [Work Email Finder](https://apify.com/b2bsearch/work-email-finder) | name + company domain → work email candidates |
| [Company Employees](https://apify.com/b2bsearch/company-employees) | company domain → current staff |
| **People Database Search** (this one) | filters → people |
| [Company Database Search](https://apify.com/b2bsearch/company-database-search) | filters → companies |

### Disclaimer

This Actor is an independent product. It is not affiliated with, endorsed by
or sponsored by LinkedIn. It does not access, crawl or scrape any of them at
run time: answers come from our own database of publicly available
professional data, and the network names only describe the kind of data it
covers. To have a person's data removed, open an issue on this Actor with only
the profile link — nothing else is needed, and it is removed from every listing.

# Actor input Schema

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

Two-letter country codes (us, de, gb...). At most 5 per run.

## `titleKeywords` (type: `array`):

Match any of these words in the current job title (founder, cto, "vice president"...). Each 3-64 characters, at most 10.

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

Keep only people currently working at companies with these site domains. At most 200.

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

Keep only people whose current role falls in these seniority buckets. Buckets are assigned from the job title, so they work across title spellings ('CTO', 'Chief Technology Officer', 'Head of Technology' all land in cxo). At most 6.

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

Stop after this many people (default 500, maximum 50,000). You pay per person delivered — $1.50 per 1,000 in the row tier — so set it to the number you need.

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

Answer with the number of matching people instead of the rows. Free — run it first to size a segment before you buy. Off by default: a run returns rows.

## `profileDetail` (type: `string`):

You pay per person delivered, never per search. row = name, title, employer, location and email flags, $1.50 per 1,000; profile = the full career profile, $3.20 per 1,000; contacts = the profile plus emails and phones, $8 per 1,000 people with a contact that reaches them today (the rest at the profile price). Profile tiers cap a run at 10,000.

## `mustHave` (type: `array`):

Deliver and charge only people who have all of these; the rest are skipped for free and counted in one note row. Personal and work email work on every tier; social profiles need the full profile; phone and "work email at the current employer" need emails & phones. The free preview count does not apply this list.

## `compact` (type: `boolean`):

Returns a short row instead of the full record: identity, current role, location, the 5 latest positions, education, top skills and any contacts — about 2 KB per person instead of 10+ KB. Made for AI agents and LLM pipelines with a context limit. Same price. Off by default: a run returns the complete record.

## `headlineKeywords` (type: `array`):

Match any of these words in the profile headline. Each 3-64 characters, at most 10.

## `pastTitleKeywords` (type: `array`):

Match any of these words in a PAST job title — people who used to be something. 3-64 characters each, at most 10.

## `excludeTitleKeywords` (type: `array`):

Drop people whose current title contains any of these words (example: junior, assistant, intern). 3-64 characters each, at most 10.

## `excludeHeadlineKeywords` (type: `array`):

Drop people whose headline contains any of these words (example: student, retired, freelance). 3-64 characters each, at most 10.

## `pastEmployerDomains` (type: `array`):

Keep only people who USED to work at these companies — give site domains (stripe.com), they are resolved to employers for you. Pair with 'left after year' to find recent leavers. At most 20.

## `leftPastEmployerAfterYear` (type: `integer`):

Only counts with 'Past employer site domains': keep people whose last day there falls in this year or later.

## `stillThereCounts` (type: `boolean`):

Off by default. A past employer match means a completed position there — and an internal promotion also closes one, so roughly a third of matches are people who never left (measured: 668 of 2,102 on one employer). By default those are dropped and you get actual leavers; turn this on to keep everyone who ever held a role there.

## `localityKeywords` (type: `array`):

Narrow inside the chosen countries by city or region text (example: san francisco, greater london). 3-64 characters each, at most 10.

## `employerIndustries` (type: `array`):

Keep only people whose current employer is in these industries. Industry names, case-insensitive, in either the current or the older wording (Software Development also finds Computer Software) — examples: Software Development, IT Services and IT Consulting, Financial Services, Hospitals and Health Care, Advertising Services.

## `employeeCountMin` (type: `integer`):

Keep only people at employers with at least this many employees.

## `employeeCountMax` (type: `integer`):

Keep only people at employers with at most this many employees.

## `excludeLowSignal` (type: `boolean`):

Drop people with under 10 connections and no known career start — the self-declared "CEO at <famous brand>" pattern. Off by default: across the whole database many such profiles are sparse but real, so turn it on for leadership searches at well-known companies. Every row carries the lowSignal column either way.

## `connectionsMin` (type: `integer`):

Keep only people with at least this many connections — a rough proxy for an active, established profile.

## `careerStartYearMin` (type: `integer`):

Year of the person's first recorded position. Use with 'career started in or before' to target experience bands (a 2015 start is roughly 10 years of experience).

## `careerStartYearMax` (type: `integer`):

Year of the person's first recorded position.

## `currentRoleStartedAfterYear` (type: `integer`):

Recent job changers: keep only people whose current position began in this year or later.

## `requireCurrentPosition` (type: `boolean`):

Drop rows where no current employer is known.

## Actor input object example

```json
{
  "countries": [
    "nl"
  ],
  "titleKeywords": [
    "founder"
  ],
  "maxResults": 25,
  "previewOnly": false,
  "profileDetail": "row",
  "compact": false,
  "stillThereCounts": false,
  "excludeLowSignal": false,
  "requireCurrentPosition": false
}
```

# Actor output Schema

## `results` (type: `string`):

No description

# 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 = {
    "countries": [
        "nl"
    ],
    "titleKeywords": [
        "founder"
    ],
    "maxResults": 25,
    "profileDetail": "row",
    "excludeLowSignal": false
};

// Run the Actor and wait for it to finish
const run = await client.actor("b2bsearch/people-database-search").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 = {
    "countries": ["nl"],
    "titleKeywords": ["founder"],
    "maxResults": 25,
    "profileDetail": "row",
    "excludeLowSignal": False,
}

# Run the Actor and wait for it to finish
run = client.actor("b2bsearch/people-database-search").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 '{
  "countries": [
    "nl"
  ],
  "titleKeywords": [
    "founder"
  ],
  "maxResults": 25,
  "profileDetail": "row",
  "excludeLowSignal": false
}' |
apify call b2bsearch/people-database-search --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,b2bsearch/people-database-search"
        }
    }
}
```

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/WsaetcgVbECzOTGec/builds/uyVyxxlMz70CDOPwd/openapi.json
