# LinkedIn Profile Scraper - Public Profiles, No Login (`mochiboo/linkedin-profile-public-scraper`) Actor

Full public LinkedIn profiles of creators, executives and public figures, no login: headline, about, experience (grouped roles), education, skills, websites, recent posts. Hidden members are not returned, limited ones come back free; only full profiles are charged. No emails or phones.

- **URL**: https://apify.com/mochiboo/linkedin-profile-public-scraper.md
- **Developed by:** [mochi](https://apify.com/mochiboo) (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 $2.50 / 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?

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

## LinkedIn Profile Scraper - full public profiles of creators, executives and public figures, no login or cookies

Reads LinkedIn's **public** profile pages, the version anyone sees without signing in. No LinkedIn account, no cookies, no login.

Pay per result, at the rate shown in the pricing panel of this page, plus a very small per-run start fee. You are charged only for the results delivered.

### Who this works for - read this first

This actor is for **creators, executives, founders, authors, speakers and other public figures**. LinkedIn shows a signed-out visitor the **full** public page only for some members, and not for all of those: **only about half of the well-known members we tried came back in full (16 of 32 on 2026-10-20).** **Ordinary members are often hidden (LinkedIn answers HTTP 999) or limited: about 4 in 5 of the ordinary members we tried were hidden (27 of 33 on 2026-10-20).** This actor does not promise every profile you list. What you get for each one:

| What LinkedIn shows a signed-out visitor | What you get | Charged |
|---|---|---|
| The full public profile | One row in the dataset: experience, education, skills, companies, recent posts and more | Yes, per row |
| A limited profile: name, location, followers, a shortened about, the current employer and recent posts; LinkedIn masks values with asterisks, or the page shows no headline, experience, education or skills at all | One entry in the `LIMITED_PROFILES` record of the key-value store, counted as `limited` in `RUN_SUMMARY` | No, free |
| Nothing (HTTP 999: the member hides the public page, the address does not exist, or LinkedIn refuses the request) | An entry in the `FAILURES` record with status `unavailable` | No |

What we measured on 2026-10-20 (Apify platform, datacenter proxy):

- Well-known members (32 executives, creators, founders, authors and speakers): 16 came back full, 11 limited and 5 hidden (HTTP 999).
- Ordinary members (33 fresh ones): 27 were hidden (HTTP 999), 6 limited and none full.
- An earlier sample of 54 different real profiles: 40 were served (74%) and 14 answered HTTP 999 (26%). Of the 40 served, 18 were full (45%) and 22 were limited (55%). Every full page had at least 10,985 followers, and all 17 served members with fewer than 10,000 followers were limited. A profile that was served once was served again on every later request (18 of 18 repeats), so full, limited and hidden are properties of the member, not of the moment.

The samples were small and not drawn at random: read these shares as indicative.

In short: a list of well-known members comes back as full rows about half of the time. A list of ordinary employees comes back mostly as `unavailable` entries (about 4 in 5) with a few free limited records, and almost never as a charged row.

### What you get per full profile

One dataset row with:

- Identity: `profileUrl`, `publicIdentifier`, `fullName`, `headline`, `location`, `countryCode`, `photoUrl`, `badge` (for example "Creator, Top Voice")
- Reach: `followers` (number) and `connections` (text such as `500+` or `8`, as LinkedIn shows it)
- `about` (line breaks kept, no HTML) and `jobTitles`
- `currentCompanies` and `pastCompanies`: name, LinkedIn URL, start and end date
- `experience`: every position the page shows, in page order, with title, company, company URL, `isCurrent`, `dateRange`, `startDate`, `endDate`, `duration`, `location` and `description`. Several roles at one employer are separate entries; for them `company` is the employer name from LinkedIn's group header.
- `education` (school, degree, field of study, dates, description), `volunteering`, `publications`, `honorsAwards`, `skills` and `websites` (one website or several)
- `articles` and `recentPosts`: the articles and posts that appear in the page's structured data (we saw 0 to 9 posts and up to 10 articles per profile; this is not the member's full post history). Title or text, date, likes, link. No extra request. Turn both off with `includePosts: false`.
- `limitedProfile` and `maskedValues`: always `false` and `0` in the dataset, because limited profiles are not dataset rows. They are kept so that rows and `LIMITED_PROFILES` records share the same field names.
- `input` (what you typed) and `scrapedAt`

Which sections appear depends on the member: sections a member has not filled in, or has hidden, come back as `null` or `[]`. On the 18 full pages we saw, `fullName`, `profileUrl`, `followers`, `headline`, `experience` and `recentPosts` were filled on 18 of 18, `location` and `about` on 17, `education` on 14 and `skills` on 12. Do not rely on any section being present.

