# Bulk LinkedIn & Email Enrichment — CSV, 50,000 Rows (`sputnikapi/bulk-people-enrichment`) Actor

Upload a CSV of emails, LinkedIn profile URLs or names + domains — get enriched professional profiles back at $3.20 per 1,000 resolved rows, full documents $8. Columns auto-detected, 50,000 rows per run, item-granular crash-safe resume: a delivered row is never re-billed. Misses are free.

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

## Pricing

from $3.20 / 1,000 row resolved (summary)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

## Bulk LinkedIn & Email Enrichment — a CSV in, enriched profiles out

> **$3.20 per 1,000 resolved rows, $8 with the full 164-field document** —
> the same price as the single lookups, with crash-safe resume on top. A
> delivered row is never re-billed, whatever the platform does to the run.

A CSV in, enriched LinkedIn profile rows out — built for the **10,000–50,000
row exports** that are too big to paste into a lookup form.

Upload the file (or link it), and every row resolves by the **strongest
key it carries**:

1. email address
2. profile URL
3. social handle (github / twitter / facebook)
4. full name — plus a company `domain` column when you have one

Bare names resolve **only when exactly one profile carries the name**.
Namesakes come back as an honest, free refusal with the holder count —
never a guess.

### Built for big files

- Rows stream into your dataset as they resolve — no waiting for the end
  of the run to see results.
- Long runs survive platform server migrations: the actor checkpoints its
  progress and resumes where it left off instead of starting over.
- Ten consecutive service errors stop the run early instead of grinding
  through an outage on your time.

### Input

```json
{
  "csv": "email\nsatya.nadella@microsoft.com",
  "fullOutput": false
}
```

- `csv` — upload a file, paste rows, or link to a CSV file. Recognized
  columns: `email`, `profile_url`, `github`, `twitter`, `facebook`,
  `full_name` (or `first_name` + `last_name`), `domain`.
- `fullOutput` — deliver the complete profile document per resolved row
  (**164 fields across 37 sections**) at the full-profile rate, instead of
  the 60+ field summary card.

### What you get — 60+ fields per resolved row, 164 with `fullOutput`

| Group | Summary card (default, $0.0032/row) | Full document (`fullOutput: true`, $0.008/row) |
|---|---|---|
| Identity | full name, first/last, headline, photo, profile slug | same + background image |
| Current role | job title, employer name + slug (flat columns), position dates, industry | + role descriptions, employment type, job function |
| Career history | current positions | **every past position** with dates and descriptions |
| Education & certifications | schools, degrees, fields of study, dates; cert names, authorities | + descriptions, grades, license numbers |
| Skills, languages, bio | full skills list, languages, "about" text | same |
| Location & seniority | city/state/country, experience years, tenure, decision-maker flag | same |
| Salary estimate | inferred min–max | same |
| Social links | professional profile URL, GitHub, Twitter/X, Facebook, website | same |
| Contacts | — | **email addresses and phone numbers when on record** |
| Deep sections | — | publications, patents, awards, projects, articles, related profiles |
| Honesty meta | `_status`, `_freshness`, `updatedAt`; `_matchCandidates` when a key matched several people | same + per-section coverage map |

### Output

One row per input row, tagged with its record number (the header is
record 1, the first data record is 2; a quoted cell containing a line
break still counts as one record). A real resolved row:

Resolved rows carry the profile card plus flat `companyName` /
`companySlug` columns when a current position is on record; free
`ambiguous` rows carry `_nameMatchCount`, the honest namesake count.

```json
{
  "_status": "found",
  "_input": { "row": 2, "email": "satya.nadella@microsoft.com" },
  "_freshness": "fresh_90d",
  "companyName": "Microsoft",
  "companySlug": "microsoft",
  "_view": "lite-v3",
  "fullName": "Satya Nadella",
  "headline": "Chairman and CEO at Microsoft",
  "jobTitle": "Chairman and CEO",
  "about": "As chairman and CEO of Microsoft, I define my mission and that of my company as empowering every person and every organization on the planet to achieve more.",
  "location": { "city": "Redmond", "state": "WA", "country": "US" },
  "seniority": { "totalExperienceYears": 12, "currentTenureYears": 12 },
  "experience": { "work": [ { "title": "Chairman and CEO", "company": "Microsoft", "startDate": "2014-02-01", "endDate": null } ] },
  "education": [ { "school": "University of Wisconsin-Milwaukee", "degreeName": "Master’s Degree", "fieldOfStudy": "Computer Science" } ]
}
```

*(trimmed for display — the card also carries `skills`, `languages`,
`certifications`, `inferredSalary`, social links and connection counts
whenever the profile has them)*

`_status` explains every non-match (`ambiguous` with the namesake count,
`not_found`, `invalid`, `profile_removed`) — free rows, never dropped
silently: your output CSV has the same accountability as your input.

The **Output tab** shows three views: *Overview*, *Career & education*,
and *Contacts & signals*.

### Pricing — pay only for resolved rows

| Event | Price | When |
|---|---|---|
| Row resolved (summary) | $0.0032 | resolved to a profile, 60+ field row |
| Row resolved (full document) | $0.008 | `fullOutput: true`, 164 fields / 37 sections |
| Unresolvable / malformed / namesake rows | **$0** | always free, with an explanation |

A charge fires only after the row is already in your dataset.

### 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), **People/Company
Database Search**, **Name-to-Profile** (name + company → profile),
**Social Handle Lookup** (dev/social handle → person). 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

## `csv` (type: `string`):

Upload a CSV, paste rows, or link to a CSV file (up to 50,000 rows). Recognized columns: email, profile\_url, github, twitter, facebook, full\_name (or first\_name + last\_name), domain. Each row resolves by its strongest key: email > profile URL > social handle > name + domain.

## `fullOutput` (type: `boolean`):

Deliver the complete profile document per resolved row — 164 fields in 37 sections: full career history with role descriptions, education detail, certifications, publications, patents, awards, projects and all contact fields — instead of the 60+ field summary card. Refusals stay free in both modes.

## Actor input object example

```json
{
  "csv": "email\nsatya.nadella@microsoft.com",
  "fullOutput": false
}
```

# 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 = {
    "csv": `email
satya.nadella@microsoft.com`
};

// Run the Actor and wait for it to finish
const run = await client.actor("sputnikapi/bulk-people-enrichment").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 = { "csv": """email
satya.nadella@microsoft.com""" }

# Run the Actor and wait for it to finish
run = client.actor("sputnikapi/bulk-people-enrichment").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 '{
  "csv": "email\\nsatya.nadella@microsoft.com"
}' |
apify call sputnikapi/bulk-people-enrichment --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,sputnikapi/bulk-people-enrichment"
        }
    }
}
```

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/EhVyi9uvJ4eaUz9nF/builds/mTlb8wA3Sg3deUlq2/openapi.json
