# LinkedIn Profile Pro - Profile + Posts + Articles, No Cookies (`constructive_calm/linkedin-profile-pro`) Actor

Scrape LinkedIn profiles in depth — name, headline, country, work history with dates, education with dates, recent posts with full text + likes, and published articles — all in one call. No cookies, no login required.

- **URL**: https://apify.com/constructive\_calm/linkedin-profile-pro.md
- **Developed by:** [Omar Eldeeb](https://apify.com/constructive_calm) (community)
- **Categories:** Lead generation, Social media, Other
- **Stats:** 35 total users, 11 monthly users, 100.0% runs succeeded, 2 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

$4.00 / 1,000 linkedin profile resolveds

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/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 Pro

Deep, cookie-free LinkedIn profile scraper. Hand it profile URLs (or bare slugs), get back full profile metadata **plus** recent posts **plus** published articles — in a single call, in one dataset row per profile.

Built for Clay, n8n, and Make pipelines where you currently have to call two or three separate actors to assemble the same picture.

***

### What you get per profile

```jsonc
{
  "url": "https://www.linkedin.com/in/williamhgates/",
  "slug": "williamhgates",
  "name": "Bill Gates",
  "headline": "Creator, Top Voice",
  "about": "Co-chair of the Bill & Melinda Gates Foundation...",
  "photoUrl": "https://media.licdn.com/...",
  "country": "US",
  "city": "Seattle, Washington, United States",
  "followers": 40000000,
  "connections": 8,

  "workHistory": [
    { "company": "Gates Foundation", "companyLinkedInUrl": "...",
      "title": null, "description": null, "location": null,
      "startDate": "2000", "endDate": null, "isCurrent": true },
    { "company": "Breakthrough Energy", "startDate": "2015", "isCurrent": true, ... },
    { "company": "Microsoft", "startDate": "1975", "isCurrent": true, ... }
  ],
  "education": [
    { "school": "Harvard University", "startYear": 1973, "endYear": 1975 }
  ],
  "languages": [],
  "awards": [],
  "externalLinks": [],

  "recentPosts": [
    { "urn": "7464707901635395584",
      "url": "https://www.linkedin.com/posts/williamhgates_...",
      "text": "I felt so powerless and scared when my dad was diagnosed with Alzheimer's...",
      "postedAt": "2026-05-25T03:10:26.346Z",
      "likes": 1765, "comments": 84, "shares": null,
      "isArticle": false, "headline": null }
    // up to ~10 recent posts per profile
  ],
  "publishedArticles": [
    { "url": "https://www.linkedin.com/pulse/every-year-2-million-...",
      "title": "Every year, 2 million babies are stillborn.",
      "datePublished": "2026-05-02T00:35:55.000+00:00",
      "description": "A simple retinal scanner can change that." }
    // up to ~10 articles per profile (when published)
  ],

  "fetchedAt": "2026-05-26T15:20:00.000Z",
  "source": {
    "htmlSize": 613470,
    "proxyGroup": "DATACENTER",
    "attemptsRequired": 1,
    "uaUsed": "facebookexternalhit"
  }
}
```

***

### Input

| Field | Type | Default | Notes |
|---|---|---|---|
| `profileUrls` | string\[] | **required** | Full LinkedIn URLs or bare slugs. Both work. `https://uk.linkedin.com/in/...` URLs accepted. `/company/`, `/jobs/`, non-LinkedIn URLs rejected silently. |
| `includeRecentPosts` | boolean | `true` | Extract up to ~10 recent posts per profile. Bundled in same charge. |
| `includeArticles` | boolean | `true` | Extract LinkedIn-published articles. Bundled in same charge. |
| `enrichEngagementFromDom` | boolean | `true` | Parse comment + share counts (post `likes` already come from JSON-LD). Negligible cost. |
| `postedAfter` | string | — | Drop posts/articles older than this date. Accepts `YYYY-MM-DD` or full ISO 8601 timestamp. Leave empty for no lower bound. |
| `postedBefore` | string | — | Drop posts/articles newer than this date. Same format as `postedAfter`. Pair both for a between-range filter. Undated items are dropped when any date filter is active. |
| `maxRetriesPerProfile` | integer | `5` | Fresh-IP attempts per profile. 1-10. |
| `useResidentialFallback` | boolean | `false` | After 3 datacenter failures, retry via Apify residential proxy. Boosts success ~70% → ~85%. Adds ~$5/1K cost on stubborn profiles only. |
| `concurrency` | integer | `5` | Parallel profile fetches. 1-20. |
| `maxItems` | integer | `500` | Hard cap on profiles processed. |
| `proxyConfiguration` | object | Apify datacenter `BUYPROXIES94952` | Override only if you have a specific reason — datacenter is the validated optimum (cheaper AND higher success rate than residential for this surface). |

***

### Pricing

**$0.004 per profile resolved.** That's it. Recent posts and articles bundled — no per-post charge.

- First **10 profiles per run are FREE** (per-run trial — useful for testing).
- **No charge** for invalid slugs, soft-blocks, or empty responses.
- **No charge** for duplicate inputs (we dedupe upfront).
- Charge only fires when a Person JSON-LD block has been successfully parsed.

Compare with the alternative on the Store: profile scraper at $4/1K + profile-posts at $2/1K = $6/1K bundled. This one is **$4/1K bundled**, in one call, one dataset row, one charge.

***

### How it works (high-level)

LinkedIn whitelists social-link-preview bots (`facebookexternalhit/1.1`, `Twitterbot/1.0`) and serves them rich server-rendered HTML with structured JSON-LD blocks: `Person`, `DiscussionForumPosting`, `Article`. We fetch each profile with that UA via Apify's datacenter proxy, parse the JSON-LD, augment with DOM-extracted engagement counts (comments, shares), and emit a normalized row.

When a profile resists datacenter IPs (rotating residential block-lists land on Apify's pool), the optional residential fallback kicks in after 3 failures. Soft-block detection is byte-precise (LinkedIn's "we noticed unusual activity" template is exactly 317,547 bytes), so we never mistake it for a real 200.

No cookies, no login, no LinkedIn account, no Voyager API impersonation. Public data only.

***

### Why some profiles fail to resolve

If a profile fails, the warning log tells you exactly why:

| Reason in log | What it means | Can retries / residential fallback fix it? |
|---|---|---|
| `profile_not_found` | The slug genuinely doesn't exist on LinkedIn. Probably a typo or the profile was deleted/renamed. | No — check spelling. |
| `not_publicly_visible` | The slug exists but the owner has disabled public-profile visibility. You can see this profile if you're logged into LinkedIn, but our cookie-free path cannot. | No — this is a LinkedIn privacy setting, not anti-bot. Common for recruiters, executives, and privacy-conscious users. |
| `soft_block` | LinkedIn anti-bot fired (the 317KB "unusual activity" template). | Yes — usually clears with `useResidentialFallback: true` or higher `maxRetriesPerProfile`. |
| `fetch_errors` | Network errors before reaching LinkedIn (proxy / DNS). | Yes — retry or try a different proxy group. |
| `no_person_jsonld` | Page returned content but no Person JSON-LD shape we recognize (likely a new LinkedIn rendering variant). | Worth reporting — please open an issue with the slug. |

No charge fires for any failed profile.

***

### Common use cases

- **Clay/n8n/Make enrichment pipelines** — pipe a list of LinkedIn URLs in, get full profiles + recent activity out.
- **B2B prospecting with context** — beyond just name+title, see what they've posted recently to personalize outreach.
- **Investor / hedge fund headcount tracking** — re-run periodically, diff `workHistory[].isCurrent` to detect hires and exits.
- **Competitive intel on key people** — track posts + articles to monitor what competitors' execs are talking about.
- **Recruiting research** — surface candidates' recent activity, not just their static resume.

***

### What's NOT included (cookie-free limits)

- **Skills, certifications, volunteer, recommendations** — these sections aren't in the cookie-free JSON-LD. Add them in v0.2 if there's demand (DOM-extractable).
- **Per-reaction breakdown** (like vs love vs celebrate) — only total likes available.
- **Comment thread bodies** — only comment counts (auth-walled bodies).
- **Sales Navigator, Recruiter, full activity history** — separate auth-scope, out of scope.
- **Per-position job titles** — for some profiles LinkedIn doesn't include per-`worksFor` titles in the JSON-LD. You'll see `title: null` even when the company is present. Profile-side limitation.

Expect ~95% success rate on public profiles at default settings; ~98% with `useResidentialFallback: true`. The remaining tail is profiles where the owner has set `Public profile` to off (cookie-free fundamentally cannot reach those — see the failure-reason table above) or new LinkedIn rendering variants we haven't taught the extractor about yet. **Failed profiles are NOT charged.**

**Heads up on "partial-public" profiles:** some profile owners enable the master "Public profile" switch but disable per-section visibility (Experience, Education, Summary, etc.) in their LinkedIn privacy settings. The actor will resolve these profiles and charge $0.004 because we extract whatever LinkedIn ships — but the row may have a redacted last name (e.g. "Dianne E."), an empty `about`, mostly-empty `workHistory` entries, and few/no posts. There is no cookie-free way around per-section privacy. If your input list includes many such profiles, factor it into your cost expectation.

***

### License

MIT — use the data within LinkedIn's Terms of Service and applicable scraping law (hiQ v. LinkedIn covers public data; this actor never touches authenticated surfaces).

# Actor input Schema

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

Paste full LinkedIn profile URLs (https://www.linkedin.com/in/<slug>) or just the slugs. Both work — we auto-normalize.

## `includeRecentPosts` (type: `boolean`):

Extract up to ~10 recent posts per profile (full text, date, like count, post URL). Bundled with profile in same charge — no extra cost.

## `includeArticles` (type: `boolean`):

Extract LinkedIn-published articles in addition to recent posts. Bundled — no extra cost.

## `enrichEngagementFromDom` (type: `boolean`):

Parse comment and share counts from HTML markup (post likes already come from JSON-LD). Negligible extra cost.

## `postedAfter` (type: `string`):

Drop posts/articles older than this date. Accepts YYYY-MM-DD or full ISO 8601 timestamp. Leave empty for no lower bound. Pair with 'postedBefore' for a between-range filter.

## `postedBefore` (type: `string`):

Drop posts/articles newer than this date. Accepts YYYY-MM-DD or full ISO 8601 timestamp. Leave empty for no upper bound.

## `maxRetriesPerProfile` (type: `integer`):

How many fresh-IP attempts before giving up on a profile. Higher = more reliable but more proxy spend. 5 is the validated sweet spot.

## `useResidentialFallback` (type: `boolean`):

After 3 datacenter failures, retry via Apify residential proxy. Increases success rate to ~85-90% but adds ~$5/1K cost on failed profiles only. Recommended for stubborn profiles.

## `concurrency` (type: `integer`):

Parallel profile fetches. Higher = faster but more 429 risk. 5 is the validated sweet spot.

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

Hard cap on profiles processed in this run. Protects against runaway input.

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

Apify datacenter proxy (BUYPROXIES94952) is the validated optimum for cookie-free LinkedIn — 8x cheaper than residential at higher success rate. Override only if you have a specific reason.

## Actor input object example

```json
{
  "profileUrls": [
    "https://www.linkedin.com/in/williamhgates/",
    "https://www.linkedin.com/in/satyanadella/",
    "https://www.linkedin.com/in/simonsinek/"
  ],
  "includeRecentPosts": true,
  "includeArticles": true,
  "enrichEngagementFromDom": true,
  "maxRetriesPerProfile": 5,
  "useResidentialFallback": false,
  "concurrency": 5,
  "maxItems": 500,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "BUYPROXIES94952"
    ]
  }
}
```

# Actor output Schema

## `results` (type: `string`):

All extracted profile rows incl. work history, education, recent posts, and articles.

## `resultsCsv` (type: `string`):

Same dataset as CSV.

# 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/williamhgates/",
        "https://www.linkedin.com/in/satyanadella/",
        "https://www.linkedin.com/in/simonsinek/"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("constructive_calm/linkedin-profile-pro").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/williamhgates/",
        "https://www.linkedin.com/in/satyanadella/",
        "https://www.linkedin.com/in/simonsinek/",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("constructive_calm/linkedin-profile-pro").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).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/williamhgates/",
    "https://www.linkedin.com/in/satyanadella/",
    "https://www.linkedin.com/in/simonsinek/"
  ]
}' |
apify call constructive_calm/linkedin-profile-pro --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=constructive_calm/linkedin-profile-pro",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/BYC9EbuHgPC4b3u9I/builds/cLEn9WA3KycSkVOmG/openapi.json