Two things to check before you rely on a row:

- `profileUrl` is the address LinkedIn gives for the member. It can carry LinkedIn's regional host (for example `uk.linkedin.com` or `sg.linkedin.com`) instead of `www`.
- Compare `input` with `publicIdentifier`. For an old or alternative address LinkedIn sometimes serves another member's page (3 of the 45 different profiles served in our test runs: two were the same person under a new address, one was an unrelated member). The actor does not look any further and never opens another profile.

### The LIMITED_PROFILES record (free)

A JSON list in the key-value store, one object per limited profile: `fullName`, `profileUrl`, `publicIdentifier`, `location`, `followers`, `about` (LinkedIn shortens it on limited pages), `currentCompany` (the first current employer shown), `recentPosts` (left out when `includePosts` is `false`), `limitedProfile` (always `true`), `maskedValues`, `input` and `scrapedAt`. Masked values are never passed on as asterisks. A page that shows no headline, experience, education or skills at all counts as limited even when it has no asterisks; its `maskedValues` is then `0`. It is the "Limited profiles" link in the run's output, and it is written (as `[]` when there was none) by every run that fetched pages. It is never charged.

### Inputs

- `profiles`: LinkedIn profile URLs or public profile slugs: `https://www.linkedin.com/in/satyanadella`, `https://sg.linkedin.com/in/satyanadella/` or `satyanadella`. Duplicates (any capitalisation) are fetched once.
- `includePosts`: default `true`.
- `maxProfiles`: 1 to 1000 (default 100). It caps the full profiles you are charged for; limited and unavailable profiles do not count towards it.
- `proxyConfiguration`: default is the Apify datacenter proxy.

Anything that is not a LinkedIn profile URL or slug (a company URL, a name with spaces) is not fetched and is listed in `FAILURES` as `unsupported_input`. If none of your inputs is usable the run fails at once with a clear message and nothing is charged.

### What is NOT collected

- **No emails, phone numbers or other contact data.** LinkedIn's "Contact Info" needs a login and is never touched. Email addresses and phone numbers that members write into their text are replaced with `[email redacted]` / `[phone redacted]` in `about`, every `description` and the post text; plain addresses are also replaced in the other text fields.
- **No search by name or company**, no people lists, no guessing of profile addresses: you give the profiles, the actor fetches those.
- The actor **does not follow links to other people** and never opens another profile. It does not read "people also viewed" / "other similar profiles", mutual connections or recommendations written by others. (Names that the member mentions in their own posts or descriptions stay in that text.)
- Not extracted at all: certifications, languages, projects, courses, test scores, patents, organizations and instructor courses.
- Nothing behind a login: if LinkedIn asks for one, the profile is recorded as blocked and nothing is worked around.

### Output notes and failures

- Public page data only: exactly what LinkedIn shows a signed-out visitor, as of the moment of the run. Counts such as followers change daily.
- A profile LinkedIn answers with HTTP 999 is written to the `FAILURES` record in the key-value store as `unavailable`. **A 999 is never retried.** We saw 999 for profiles that do not exist and for ordinary members at scattered places in a list; LinkedIn uses it for profiles hidden from the public and for requests it refuses, so 999 does not prove the profile is missing. Members who answered 999 answered it again later (10 of 10 about two hours later), so it is mostly a property of the member and not a sign that the actor is blocked. A 404 is `not_found`. A page without profile data is `no_profile_data`. Requests LinkedIn blocks (HTTP 429, 403, a sign-in wall) are `blocked`. A redirect to another profile address is followed once; one that cannot be resolved is `redirect_unresolved`; a request that kept failing is `request_failed`. **None of these are charged**, and neither are limited profiles.
- **If LinkedIn blocks the actor it records the block, does not retry and does not try to get around it.** The run stops, keeping everything already written, when more than 20% of the requests so far were blocked (HTTP 429, 403 or a sign-in wall; checked every 10 requests) or when LinkedIn answered HTTP 999 to **25 profiles in a row**. The limit is 25 because about 4 in 5 ordinary members answer 999, so a long list of them can hold long runs of 999 without any block; 25 in a row is where we stop asking. When the run stops this way, `RUN_SUMMARY` says `blocked` and `complete: false`, the profiles after the stop were not requested and are not listed in `FAILURES`: compare the `input` values of your output with your list and run the rest in a new run. If the 25th 999 comes from the last profile of your list, nothing was skipped and the run ends `finished`.

### Run summary and limits

