# LinkedIn People Search Scraper (`agnes.developer.queen/linkedin-people-search-scraper`) Actor

LinkedIn people search scraper with no login or cookies. Find profiles by job title, company and location. $0.015 per profile, charged only when the person's own headline confirms the title and their public profile confirms the current company. Other rows are free.

- **URL**: https://apify.com/agnes.developer.queen/linkedin-people-search-scraper.md
- **Developed by:** [Agnes Maina](https://apify.com/agnes.developer.queen) (community)
- **Categories:** Lead generation, Social media
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $15.00 / 1,000 matched profiles

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 Scraper: find profiles by job title and company, no login

[![Run on Apify](https://apify.com/actor-badge?actor=agnes.developer.queen/linkedin-people-search-scraper)](https://apify.com/agnes.developer.queen/linkedin-people-search-scraper)

This LinkedIn people search scraper finds public LinkedIn profiles by job title, company and location, with no LinkedIn login and no cookies. You pay $0.015 per profile only when the person's own headline confirms the title and their public profile confirms the company as a current employer, plus $0.005 per run, and every other row is delivered free with the reason.

### What you get

One row per person found. Every field below comes from the Actor's dataset schema.

- `fullName`: name as shown on LinkedIn
- `headline`: the person's own LinkedIn headline
- `location`: location when Google shows it
- `profileUrl`: public LinkedIn profile URL (null when Google hid the link and it could not be resolved)
- `isDecisionMaker`: true for owner, partner, C level, VP or director
- `seniorityBand`: owner, partner, cxo, vp, director, manager, senior, entry or unknown
- `department`: function read from the headline, for example sales, finance or engineering
- `matchedTitle`: the searched title the headline confirmed
- `matchedCompany`: the searched company the headline names as current employer
- `verifiedEmployer`: with strict match, the searched company the person's own profile data names as a current employer
- `profileEmployers`: with strict match, every current employer the public profile lists
- `charged`: true when this row was billed as a matched profile
- `reason`: why the row was or was not billed
- `searchedTitles`, `searchedCompany`, `searchedLocation`: the search that found the person
- `scrapedAt`: when the row was collected, ISO 8601

Each run also writes a `RUN_SUMMARY` record to its key-value store.

What it does not return: email addresses, phone numbers, full work history, skills, education, private profiles, people Google does not index, or Sales Navigator data. Strict match checks the person's own public LinkedIn profile, not the employer, so several people can still list themselves as CTO of the same company.

### Try it in 30 seconds

1. Open the [Actor page in the Apify Store](https://apify.com/agnes.developer.queen/linkedin-people-search-scraper) and click **Try for free**.
2. Paste the job titles you sell to or hire for, one per line, for example `VP of Sales` and `Head of Sales`.
3. Paste the companies on your list, as names (`Stripe`) or LinkedIn company URLs (`https://www.linkedin.com/company/stripe/`).
4. Click **Start**. When the run finishes, open the Profiles table or download JSON, CSV or Excel.

Every Apify account gets $5 of free platform credit each month, which covers the start fee plus more than 300 confirmed profiles at this price.

### Why you only pay for confirmed matches

A Google search for CFOs at a company also returns finance analysts, former CFOs, assistants and people at other companies with a similar name. Most people search tools bill every one of those rows. This Actor reads each person's own headline and bills only the rows that prove the match. The rest are still delivered, free, with the reason.

```json
[
    { "headline": "Chief Financial Officer, Asana", "matchedTitle": "CFO", "matchedCompany": "Asana", "charged": true, "reason": "title and current company confirmed in own headline" },
    { "headline": "Chief Financial Officer at Linear Controls", "matchedTitle": null, "charged": false, "reason": "current company not confirmed" },
    { "headline": "Executive Business Partner to CTO @ Vercel", "matchedTitle": null, "charged": false, "reason": "title not in own headline" },
    { "headline": "Head of Stock Administration at Asana", "matchedTitle": null, "charged": false, "reason": "title not in own headline" }
]
```

These are real headlines from a test run. A match needs the company in the employer spot for that title, as in "CFO at Stripe", "CFO | Stripe" or "Stripe CFO". So "CMO @ G2 || Advisor || Zoom" is not billed as Zoom's CMO, a name Google cut off with "..." is not trusted, and past or support roles such as "ex-Stripe", "Retired CFO" or "Assistant to the CFO" never count.

A headline can still say anything. With strict match (on by default) the Actor also opens the person's public profile before charging and bills only when the profile is readable and LinkedIn's own profile data lists a company you searched as a current employer. In a test run, four of six rows whose headline said "Chief Executive Officer at Stripe" or "at Asana" were days-old profiles that LinkedIn does not show publicly. Strict match delivers those free with the reason `profile is not publicly readable, so the match is not verified`.

Titles match their common forms, so a CFO search also finds "Chief Financial Officer", and a "VP of Sales" search also finds "Vice President, Sales" and "SVP of Sales".

### How it works

```
job titles + companies + locations
            |
            v
Google search of public LinkedIn profiles
            |
            v
name, headline, location and profile URL for each person
            |
            v
charge bar: title and current company in the same part of the person's own headline
            |
            +--> not confirmed  -->  delivered free, with the reason
            |
            v
strict match: the public profile is readable and names the company as a current employer
            |
            +--> confirmed      -->  Matched profile (charged)
            |
            +--> not confirmed  -->  delivered free, with the reason
```

### Example output

One full record from a real run.

```json
{
    "fullName": "Patrick Collison",
    "headline": "Stripe CEO",
    "location": "",
    "profileUrl": "https://www.linkedin.com/in/patrickcollison/",
    "isDecisionMaker": true,
    "seniorityBand": "cxo",
    "department": "executive",
    "matchedTitle": "CEO",
    "matchedCompany": "Stripe",
    "verifiedEmployer": "Stripe",
    "profileEmployers": ["Stripe", "Arc Institute"],
    "charged": true,
    "reason": "title in own headline, current employer confirmed on profile",
    "searchedTitles": ["CEO", "CFO"],
    "searchedCompany": "Stripe",
    "searchedLocation": null,
    "scrapedAt": "2026-09-23T22:44:25.660Z"
}
```

### Pricing

Pay per event. No subscription, no monthly rental.

| Event | Price | You pay when |
|---|---|---|
| Actor start | $0.005 | once per run |
| Matched profile | $0.015 | a person's own headline confirms a title you searched, and their public profile confirms the company you searched as a current employer |
| Any other row | $0 | delivered with `charged: false` and a `reason` |

Worked example: a run that returns 100 confirmed profiles costs 100 x $0.015 + $0.005 = $1.505. If the same run also delivered 60 rows that did not confirm, those 60 cost nothing. That is $15 per 1,000 matched profiles.

### How this compares

Store data pulled on 2026-09-30 for the most used LinkedIn people search and profile search Actors. Prices are each Actor's current published pricing on the free plan.

| Actor | Total users | Price per result | Login or cookies required? | Charges for non-matching rows? | Confirmation check |
|---|---|---|---|---|---|
| This Actor | 2 | $0.015 per confirmed profile + $0.005 per run | No | No, delivered free with a reason | Title in own headline, current employer on public profile |
| harvestapi/linkedin-profile-search | 45,728 | $0.10 per search page of up to 25 short profiles; $0.004 per full profile | No ("No cookies or account required") | Every page and profile returned is billed | not stated |
| powerai/linkedin-peoples-search-scraper | 3,970 | $0.00999 per dataset item + $0.09 actor start | not stated | Every dataset item is billed | not stated |
| memo23/linkedin-people-search | 2,530 | $0.006 per profile found + $0.005 actor start | No ("no login") | Every profile returned is billed | not stated |

Per row, the others are cheaper. The difference is what a row means. If half of what a broad search returns is the wrong person, a $0.006 row is really $0.012 per right person, and you still have to filter the list yourself. Here the filter runs first and you pay only for rows that passed it.

### Input

```json
{
    "titles": ["CFO", "VP of Finance"],
    "companies": ["Asana", "https://www.linkedin.com/company/stripe/"],
    "locations": ["United States"],
    "maxResults": 200,
    "strictMatch": true
}
```

| Field | Type | Default | Description |
|---|---|---|---|
| `titles` | array | required | Job titles to find, 1 to 30, for example `VP of Sales` or `Head of Marketing` |
| `companies` | array | | Up to 200 company names or LinkedIn company URLs. A URL is read by its URL name, so use it only when that name is the company name, as in `linkedin.com/company/stripe`. When set, the company must be the current employer for the title in the headline. Past employers and service providers do not count |
| `locations` | array | | Up to 20 cities, regions or countries to narrow the search, for example `San Francisco` or `Germany` |
| `maxResults` | integer | 50 | Stop after this many profiles are delivered, matched or not, 1 to 2,000. Only matched profiles are charged |
| `strictMatch` | boolean | true | Open each headline-matched profile and charge only when it is public and lists a searched company as a current employer. Set false to charge on the headline alone |

### Use cases

- Outbound lists. Take a target account list, find the VP of Sales and Head of Sales at each company, and send only the charged rows to your sequencer.
- Recruiting. Find CTOs and VPs of Engineering at companies in one city, and skip the people whose headline says "ex-".
- Account-based marketing. Map the CFO, CMO and Head of Operations at each named account before a campaign.
- CRM enrichment. For each company in your CRM, look up one title and write back the matched profile URL and seniority band.
- Investor research. Find the founders, CEOs and CFOs at a portfolio or a pipeline of companies.

### Integrations

#### Apify API

This runs the Actor and returns the dataset in the same request.

```bash
curl -X POST \
  "https://api.apify.com/v2/acts/agnes.developer.queen~linkedin-people-search-scraper/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "titles": ["CMO"], "companies": ["Figma"], "maxResults": 20 }'
```

#### Python

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("agnes.developer.queen/linkedin-people-search-scraper").call(
    run_input={"titles": ["VP of Sales"], "companies": ["Stripe"], "maxResults": 50}
)
for row in client.dataset(run["defaultDatasetId"]).iterate_items():
    if row["charged"]:
        print(row["fullName"], row["matchedTitle"], row["profileUrl"])
```

#### Node.js

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });
const run = await client.actor('agnes.developer.queen/linkedin-people-search-scraper').call({
    titles: ['Head of Marketing'],
    companies: ['HubSpot'],
    maxResults: 50,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.filter((row) => row.charged));
```

#### Make, n8n and Zapier

Use the Apify app or node in Make, n8n or Zapier, pick "Run Actor", choose `agnes.developer.queen/linkedin-people-search-scraper` and map your titles and companies into the input. The same call works from Clay with an HTTP column. Apify webhooks can post to your workflow when a run finishes.

#### MCP server

The Apify MCP server lets AI assistants and MCP clients call this Actor as a tool:

```
https://mcp.apify.com/?tools=agnes.developer.queen/linkedin-people-search-scraper
```

Ask for the CFO and the VP of Finance at a list of companies. The assistant should read `matchedTitle`, `matchedCompany` and `profileUrl`, and skip rows where `charged` is false.

### FAQ

#### Is scraping LinkedIn people search legal?

The Actor reads public pages only: public Google results and public LinkedIn profiles. It never logs in and never touches data behind a login. You are responsible for how you use the data, including compliance with LinkedIn's terms of service and the privacy laws that apply in your jurisdiction, such as GDPR or CCPA. If you are unsure, ask a lawyer before you run outreach on the results.

#### Does it need my LinkedIn cookies or account?

No. There is no LinkedIn login and no cookies, so your own LinkedIn account is never at risk.

#### Why was a row not charged?

Its headline or public profile did not prove the match. The `reason` field says which part was missing: the title, the current company, a past or support role, or a profile LinkedIn does not show publicly. Free rows are still yours to use.

#### Is the match verified?

With strict match, yes against the person's own public profile: the Actor opens it and charges only when the profile is public and its employer data names the company you searched. It is not verified with the company itself, and division leaders can still list "CTO" at the parent company. With `strictMatch` false, the Actor bills on the headline alone.

#### How many profiles per run?

Up to 2,000, set with `maxResults`. That limit counts every delivered row, matched or not. How many confirmed profiles you get depends on how many people at each company show the title in their public headline. Large companies return more; small companies may return only a few.

#### Why is strict match slower?

Each headline-matched row costs one extra page fetch, about a second. Rows that miss the headline bar are not fetched.

#### Can I search by title only, without companies?

Yes. Leave `companies` empty. A row then bills when the person's own headline contains the title and, with strict match, the public profile is readable.

#### Can I schedule it?

Yes. Save your input as an Apify task and add a schedule, for example weekly, to catch people who changed jobs or updated their headline. Pair it with a webhook to push new rows to your CRM.

#### Does it return emails or phone numbers?

No. It returns the person's name, headline, location, seniority, department and LinkedIn URL. Use an email finder on the matched rows if you need contact details.

#### How much does a test run cost?

A run is $0.005 to start plus $0.015 for each confirmed profile. A 10-profile test with two confirmed matches costs $0.035, well inside the $5 free monthly credit.

### Related Actors

- [LinkedIn New Hires Finder](https://apify.com/agnes.developer.queen/linkedin-new-hires-finder) finds decision makers who joined a company recently.

### Support

Found a bug or a wrong charge? Open an issue on the [Issues tab of the Store page](https://apify.com/agnes.developer.queen/linkedin-people-search-scraper/issues) with the run ID. Docs and examples: [github.com/agnesthedeveloper/linkedin-people-search-scraper](https://github.com/agnesthedeveloper/linkedin-people-search-scraper).

# Actor input Schema

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

Job titles to search for, for example VP of Sales or Head of Marketing. A matched profile is a person whose own LinkedIn headline contains one of these titles.

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

Optional. Company names, or LinkedIn company URLs whose URL name is the company name (linkedin.com/company/stripe). When set, a matched profile must name one of these companies as the employer for the title in the person's own headline. Past employers and service providers do not count.

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

Optional. Cities, regions or countries to narrow the search, for example San Francisco or Germany.

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

Stop after this many profiles are delivered, matched or not. Only matched profiles are charged.

## `strictMatch` (type: `boolean`):

Before charging for a row, open the person's public profile and confirm it is readable and that LinkedIn's own profile data names a searched company as a current employer. Catches profiles that only claim the title in their headline. Turn off to charge on the headline alone.

## Actor input object example

```json
{
  "titles": [
    "VP of Sales",
    "Head of Sales"
  ],
  "companies": [
    "Stripe"
  ],
  "maxResults": 10,
  "strictMatch": true
}
```

# Actor output Schema

## `profiles` (type: `string`):

No description

## `summary` (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 = {
    "titles": [
        "VP of Sales",
        "Head of Sales"
    ],
    "companies": [
        "Stripe"
    ],
    "maxResults": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("agnes.developer.queen/linkedin-people-search-scraper").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 = {
    "titles": [
        "VP of Sales",
        "Head of Sales",
    ],
    "companies": ["Stripe"],
    "maxResults": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("agnes.developer.queen/linkedin-people-search-scraper").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 '{
  "titles": [
    "VP of Sales",
    "Head of Sales"
  ],
  "companies": [
    "Stripe"
  ],
  "maxResults": 10
}' |
apify call agnes.developer.queen/linkedin-people-search-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,agnes.developer.queen/linkedin-people-search-scraper"
        }
    }
}
```

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/a1QIzTqrpWq63yAoe/builds/Risgze54Oxs3ehQFh/openapi.json
