# LinkedIn Profile Contact Enricher (`automation-lab/linkedin-profile-contact-enricher`) Actor

Enrich public LinkedIn profile URLs with profile details and source-attributed work contacts from public company websites. Emails are never guessed.

- **URL**: https://apify.com/automation-lab/linkedin-profile-contact-enricher.md
- **Developed by:** [Stas Persiianenko](https://apify.com/automation-lab) (community)
- **Categories:** Lead generation, Social media
- **Stats:** 2 total users, 2 monthly users, 43.2% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#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 Contact Enricher

Turn known public LinkedIn profile URLs into typed professional records and inspect official company or institution pages for explicitly published work contacts. The Actor uses public metadata and public website pages—no LinkedIn login, cookies, or private data.

It never invents an email address. Every returned email includes the exact public page and nearby evidence that explains what the address represents, such as an administrative contact.

### What does LinkedIn Profile Contact Enricher do?

Provide one or more `linkedin.com/in/...` URLs. For each URL, the Actor:

1. normalizes the profile URL;
2. resolves public profile metadata such as name, headline, company, and summary;
3. optionally visits a supplied official company or institution URL;
4. follows up to five same-domain contact, team, leadership, or about links;
5. returns only published emails found near the person's name or on the supplied public person page;
6. attaches source URLs and evidence to every result.

The default dataset contains one record per successfully enriched profile.

### Who is it for?

- **Sales operations teams** enriching an existing list of known profile URLs.
- **Recruiters** adding public headline and organization context to candidate records.
- **Research teams** collecting reproducible, source-attributed professional metadata.
- **CRM administrators** normalizing profile URLs before imports and deduplication.
- **Developers** building responsible enrichment pipelines through the Apify API.

Use [LinkedIn Public Profile Search Scraper](https://apify.com/automation-lab/linkedin-public-profile-search-scraper) when you need to discover profile URLs first.

### Why use this Actor?

#### No LinkedIn account

The Actor does not request your LinkedIn cookies, credentials, or Sales Navigator session.

#### No guessed private emails

Many lead tools generate likely addresses from a person's name and a company pattern. This Actor does not. An empty `publicWorkEmails` array means no qualifying published address was found.

#### Evidence, not a black box

Each profile has a `sources` array. Each email includes `sourceUrl` and `evidence`, so a reviewer can understand whether it is a direct address, administrator address, or another published work-contact route.

#### Bounded public website inspection

Company enrichment stays on the hostname you provide and inspects at most five relevant pages. It does not crawl an entire website.

### What LinkedIn profile data is extracted?

| Field | Type | Meaning |
|---|---|---|
| `linkedinUrl` | string | Normalized public `/in/` URL |
| `fullName` | string or null | Name from public metadata |
| `headline` | string or null | Public professional headline |
| `currentCompany` | string or null | Company parsed from an “at” headline when available |
| `publicProfileSummary` | string or null | Public profile description or search snippet |
| `publicWorkEmails` | array | Published work-contact emails with evidence; never predicted |
| `companyContacts` | array | General contact pages or phone numbers found on contact pages |
| `sources` | array | Retrieval surface, URL, and timestamp |
| `enrichedAt` | string | ISO 8601 completion time |
| `warnings` | array | Skipped enrichment or recoverable company-page errors |

A published address can belong to an assistant or administrator. Read its `evidence`; do not assume every address is the person's direct inbox.

### Getting started

1. Open the Actor in Apify Console.
2. Add one or more public LinkedIn profile URLs under **Profiles to enrich**.
3. Add `fullName` for best matching, especially when a profile slug is opaque.
4. Optionally add an official public `companyDomain` or exact person page.
5. Keep **Inspect public company websites** enabled when you want work contacts.
6. Set **Maximum profile records**.
7. Click **Start**.
8. Open the default dataset to review records and source evidence.

### Input parameters

#### `profiles`

Required array with 1–1,000 objects.

```json
{
  "url": "https://www.linkedin.com/in/fei-fei-li-4541247",
  "fullName": "Fei-Fei Li",
  "companyDomain": "https://profiles.stanford.edu/fei-fei-li"
}
```

- `url` must be a public LinkedIn `/in/` profile.
- `fullName` is recommended and required when the slug does not contain separate name words.
- `companyDomain` is optional. Use an official public hostname or an exact public staff/profile page.

#### `enrichCompanyContacts`

Defaults to `true`. Set it to `false` for profile-only enrichment and fewer external requests.

#### `maxItems`

Defaults to `10`, with a maximum of `1,000`. Processing stops after this many successful dataset records.

### Profile-only input example

```json
{
  "profiles": [
    {
      "url": "https://www.linkedin.com/in/satyanadella",
      "fullName": "Satya Nadella"
    }
  ],
  "enrichCompanyContacts": false,
  "maxItems": 1
}
```

### Public work-contact input example

```json
{
  "profiles": [
    {
      "url": "https://www.linkedin.com/in/fei-fei-li-4541247",
      "fullName": "Fei-Fei Li",
      "companyDomain": "https://profiles.stanford.edu/fei-fei-li"
    }
  ],
  "enrichCompanyContacts": true,
  "maxItems": 1
}
```

This real example returns Fei-Fei Li's public professional metadata and a Stanford administrative contact published on the supplied profile page. The evidence identifies the address as an administrative contact rather than claiming it is a private or direct email.

### Output example

A current profile-only run produces this shape:

```json
{
  "linkedinUrl": "https://www.linkedin.com/in/satyanadella",
  "fullName": "Satya Nadella",
  "headline": "Chairman and CEO at Microsoft",
  "currentCompany": "Microsoft",
  "publicProfileSummary": "Chairman and CEO at Microsoft · As chairman and CEO of Microsoft...",
  "publicWorkEmails": [],
  "companyContacts": [],
  "sources": [
    {
      "type": "public-metadata",
      "url": "https://api.microlink.io?url=https%3A%2F%2Fwww.linkedin.com%2Fin%2Fsatyanadella",
      "retrievedAt": "2026-08-02T02:46:23.618Z"
    }
  ],
  "enrichedAt": "2026-08-02T02:46:23.619Z",
  "warnings": ["Company contact enrichment disabled."]
}
```

Timestamps and public snippets change between runs.

### How source attribution works

Profile metadata is obtained through a public metadata endpoint, with public Bing search snippets as a fallback. The Actor accepts a profile only when the returned URL normalizes to the requested LinkedIn profile.

Company-page sources use `type: "company-page"`. Website requests remain on the supplied origin after redirects. Private IP addresses, localhost names, credentials in URLs, and unsupported protocols are rejected.

For an email to be returned, it must be explicitly present:

- near the supplied person's name in visible page text; or
- as a `mailto:` link on the supplied non-root public person page that also contains the person's name.

No name-to-email pattern generation occurs.

### How much does it cost to enrich LinkedIn profile contacts?

The Actor uses pay-per-event pricing:

- one `start` event per run;
- one `item` event for each successful profile record;
- no separate charge for individual emails, phones, or source pages.

Current Bronze pricing is **$0.005 per run plus $0.008 per successful profile**. Pricing is tiered by Apify plan; the live Console price shown before a run is authoritative.

Bronze examples:

| Successful profiles | Estimated Actor charge |
|---:|---:|
| 1 | $0.013 |
| 10 | $0.085 |
| 100 | $0.805 |

Failed or rejected inputs do not create an `item` event. Platform limits and maximum total charge settings can stop a run early.

### Reliability and retry behavior

The Actor retries temporary network errors, HTTP 429 responses, and selected 5xx responses with bounded backoff. It does not repeatedly retry malformed URLs, deterministic access errors, or parser bugs.

A profile that cannot be matched to public metadata fails closed rather than returning another person's record. A company page failure is recorded in `warnings`; the already matched profile can still be useful.

For repeatable production jobs:

- supply `fullName` for every input;
- supply exact official person pages when known;
- keep batches bounded;
- inspect warnings and evidence before writing data to a CRM;
- use Apify schedules for recurring snapshots.

The Actor itself does not compare snapshots, monitor changes, or send alerts. Store run datasets separately if your workflow needs change detection.

### Export and integration workflows

#### Spreadsheet enrichment

Run a batch, open the dataset, and export JSON, CSV, Excel, or XML from Apify Console.

#### CRM pipeline

Use a webhook or API client to read the default dataset. Keep `linkedinUrl` as the stable deduplication key and store contact evidence alongside any email.

#### Responsible outreach review

Filter rows with non-empty `publicWorkEmails`, inspect `evidence`, and route ambiguous administrator or general contacts to a human review step before outreach.

#### Scheduled snapshots

Use an Apify schedule to rerun a stable input. Your downstream system can compare headline, company, summary, and contact arrays. Scheduling does not guarantee that upstream public metadata remains available.

### Use the Actor through the API

Replace `YOUR_APIFY_TOKEN` with a secret stored outside source control.

#### cURL

```bash
curl -X POST \
  "https://api.apify.com/v2/acts/automation-lab~linkedin-profile-contact-enricher/runs?token=YOUR_APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "profiles": [{
      "url": "https://www.linkedin.com/in/satyanadella",
      "fullName": "Satya Nadella"
    }],
    "enrichCompanyContacts": false,
    "maxItems": 1
  }'
```

#### JavaScript

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

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('automation-lab/linkedin-profile-contact-enricher').call({
  profiles: [{
    url: 'https://www.linkedin.com/in/fei-fei-li-4541247',
    fullName: 'Fei-Fei Li',
    companyDomain: 'https://profiles.stanford.edu/fei-fei-li',
  }],
  enrichCompanyContacts: true,
  maxItems: 1,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

#### Python

```python
import os
from apify_client import ApifyClient

client = ApifyClient(os.environ['APIFY_TOKEN'])
run = client.actor('automation-lab/linkedin-profile-contact-enricher').call(run_input={
    'profiles': [{
        'url': 'https://www.linkedin.com/in/satyanadella',
        'fullName': 'Satya Nadella',
    }],
    'enrichCompanyContacts': False,
    'maxItems': 1,
})
items = client.dataset(run['defaultDatasetId']).list_items().items
print(items)
```

### Use with Apify MCP

Add the Actor to Claude Code:

```bash
claude mcp add --transport http apify \
  "https://mcp.apify.com?tools=automation-lab/linkedin-profile-contact-enricher"
```

#### Claude Desktop, Cursor, and VS Code MCP setup

Use this equivalent remote MCP configuration in Claude Desktop, Cursor, or VS Code:

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com?tools=automation-lab/linkedin-profile-contact-enricher"
    }
  }
}
```

Example prompts:

- “Enrich these LinkedIn profile URLs and return source-attributed public fields.”
- “Inspect the supplied official staff pages and list only published work contacts with evidence.”
- “Prepare a CSV-ready dataset and flag rows with no public work contact.”

### Limits and responsible expectations

- Public metadata providers and search surfaces can rate-limit, change, or omit profiles.
- The Actor does not access login-only sections or private LinkedIn data.
- It does not return complete work history, education history, posts, or connections.
- A company website may publish no email for a person; empty arrays are expected.
- General phone numbers and administrative addresses are not guaranteed to reach the person directly.
- JavaScript-only company pages may expose less information to the HTTP parser.
- Website crawling is limited to five same-origin pages per profile.
- There is no automatic residential proxy or browser fallback.

### Legal and ethical use

Use only data you are authorized to process. Follow LinkedIn's terms, the terms and robots guidance of company websites, applicable privacy law, anti-spam law, and your organization's retention rules.

Public availability does not automatically authorize unsolicited outreach. Establish a lawful purpose, minimize collected fields, preserve source and context, honor deletion or objection requests, and review contact evidence before use.

Do not use this Actor for discrimination, surveillance, credential harvesting, or attempts to reveal private contact information.

### Troubleshooting

#### “No public metadata or search snippet matched”

Confirm that the URL is a real public `/in/` profile and that `fullName` exactly identifies the person. Public metadata can be temporarily unavailable; retry later rather than substituting another profile.

#### The record has no email

This is normal. Add the exact official public person page as `companyDomain` when available. The Actor deliberately does not guess an address.

#### A company page appears in warnings

Check that the hostname is public, resolves normally, serves HTML, and does not require login or browser interaction. The profile record may still be valid.

#### `currentCompany` is null

The field is parsed only from public headlines containing an “at” phrase. Use `headline` and `publicProfileSummary` for the unmodified public context.

### Related Automation Lab Actors

- [LinkedIn Profile Scraper](https://apify.com/automation-lab/linkedin-profile-scraper) — deeper LinkedIn profile fields when you need work history, education, and other profile sections.
- [LinkedIn Public Profile Search Scraper](https://apify.com/automation-lab/linkedin-public-profile-search-scraper) — discover public profile URLs by role, company, location, or keyword.
- [LinkedIn Profile URL Finder](https://apify.com/automation-lab/linkedin-profile-url-finder) — resolve known people or identity records to likely LinkedIn URLs.
- [Domain to LinkedIn URL Resolver](https://apify.com/automation-lab/domain-to-linkedin-url-resolver) — connect company domains to LinkedIn company pages.

Choose this Actor when you already have person-profile URLs and specifically need conservative, source-attributed public contact enrichment.

### FAQ

#### Does it need LinkedIn cookies?

No.

#### Does it infer likely email addresses?

No. It returns only addresses explicitly published on inspected company or institution pages.

#### Is every returned email the person's direct inbox?

No. It can be an administrator or other work-contact route. Always inspect `evidence`.

#### Can it find profiles by keyword or title?

No. Use LinkedIn Public Profile Search Scraper first, then pass the resulting URLs here.

#### Can I run it on a schedule?

Yes. Apify schedules can create recurring snapshots, but comparison and alerting belong in your downstream workflow.

#### What happens when one company site fails?

The Actor records a warning and preserves the matched profile record. An unmatched profile fails rather than emitting incorrect data.

#### Does it use my supplied domain outside that domain?

No. Company links must remain on the same origin after redirects.

# Actor input Schema

## `profiles` (type: `array`):

LinkedIn /in/ profile URLs. Add fullName for opaque profile slugs and companyDomain to inspect an official public company website for attributable contacts.

## `enrichCompanyContacts` (type: `boolean`):

When enabled, inspect supplied companyDomain pages for attributable published emails and general company contact routes.

## `maxItems` (type: `integer`):

Stop after this many successfully enriched profile records.

## Actor input object example

```json
{
  "profiles": [
    {
      "url": "https://www.linkedin.com/in/satyanadella",
      "fullName": "Satya Nadella",
      "companyDomain": "https://news.microsoft.com/source/exec/satya-nadella/"
    }
  ],
  "enrichCompanyContacts": true,
  "maxItems": 10
}
```

# Actor output Schema

## `overview` (type: `string`):

Default dataset items in the profile-contact overview view.

# 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 = {
    "profiles": [
        {
            "url": "https://www.linkedin.com/in/satyanadella",
            "fullName": "Satya Nadella",
            "companyDomain": "https://news.microsoft.com/source/exec/satya-nadella/"
        }
    ],
    "enrichCompanyContacts": true,
    "maxItems": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("automation-lab/linkedin-profile-contact-enricher").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 = {
    "profiles": [{
            "url": "https://www.linkedin.com/in/satyanadella",
            "fullName": "Satya Nadella",
            "companyDomain": "https://news.microsoft.com/source/exec/satya-nadella/",
        }],
    "enrichCompanyContacts": True,
    "maxItems": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("automation-lab/linkedin-profile-contact-enricher").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 '{
  "profiles": [
    {
      "url": "https://www.linkedin.com/in/satyanadella",
      "fullName": "Satya Nadella",
      "companyDomain": "https://news.microsoft.com/source/exec/satya-nadella/"
    }
  ],
  "enrichCompanyContacts": true,
  "maxItems": 10
}' |
apify call automation-lab/linkedin-profile-contact-enricher --silent --output-dataset

```

## MCP server setup

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

```

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/rsNmkJnNsrTPUzMtD/builds/rsLi46Rc4H2c1HOh3/openapi.json
