# Linkedin Smart Talent Scraper | Recruitment Tool (`coolinbex/linkedin-talent-scraper`) Actor

Collect LinkedIn people-search summaries and optionally visible profile details using your session cookie. Deduplicates profiles and reports partial results and access failures.

- **URL**: https://apify.com/coolinbex/linkedin-talent-scraper.md
- **Developed by:** [coolinbex](https://apify.com/coolinbex) (community)
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $8.00 / 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?

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 Talent Scraper

Collect people-search results from LinkedIn and optionally visit each profile for visible details. Results are deduplicated by profile URL across searches and saved to an Apify dataset for JSON, CSV, or Excel export.

You need your own active LinkedIn session. Results depend on what that account can see. Profile visits collect visible sections; they do not guarantee a complete work history, education history, or skills list.

### Quick start

1. Log into LinkedIn in your browser. Open DevTools > Application > Cookies > `https://www.linkedin.com` and copy the **value** of `li_at`.
2. Paste it into **LinkedIn session cookie**. Enter at least one search in the form.
3. Start with **10 profiles**, **1 concurrent page**, and profile details disabled. The default residential proxy requires Apify proxy access and adds proxy charges.
4. Run the actor. Open **Output > Profiles** to review the people collected. Use **Run summary** to check completion and errors.
5. Enable **Collect visible profile details** if needed, and set the enrichment limit to control additional requests.

Treat the session cookie as a password: paste only its value, never share it in logs or support requests. Apify stores this input as a secret. Local input files are plain text and must remain private. LinkedIn may restrict automated activity; use only accounts and data you are authorized to access. The actor stops at login and verification pages.

### Example input

Replace the placeholder cookie before running:

```json
{
  "liAtCookie": "REPLACE_WITH_YOUR_COOKIE_VALUE",
  "searchQueries": [
    { "keywords": "senior backend engineer", "location": "Berlin" }
  ],
  "maxProfiles": 10,
  "scrapeFullProfiles": false,
  "maxConcurrency": 1,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": ["RESIDENTIAL"]
  }
}
```

**Text fields are keywords, not exact filters.** `keywords`, `title`, `company`, and `location` are joined into one search phrase. For exact location, current-company, or other LinkedIn filters, configure the search in LinkedIn and paste its people-search URL:

```json
{
  "directSearchUrl": "https://www.linkedin.com/search/results/people/?keywords=engineer&geoUrn=%5B%22101165590%22%5D"
}
```

A direct URL overrides all text fields in that query. Only HTTPS LinkedIn people-search URLs are accepted. Existing filters are preserved; `start` and `page` are reset so pagination begins at the first page. Do not use a Sales Navigator, company, profile, or login URL. Multiple searches share the result and enrichment limits. The first search to discover a profile supplies its source label; ordering across concurrent searches is not guaranteed.

### Inputs

| Input | Default | Allowed values and behavior |
| --- | --- | --- |
| `liAtCookie` | Required | Your session cookie value, without `li_at=` or other cookies. |
| `searchQueries` | Required | 1–100 objects containing non-empty text fields or a `directSearchUrl`. The example in the form is a prefill, not an automatic API search. Invalid entries fail the run with their array index. |
| `maxProfiles` | 100 | Integer, 1–5,000. Maximum unique people across all searches. |
| `scrapeFullProfiles` | false | Whether to visit profiles for visible details. |
| `maxProfilesToEnrich` | 50 | Integer, 0–5,000. Also bounded by `maxProfiles`; 0 means summaries only. |
| `maxPagesPerQuery` | 10 | Integer, 1–100. Maximum search pages per query. |
| `maxRunTimeSecs` | 240 | Integer, 60–86,400. Stops early and saves results before the platform timeout. Set the Apify timeout at least 30 seconds higher. |
| `minDelayMs` | 2,500 | Integer, 500–60,000. Minimum wait before navigation. |
| `maxDelayMs` | 6,000 | Integer, 500–60,000; at least `minDelayMs`. |
| `maxConcurrency` | 1 | Integer, 1–5. Maximum pages open at once. |
| `maxRequestRetries` | 3 | Integer, 0–10. Retries after transient failures. Login and verification failures stop the run. |
| `proxyConfiguration` | Apify residential | Standard Apify proxy settings; custom proxy URLs are supported. Set `{"useApifyProxy": false}` to disable proxies. |

Navigation starts are spaced across concurrent pages, including retries and enrichment. Concurrency can overlap page loading, but does not bypass the delay. More profiles, enrichment, retries, and proxy traffic increase cost. Empty searches, repeated pages, account visibility, and the page limit may return fewer people than requested. The default page limit is 10, and pagination stops after two consecutive pages add no unique profiles. Reaching `maxProfiles` stops new searches while allowing collected profiles to finish enrichment.

### Output and exports

Every profile has the same fields. The **Profiles** view shows names, headlines, locations, links, connection degree, source, and enrichment status. **Profile details** shows the visible sections. **Data quality** shows visit failures, parsing warnings, and collection time. Download JSON to preserve nested experience and education entries; CSV and Excel are convenient for a shortlist.

```json
{
  "fullName": "Jane Doe",
  "headline": "Senior Backend Engineer at Acme Corp",
  "location": "Berlin, Germany",
  "connectionDegree": "2nd",
  "profileUrl": "https://www.linkedin.com/in/jane-doe-123/",
  "profileImageUrl": null,
  "sourceSearchQuery": "senior backend engineer | Berlin",
  "scrapedAt": "2026-09-12T10:00:00.000Z",
  "about": null,
  "experience": [],
  "education": [],
  "skills": [],
  "connectionsCount": null,
  "openToWork": null,
  "profileEnriched": false,
  "enrichmentStatus": "not_requested",
  "enrichmentError": null,
  "_parseWarnings": []
}
```

| Fields | Meaning |
| --- | --- |
| `fullName`, `headline`, `location`, `connectionDegree`, `profileImageUrl` | Visible search-card values; unavailable values are `null`. Profile details can improve name, headline, and location. Private names remain unavailable. |
| `profileUrl` | Canonical LinkedIn profile URL with tracking parameters removed. |
| `sourceSearchQuery`, `scrapedAt` | First discovering query and UTC time the summary was collected. |
| `about` | Visible About text, or `null`. |
| `experience` | Array of `{ "title": string or null, "company": string or null, "duration": string or null }`. Duration is source text. |
| `education` | Array of `{ "school": string or null, "degree": string or null }`. |
| `skills` | Array of visible skill names. |
| `connectionsCount` | Displayed integer or `null`. `500+ connections` becomes `500`, a lower bound. |
| `openToWork` | `true` when a visible indicator is found; otherwise `null`. Absence does not establish availability. |
| `profileEnriched` | Whether the profile page was successfully parsed; does not promise complete sections. |
| `enrichmentStatus` | `not_requested`, `limit_reached`, `enriched`, or `failed`. |
| `enrichmentError` | Reason a requested visit failed or could not finish, otherwise `null`. The search summary is retained. |
| `_parseWarnings` | Missing core fields or parsing issues. Export without `clean=true` to retain this underscore-prefixed field. |

Empty arrays mean no items were collected, not that the person has no experience, education, or skills. This actor does not fetch contact information, email addresses, phone numbers, or private profile sections. Selectors and English-language page messages are tested against fixtures; other languages and future LinkedIn layouts may need adjustments.

### Run report and failures

`RUN-SUMMARY` in the default key-value store contains `status`, `startedAt`, `finishedAt`, `profilesFound`, `profilesSaved`, `profilesEnriched`, `failedRequests`, `searchPages`, `limitReached`, `runTimeLimitReached`, `fatalError`, and `errors` (objects with `url`, `label`, and `message`). The output schema links directly to it.

When LinkedIn returns a login wall, verification challenge, empty page, or unknown layout, the actor also creates `DIAGNOSTIC.json` and `DIAGNOSTIC.png`. They contain the final URL, page title, link count, a short visible-text preview, and a private screenshot of the last failed page. Review them from the Output tab. They may contain visible LinkedIn account or profile information, so do not publish or share them without checking their contents.

A successful empty search is different from a page that failed to load: an explicit no-results message ends that search; an unrecognized page is retried. Request errors are recorded in the report instead of being mixed into the profile dataset. A run with some usable records and exhausted requests reports `partial`; Apify may still mark it successful, so integrations should check the report. Fatal access errors or a run with only failed requests fail the Apify run. Setup/input errors can occur before the report exists.

On normal failure, pending profiles are saved as summaries. Crawl state is periodically persisted and existing dataset rows are reconciled after migration. Abrupt termination during a storage write cannot provide transactional exactly-once delivery; consumers requiring that guarantee should deduplicate on `profileUrl`.

| Problem | Next step |
| --- | --- |
| Login wall | Log into LinkedIn manually, copy a fresh cookie, and start a new run. |
| Verification checkpoint | Resolve the challenge in your browser before starting a new run. |
| Proxy setup failure | Check residential proxy access or supply a working proxy configuration. |
| Empty or unrecognized page | Check account access and page language. If it persists, the page layout may have changed. |
| Too few profiles | Check the report, filters, page limit, and account visibility; use exact filters through a direct URL. |
| Profile visit failed | Use the retained summary and inspect `enrichmentError`; the profile may be unavailable. |
| Missing sections or parsing warnings | The field may be hidden, not loaded, or affected by a layout change. Enrichment does not expand every section. |

### Development

Requires Node.js 22 or later. Install the locked dependencies and run the offline checks:

```bash
npm ci
npm test
npm run check
```

On Windows with PowerShell script execution disabled, use `npm.cmd` in place of `npm`. Tests use HTML fixtures and injected storage operations; they do not log into LinkedIn or spend proxy credits. Schema tests use Apify's official validators and check emitted records against the dataset contract.

For a local live run, install Chrome, put your private input at `storage/key_value_stores/default/INPUT.json`, and run `npm start`. Set `APIFY_TOKEN` privately if using Apify proxy locally. The Docker build uses Apify's Node 22 Playwright/Chrome image and `npm ci`. Local storage, dependencies, caches, and environment files are excluded from the image.

Before publishing, build and run a small test on Apify with a valid cookie and proxy. Verify both summary-only and enriched results against the actual pages. Offline fixtures validate known layouts, not current live-site compatibility.

The schema setup follows [Apify input schemas](https://docs.apify.com/actors/development/actor-definition/input-schema/specification/v1), [output schemas](https://docs.apify.com/actors/development/actor-definition/output-schema), and [dataset schemas](https://docs.apify.com/storage/dataset-schema).

# Actor input Schema

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

Paste only the li\_at cookie value from DevTools > Application > Cookies > https://www.linkedin.com. This is a sensitive session credential, stored encrypted by Apify. Never share it in examples or logs.

## `searchQueries` (type: `array`):

Add at least one search. Text fields are combined as keywords, not exact title, company, or location filters. For exact filters, paste a people-search URL copied from LinkedIn. A direct URL overrides the text fields; pagination starts from the first page.

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

Hard cap on total number of unique profiles collected across all search queries combined.

## `scrapeFullProfiles` (type: `boolean`):

Visit profiles to collect visible About, Experience, Education, Skills and connection count. Adds requests and proxy usage. Does not expand every section or guarantee complete history or skills. Search summaries are retained if a visit fails.

## `maxProfilesToEnrich` (type: `integer`):

Maximum unique profiles to visit for details, also bounded by maxProfiles. Set 0 for summaries only. Ignored when profile details are disabled.

## `minDelayMs` (type: `integer`):

Minimum randomized wait before each navigation, including retries and profile visits. Navigation starts are spaced across concurrent pages.

## `maxDelayMs` (type: `integer`):

Maximum randomized wait before navigation; must be at least minDelayMs.

## `maxConcurrency` (type: `integer`):

Maximum pages running at once. The default is 1; increasing it uses more memory and can trigger restrictions.

## `maxRequestRetries` (type: `integer`):

Retries after a transient navigation or parsing failure. Login and verification challenges stop the run and require account attention.

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

Defaults to Apify residential proxy, which requires access and incurs proxy charges. You can supply your own proxy URLs or disable proxy use. A proxy cannot guarantee successful access.

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

Limits search pagination per query, including pages with overlapping profiles. Lower this to control run time and proxy use.

## `maxRunTimeSecs` (type: `integer`):

Stops the crawler and saves collected profiles before the Apify run timeout. Keep the Apify platform timeout at least 30 seconds higher than this value.

## Actor input object example

```json
{
  "searchQueries": [
    {
      "keywords": "senior backend engineer",
      "location": "Berlin"
    }
  ],
  "maxProfiles": 100,
  "scrapeFullProfiles": false,
  "maxProfilesToEnrich": 50,
  "minDelayMs": 2500,
  "maxDelayMs": 6000,
  "maxConcurrency": 1,
  "maxRequestRetries": 3,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  },
  "maxPagesPerQuery": 10,
  "maxRunTimeSecs": 240
}
```

# Actor output Schema

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

Unique profiles, their visible details, and enrichment status in the default dataset.

## `runSummary` (type: `string`):

Counts, completion status, and request errors. Check this when results are partial or empty.

## `diagnosticDetails` (type: `string`):

Created only when LinkedIn returns a login, verification, empty, or unrecognized page.

## `diagnosticScreenshot` (type: `string`):

Private screenshot of the last page that could not be processed. Created only on access or layout failures.

# 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 = {
    "searchQueries": [
        {
            "keywords": "senior backend engineer",
            "location": "Berlin"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("coolinbex/linkedin-talent-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 = { "searchQueries": [{
            "keywords": "senior backend engineer",
            "location": "Berlin",
        }] }

# Run the Actor and wait for it to finish
run = client.actor("coolinbex/linkedin-talent-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 '{
  "searchQueries": [
    {
      "keywords": "senior backend engineer",
      "location": "Berlin"
    }
  ]
}' |
apify call coolinbex/linkedin-talent-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,coolinbex/linkedin-talent-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/fZyREXuL75jd5TC2j/builds/t4pa0EWuMNbzlceXz/openapi.json
