# LinkedIn Profile Scraper & Public Email Finder (`generous_fog/linkedin-profile-scraper-public-email-finder`) Actor

Extract structured LinkedIn profiles from exact URLs with public no-cookie discovery, authenticated deep enrichment, work history, education, skills, recent activity, deduplication, and optional evidence-based public email finding. Built for recruiting, sales, CRM enrichment, and research.

- **URL**: https://apify.com/generous\_fog/linkedin-profile-scraper-public-email-finder.md
- **Developed by:** [Cracks API](https://apify.com/generous_fog) (community)
- **Categories:** Lead generation, Social media, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 basic profile 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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## LinkedIn Profile Scraper & Public Email Discovery

Profile-first LinkedIn collection for exact `/in/` URLs, with public no-cookie parsing, optional account-authorized Voyager enrichment, and evidence-only public email discovery.

> **Unofficial — not affiliated with LinkedIn.** This independent Actor is not endorsed by, sponsored by, or connected with LinkedIn. LinkedIn is a trademark of LinkedIn Corporation.

### Capabilities

- Deduplicates and limits submitted profile URLs with `maxProfiles`.
- Public mode extracts Open Graph/title signals, profile-card highlights only, and flattened JSON-LD `Article` / `DiscussionForumPosting` activity.
- When public LinkedIn is blocked, records only an exact-URL public-index fallback. Thin snippets never qualify as paid results.
- Authenticated mode uses the caller's secret `liAt` and `jsessionId` session values, tries both Voyager endpoint variants, keeps one proxy session per profile, selects a Profile entity from `included`, and normalizes experience, education, skills, certifications, languages, websites, authorized contact emails and phones.
- Email discovery searches public result snippets only. It reports directly visible, conservatively validated evidence with a source and confidence. It never generates an address.
- Includes polite delay/jitter, 429/5xx/999 retries, stable proxy sessions, completeness scoring, per-profile error records, dry runs, and output views.

### Inputs

`profileUrls` is required. Defaults use the real public profile `https://www.linkedin.com/in/satyanadella/` and a Residential Apify proxy for daily-test reliability.

| Field                                                  | Purpose                                                                                                                           |
| ------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------- |
| `maxProfiles`                                          | 1-1000 URLs, default 100.                                                                                                         |
| `mode`                                                 | `public`, `authenticated`, or safe `auto` fallback.                                                                               |
| `liAt`, `jsessionId`                                   | Optional secret values for the caller's own authorized session. Never emitted.                                                    |
| `includeRecentActivity`, `maxActivityItems`            | Public structured activity switch and 0-25 item limit, default 5.                                                                 |
| `discoverPublicEmails`, `maxEmailSources`              | Evidence-only snippet scan and 1-5 source limit.                                                                                  |
| `requestDelaySecs`, `maxRetries`, `proxyConfiguration` | Pacing, resilient retries, and proxy behavior.                                                                                    |
| `maxTotalCharge`                                       | Optional USD cap. No paid event is emitted after the local cap would be exceeded. Apify run charging limits remain authoritative. |
| `dryRun`                                               | Emits planned records with no network calls and no paid result events.                                                            |

### Output and charge events

A successful profile is written atomically with exactly one result event using `Actor.pushData(record, eventName)`. Before charging, the profile must pass a hard quality gate: valid name and headline, at least 45% completeness, and at least two rich sections such as about, experience, education, skills, certifications, or languages. Thin public-index fallbacks are emitted as `insufficient_data` without a paid event. Error, limit-reached, and dry-run records are also uncharged.

| Event                      | Planned price | Result                                                 |
| -------------------------- | ------------: | ------------------------------------------------------ |
| `basic-profile`            |    $1 / 1,000 | Quality-gated public profile.                          |
| `full-profile`             |    $2 / 1,000 | Quality-gated authorized profile.                      |
| `basic-profile-with-email` |    $4 / 1,000 | Quality-gated public profile plus requested email scan.|
| `full-profile-with-email`  |    $5 / 1,000 | Quality-gated authorized profile plus email scan.      |

Email prices are **total per profile**, not additive to another profile event. Configure these event prices in Apify Console after build/publication. The repository only emits event keys.

Views: **Successful profiles**, **Structured public activity**, **Email and contact evidence**, and **Errors and limits**.

### Limitations and responsible use

Public pages, search snippets, and Voyager response shapes may change or be unavailable by region. The Actor does not bypass CAPTCHA, login walls, or access controls. Use only data you are permitted to collect and process, follow applicable law and platform terms, and do not use results for unsolicited or harmful outreach.

### Local checks

```bash
npm install
npm run check
```

# Actor input Schema

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

One or more public LinkedIn /in/ profile URLs. Duplicate URLs are removed automatically.

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

Maximum unique valid profile URLs to process.

## `mode` (type: `string`):

auto tries authorized Voyager only when both secret session values are supplied, then falls back to public data.

## `liAt` (type: `string`):

Optional LinkedIn session cookie for your authorized account. Used only in authenticated mode.

## `jsessionId` (type: `string`):

Optional LinkedIn CSRF session value for your authorized account. Used only in authenticated mode.

## `includeRecentActivity` (type: `boolean`):

Extract public JSON-LD Article and DiscussionForumPosting activity where available.

## `maxActivityItems` (type: `integer`):

Maximum structured public activity items returned per profile.

## `discoverPublicEmails` (type: `boolean`):

Search public web snippets only. Returns directly evidenced and conservatively validated emails, never inferred addresses.

## `maxEmailSources` (type: `integer`):

Maximum public search-result sources examined per profile.

## `requestDelaySecs` (type: `number`):

Base polite delay. A small random jitter is added.

## `maxRetries` (type: `integer`):

Retries for transient network, 429, 5xx, and LinkedIn 999 responses.

## `maxTotalCharge` (type: `number`):

Optional USD charging cap. The Actor will not emit a paid result event past this cap; Apify run charging limits remain authoritative.

## `dryRun` (type: `boolean`):

Validate and normalize URLs, then emit planned records without network calls or paid result events.

## `proxyConfiguration` (type: `object`):

Optional Apify proxy. Residential proxy groups are recommended for reliable daily testing and larger compliant runs.

## Actor input object example

```json
{
  "profileUrls": [
    "https://www.linkedin.com/in/satyanadella/"
  ],
  "maxProfiles": 100,
  "mode": "public",
  "includeRecentActivity": true,
  "maxActivityItems": 5,
  "discoverPublicEmails": false,
  "maxEmailSources": 2,
  "requestDelaySecs": 2,
  "maxRetries": 2,
  "dryRun": false,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

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

No description

## `successfulProfiles` (type: `string`):

No description

## `activity` (type: `string`):

No description

## `emailEvidence` (type: `string`):

No description

## `errors` (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 = {
    "profileUrls": [
        "https://www.linkedin.com/in/satyanadella/"
    ],
    "mode": "public",
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("generous_fog/linkedin-profile-scraper-public-email-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 = {
    "profileUrls": ["https://www.linkedin.com/in/satyanadella/"],
    "mode": "public",
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("generous_fog/linkedin-profile-scraper-public-email-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 '{
  "profileUrls": [
    "https://www.linkedin.com/in/satyanadella/"
  ],
  "mode": "public",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call generous_fog/linkedin-profile-scraper-public-email-finder --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,generous_fog/linkedin-profile-scraper-public-email-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/o46ZeVCPeGa8qcDeE/builds/0EZg1XTA92gE8SPlv/openapi.json
