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

Search 1B+ professional profiles by job title, seniority, employer, employer size and industry, past employer, city and career length — $2 per 1,000 distinct people, half what the busiest Actor on this shelf charges for the same page. Every person once. Free count preview, 50,000 rows a run.

- **URL**: https://apify.com/sputnikapi/people-database-search.md
- **Developed by:** [Sputnik API](https://apify.com/sputnikapi) (community)
- **Categories:** Lead generation, AI, Agents
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $100.00 / 1,000 result page (50 distinct people)s

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

## LinkedIn People Search — filter a database of 1B+ profiles

> **$2 per 1,000 distinct people — half the usual shelf price**, and the
> count preview is free, so you size a segment before you buy a row. Every
> person is delivered once: repeats never reach your dataset and never
> reach your bill.

Search a database of **over one billion professional profiles** — by
country, job title, seniority, employer, employer size and industry, past
employer, career length, city and more — and see how big your segment is
**for free** before you buy a single row.

This is a **database search, not a live scrape**: no per-profile crawling,
no row caps in the thousands, results in seconds with a freshness stamp on
every row.

### What it costs against the shelf

Search Actors on this platform commonly charge about $0.10 per 25-result
page — $4 per 1,000 people — and bill you whether or not the segment turned
out to be the one you wanted.

| | Typical shelf price | Here |
|---|---|---|
| 1,000 people matching your filters | ~$4 | **$2** |
| Seeing the segment size before buying | not offered | **free** |
| Export ceiling per query | 2,500 rows is common | **50,000 rows per run** |

(Shelf price read off public Apify listings on 2026-09-17.)

### Free preview first

Run with `previewOnly: true` (the default) and the actor answers with one
free row: the total number of matching people. Size the segment, tune the
filters, then uncheck preview to fetch the rows.

### Input

```json
{
  "countries": ["nl", "de"],
  "titleKeywords": ["founder", "cto"],
  "seniority": ["cxo", "founder"],
  "employeeCountMin": 50,
  "employeeCountMax": 500,
  "requireEmail": true,
  "previewOnly": true,
  "maxResults": 500
}
```

**Who they are**

- `countries` — required, two-letter codes, up to 5.
- `titleKeywords` / `headlineKeywords` — match 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, so it catches every spelling: "CTO", "Chief
  Technology Officer" and "Head of Technology" all land in `cxo`.
- `excludeTitleKeywords` / `excludeHeadlineKeywords` — drop the rows you
  never want (junior, intern, student, retired).
- `localityKeywords` — narrow inside the countries by city or region.
- `connectionsMin` — a floor on network size, a rough proxy for an
  established profile.

**Where they work**

- `companyDomains` — currently at these companies, named by site domain
  (up to 200). Domains resolve to employers for you.
- `employerIndustries` — exact industry labels, as they appear in the
  `companyIndustry` column of result rows.
- `employeeCountMin` / `employeeCountMax` — employer headcount band, the
  fastest way to separate startups from enterprises.

**Where they have been**

- `pastEmployerDomains` — people who *used to* work somewhere, named by site
  domain (up to 20). A domain claimed by many businesses resolves to the
  largest employer on it, which is the one you meant. People still working
  there are dropped: an internal promotion also closes a position, and on
  the employer we measured that was 668 of 2,102 matches. Set
  `stillThereCounts` if you want everyone who ever held a role there.
- `leftPastEmployerAfterYear` — pair it with the above for recent leavers.
- `pastTitleKeywords` — people who *used to* hold a title.
- `careerStartYearMin` / `careerStartYearMax` — experience band by the year
  of the first recorded position (a 2015 start is roughly ten years in).
- `currentRoleStartedAfterYear` — recent job changers.

**Reachability and run control**

- `requireEmail` — only people with a personal email on record (the address
  itself comes from the profile Actors).
- `requireCurrentPosition` — drop rows with no known current employer.
- `previewOnly` (default true) — free count instead of rows.
- `maxResults` — hard cap on rows, and therefore on spend.

A country alone is refused outside preview mode — it matches tens of
millions of people; add one more filter first.

### What you get — every row is a person + employer card

| Group | Fields |
|---|---|
| Identity | full name, first/last, headline, profile URL, slug |
| Current role (flat columns) | `jobTitle`, `companyName`, `companyDomain`, `companyIndustry`, `companyEmployeeCount` — hoisted from the primary position for instant spreadsheets |
| All current positions | title, seniority bucket, start year — **each with a live employer card**: company name, domain, website, country, headcount, industry, verticals, funding summary, founding year, HQ |
| Location | city, country code |
| Reachability | `hasPersonalEmail`, `hasWorkEmail` — know who is contactable before enriching |
| Network size | connection count |
| Career depth | first position year (career start) |
| Freshness | `_freshness` bucket + exact `updatedAt` |
| Upsell | `_fullData` — the same person's complete 164-field document via the direct API |

A real row (trimmed for display):

```json
{
  "_status": "found",
  "_freshness": "fresh_90d",
  "jobTitle": "CTO & Co-founder",
  "companyName": "Acme Analytics",
  "companyDomain": "acme-analytics.example",
  "companyIndustry": "Software Development",
  "companyEmployeeCount": 85,
  "personId": 123456789,
  "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,
      "companyName": "Acme Analytics",
      "company": {
        "name": "Acme Analytics",
        "domain": "acme-analytics.example",
        "countryCode": "NL",
        "employeeCount": 85,
        "industry": "Software Development",
        "funding": { "hasFunding": true, "lastRoundType": "Series A", "lastRoundDate": "2023-05-01" },
        "founded": 2018
      }
    }
  ],
  "profileUrl": "https://www.linkedin.com/in/…"
}
```

*(anonymized sample of the row shape — run a preview + one page to see live
rows)*

The **Output tab** shows two views: *Overview* (one line per person) and
*Current positions* (with the nested employer cards).

### Pricing — per page of distinct people

| Event | Price | When |
|---|---|---|
| Result page (up to 50 distinct people) | $0.10 | after the rows landed in your dataset |
| Count preview | **$0** | always free |

≈ **$2 per 1,000 people** — set `maxResults` to cap the spend of a run. No
2,500-row export cap: up to 50,000 rows per run. Filters are free: narrowing
a segment costs nothing, only the people you take are billed.

**A page means fifty different people.** The store keeps historical snapshots
of a person under separate ids, so a raw walk of a segment hands back the
same human several times — measured on a full segment at 26% of rows. This
Actor remembers who it has already delivered: repeats are dropped, the walk
keeps going until the page is genuinely full, and a round trip that returns
nothing new costs you nothing.

### FAQ

**How do I get emails for these people?** Each row carries `hasPersonalEmail`
/ `hasWorkEmail` flags and a `_fullData` link; feed the `profileUrl` column
to our **Bulk People Enrichment (CSV)** actor to turn rows into full
profiles (`fullOutput: true` includes the contact block), or the
**Work Email Finder** for name + domain → verified work email.

**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 to size it first.

### The family

Same engine, other doors: **Reverse Email Lookup** (email → person),
**Profile Lookup** (URL → profile), **Work Email Finder** (name + domain →
email), **Company Employees Finder** (domain → roster), **Company Database
Search** (filters → companies), **Name-to-Profile**, **Social Handle
Lookup**, **Bulk People Enrichment** (CSV, 50k rows). One data core, one
billing promise: misses are free.

### Disclaimer

This Actor is an independent product and is not affiliated with, endorsed
by, or sponsored by LinkedIn Corporation. It does not access, crawl, or
scrape LinkedIn at run time — answers come from our own database of
publicly available professional data; "LinkedIn" is used only to describe
the kind of public profile data the database covers. Removal requests are
honored via the Issues tab.

# 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.

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

Match any of these words in the profile headline. 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.

## `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. Values must match exactly as they appear in the companyIndustry column of result rows — examples: Computer Software, Information Technology & Services, Financial Services, Hospital & Health Care, Marketing & Advertising.

## `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.

## `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.

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

Keep only rows where a personal email address is known to exist (the address itself is available through the full profile API).

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

Answer with the matching-people count instead of result pages. Free — size the segment before you buy pages.

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

Stop after this many result rows (default 500, maximum 50000). Billing is per page of up to 50 rows.

## Actor input object example

```json
{
  "countries": [
    "nl"
  ],
  "titleKeywords": [
    "founder"
  ],
  "stillThereCounts": false,
  "requireCurrentPosition": false,
  "requireEmail": false,
  "previewOnly": true,
  "maxResults": 500
}
```

# 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"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("sputnikapi/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"],
}

# Run the Actor and wait for it to finish
run = client.actor("sputnikapi/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"
  ]
}' |
apify call sputnikapi/people-database-search --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,sputnikapi/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/g77ykeDlXiJLDgd0P/openapi.json
