# LinkedIn Email Finder & Contacts Scraper (`receptional_blender/linkedin-contact-finder`) Actor

Discover publicly indexed contact emails tied to LinkedIn identities via targeted Google search, and optionally enrich specific profiles with your own session cookie. Every run reports exactly what it found — or why nothing was found, never a silent empty result.

- **URL**: https://apify.com/receptional_blender/linkedin-contact-finder.md
- **Developed by:** [Assia Fadli](https://apify.com/receptional_blender) (community)
- **Categories:** Lead generation
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.01 / 1,000 results

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 Contact Finder

Find **contact emails tied to LinkedIn identities** without guessing. This actor runs focused Google searches scoped to `linkedin.com` to surface email addresses that are already publicly indexed, and — when you supply your own logged-in session cookie — can open specific LinkedIn profiles to enrich them with name, headline, and any emails present on the page.

Built in TypeScript on Apify + Crawlee + Puppeteer. Billed **pay-per-event**: you are charged once per contact actually delivered (`contact-scraped`), never for blocked or empty pages.

### What it can and cannot do — read this first

LinkedIn is aggressively anti-bot, and it **does not put personal email addresses in profile page HTML**. A member's email lives behind the "Contact info" overlay, visible only to 1st-degree connections. There is therefore no reliable way to "scrape everyone's email" straight from LinkedIn.

What this actor **does**:

- **Discovers publicly indexed emails** associated with a LinkedIn identity — for example an address someone published in a LinkedIn About section, a company page, or a linked website that Google has crawled. This is the primary, login-free capability.
- **Enriches known LinkedIn profile URLs** (name, headline, and any emails visible on the page) when you provide a valid `li_at` cookie.

What it **cannot** guarantee:

- It cannot bypass LinkedIn's auth wall without a valid `li_at` cookie.
- It cannot surface emails that are not published anywhere public.
- Google actively rate-limits automated search, so some runs may be partially or fully blocked. **When that happens the actor tells you clearly in the output instead of returning an empty dataset.**

### Input

| Field | Required | Description |
| --- | --- | --- |
| `keywords` | yes\* | Job titles, names, or companies to search on Google (scoped to linkedin.com). |
| `locations` | no | Locations appended to each query to narrow results. |
| `profileUrls` | no | Exact LinkedIn profile/company URLs to open and enrich. Requires `liAtCookie`. |
| `liAtCookie` | recommended | Your LinkedIn `li_at` session cookie (marked secret). Required to open any LinkedIn profile page. |
| `scrapeProfiles` | no | If true and `liAtCookie` is set, LinkedIn URLs found in Google results are also opened logged-in. Off by default. |
| `gmail`, `yahoo`, `outlook`, `aol`, `icloud`, `protonmail` | no | Which email providers to target in the search. |
| `other` + `extraEmailProviders` | no | Add custom email domains (without the `@`). |
| `maxProfiles` | no | Max number of LinkedIn profiles to open (default 50). |
| `maxPagesPerQuery` | no | Google result pages to paginate per keyword (default 3). |

\* Provide `keywords` and/or `profileUrls`.

#### Getting your `li_at` cookie

1. Log into LinkedIn in a normal browser.
2. Open DevTools > Application (or Storage) > Cookies > `https://www.linkedin.com`.
3. Copy the **value** of the `li_at` cookie into the `liAtCookie` field.

Treat it like a password. It expires periodically; refresh it if runs report auth walls. Automating many profile views with your session can get your account rate-limited — keep `maxProfiles` modest.

### Output

The dataset contains typed rows:

- `type: "email"` — a discovered contact: `{ email, sourceQuery, foundOn, relatedLinkedInProfiles, discoveredAt }`
- `type: "profile"` — an enriched LinkedIn profile: `{ url, sourceQuery, name, headline, emails, note, scrapedAt }`
- `type: "diagnostic"` — a clear explanation when something was blocked (Google CAPTCHA, LinkedIn auth wall, invalid cookie) plus `whatToDo`.
- `type: "summary"` — always pushed at the end of every run, with counts of what was found and why. **A run is never silently empty.**

### Pricing (pay-per-event)

You are charged for the single event **`contact-scraped`**, once per delivered contact — that is, per unique email lead and per enriched profile. Diagnostic and summary rows are never charged. When your run's event budget is reached, the crawl stops cleanly.

### Local development

```bash
npm install
npm run build
apify run --purge --input '{"keywords":["marketing manager"],"locations":["London"]}'
```

### Legal & ethical use

Only collect and process data that is lawful for you to handle (GDPR/CCPA obligations apply to personal data). Respect LinkedIn's and Google's terms of service. You are responsible for how you use any data collected. Provided as-is, without warranty.

***

Author: **Assia Fadli** · License: **MIT**

# Actor input Schema

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

Job titles, company names, or people to look up on Google (scoped to linkedin.com). Each keyword becomes a Google query combined with the selected email providers — this is the primary way contacts are discovered. Leave empty only if you supply 'profileUrls' instead.

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

Optional locations appended to each keyword query to narrow the results, e.g. "London", "New York".

## `profileUrls` (type: `array`):

Optional list of exact LinkedIn profile or company URLs to open and enrich (name, headline, any emails present). Requires a valid 'liAtCookie'. Note: opening many profiles quickly with your session can get your LinkedIn account rate-limited.

## `liAtCookie` (type: `string`):

Your LinkedIn 'li_at' session cookie. Required to open LinkedIn profile pages, which show an auth wall to logged-out visitors. Grab it from a logged-in browser: DevTools > Application > Cookies > https://www.linkedin.com > copy the 'li_at' value. Treat it like a password; it expires periodically, so refresh it if runs report auth walls.

## `scrapeProfiles` (type: `boolean`):

When on AND a valid 'liAtCookie' is provided, LinkedIn profile URLs found in Google results are opened logged-in and enriched. Off by default because it uses your session and can trigger LinkedIn rate limits.

## `gmail` (type: `boolean`):

Include @gmail.com addresses in the search.

## `yahoo` (type: `boolean`):

Include @yahoo.com addresses.

## `outlook` (type: `boolean`):

Include @outlook.com / @hotmail.com addresses.

## `aol` (type: `boolean`):

Include @aol.com addresses.

## `icloud` (type: `boolean`):

Include @icloud.com addresses.

## `protonmail` (type: `boolean`):

Include @protonmail.com addresses.

## `other` (type: `boolean`):

Include addresses from the custom domains listed in 'extraEmailProviders'.

## `extraEmailProviders` (type: `array`):

Custom email domains to include when 'other' is enabled, e.g. "company.com". Do not include the '@'.

## `maxProfiles` (type: `integer`):

Upper limit on how many LinkedIn profile pages are opened (only relevant when 'scrapeProfiles' is on). Keep it modest to protect your LinkedIn account.

## `maxPagesPerQuery` (type: `integer`):

How many pages of Google results to paginate through for each keyword. Higher values find more but increase the chance of Google blocking the run.

## Actor input object example

```json
{
  "keywords": [
    "marketing manager"
  ],
  "scrapeProfiles": false,
  "gmail": true,
  "yahoo": true,
  "outlook": true,
  "aol": true,
  "icloud": true,
  "protonmail": true,
  "other": false,
  "maxProfiles": 50,
  "maxPagesPerQuery": 3
}
```

# 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": [
        "marketing manager"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("receptional_blender/linkedin-contact-finder").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": ["marketing manager"] }

# Run the Actor and wait for it to finish
run = client.actor("receptional_blender/linkedin-contact-finder").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": [
    "marketing manager"
  ]
}' |
apify call receptional_blender/linkedin-contact-finder --silent --output-dataset

```

## MCP server setup

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

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/gA4KnsG8ah8QQco33/builds/Tjib9w88MPKqhY8kD/openapi.json