The `RUN_SUMMARY` record has `complete`, `terminationReason` (`finished`, `max_results`, `charge_limit`, `time_budget`, `blocked`, `request_failed`), `rowsPushed` (full profiles in the dataset), `requests`, `blocked`, `unavailable` and `limited` (records in `LIMITED_PROFILES`). `blocked` means a stop rule ended the run while profiles were still waiting. A very large run may stop early before the run timeout; the summary then says `time_budget` and what was written is kept.

Requests are sequential with a 2 second pause between them. Measured on 2026-10-20 (Apify platform, datacenter proxy, 512 MB): 4.8 to 6.4 seconds per profile including the pause (20 profiles took 118 seconds, 27 took 149 and 172 in two runs, 45 took 218, 16 took 81), so about 9 to 12 profiles a minute and 100 profiles in 8 to 11 minutes. Peak memory at 512 MB was 89 MiB for 1 profile, 93 MiB for 5, 204 MiB for 20, 328 MiB for 45 and 345 MiB for 27 pages of well-known members (large pages); a second run of the same 27 pages peaked at 307 MiB. For big lists set the run timeout to 7200 seconds: at the default 3600 seconds a run stops after about 550 to 740 profiles with `time_budget` and keeps its rows. The actor stops starting requests a little before the run timeout and caps every request by the time left, then writes what it has and `RUN_SUMMARY`.

### Example row (trimmed)

```json
{
  "profileUrl": "https://www.linkedin.com/in/andrewyng", "publicIdentifier": "andrewyng", "fullName": "Andrew Ng",
  "headline": "DeepLearning.AI, AI Fund and AI Aspire", "location": "Palo Alto, California, United States", "countryCode": "US",
  "followers": 2659387, "connections": "500+", "about": "Personal home page: http://andrewng.org", "badge": "Creator, Top Voice",
  "limitedProfile": false, "maskedValues": 0,
  "currentCompanies": [{"name": "DeepLearning.AI", "linkedinUrl": "https://www.linkedin.com/company/deeplearningai", "startDate": "2017-06", "endDate": null}],
  "experience": [{"title": "Founder", "company": "DeepLearning.AI", "isCurrent": true, "dateRange": "Jun 2017 - Present", "startDate": "Jun 2017", "endDate": "Present", "duration": "9 years 5 months"}],
  "education": [{"school": "University of California, Berkeley", "degree": "Doctor of Philosophy - PhD", "fieldOfStudy": "Computer Science", "dateRange": "2003 - Present"}],
  "skills": ["Education", "Educational Technology", "Machine Learning", "Artificial Intelligence"],
  "websites": [{"label": "Company Website", "url": "http://www.coursera.org"}, {"label": "Personal Website", "url": "http://www.cs.stanford.edu/~ang/"}],
  "recentPosts": [{"datePublished": "2026-09-29T15:08:53.718Z", "likes": 1390}],
  "input": "andrewyng", "scrapedAt": "2026-10-20T04:26:37.434Z"
}
```

(A row from a platform run on 2026-10-20; the example is trimmed and the counts change daily.)

### Good for

linkedin profile scraper, linkedin profile data, linkedin people data, linkedin experience, linkedin no cookies, linkedin profile api, recruiter data, sales prospecting, b2b enrichment, linkedin education, linkedin skills.

Use of the data is your responsibility; check LinkedIn's terms and the laws that apply to you.

# Actor input Schema

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

e.g. https://www.linkedin.com/in/satyanadella or just satyanadella. Public profiles only. Works for creators, executives and public figures: ordinary members are often hidden or limited by LinkedIn; limited profiles are free and come back in the LIMITED_PROFILES record.

## `includePosts` (type: `boolean`):

Adds recentPosts and articles to each row: the posts (0 to 9 seen) and articles (up to 10) that appear in the page's structured data. No extra requests.

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

Max full profiles (the charged rows) per run. Limited and unavailable profiles do not count. Hard cap 1000.

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

Default: Apify datacenter proxy.

## Actor input object example

```json
{
  "profiles": [
    "https://www.linkedin.com/in/satyanadella",
    "williamhgates"
  ],
  "includePosts": true,
  "maxProfiles": 100,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

One row per full public profile: headline, experience, education, skills, companies and recent posts.

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

What the run wrote and how it ended (the RUN_SUMMARY record in the default key-value store).

## `limitedProfiles` (type: `string`):

Profiles LinkedIn shows only in part (name, location, followers, shortened about, current employer, recent posts). Never charged: the LIMITED_PROFILES record in the default key-value store.

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

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

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

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,mochiboo/linkedin-profile-public-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/jEWL5s99GIet7kIxn/builds/fK26V0elbmbT7OZgH/openapi.json
