# LinkedIn Profile Scraper — No Cookies, No Key | $3.9/1K (`bovi/linkedin-profile-scraper`) Actor

Scrape public LinkedIn profile records from profile URLs or bare public IDs. Get names, headlines, companies, locations, education, photos, followers, and parsing quality. Run with profileUrls; no login, cookies, or API key. Pricing: $0.0039 per returned record on the Free plan.

- **URL**: https://apify.com/bovi/linkedin-profile-scraper.md
- **Developed by:** [Vitalii Bondarev](https://apify.com/bovi) (community)
- **Categories:** Lead generation, Business
- **Features:** Limited permissions
- **Stats:** 80 total users, 16 monthly users, 97.7% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.78 / 1,000 profile records

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

Scrape public LinkedIn profile data from URLs or bare public IDs into structured records using LinkedIn's server-rendered JSON-LD and Open Graph data. No login, cookies, `li_at`, or API key is required.

- Enrich LinkedIn URLs in Clay, Make, or n8n with names, headlines, companies, and locations.
- Build recruiter and sales contact sheets from profile links.
- Clean profile lists with canonical URLs and public slugs.
- Review `parse_path`, `parse_confidence`, and `warnings` for each record.
- Set `maxProfiles` to limit processing and cost.

### What you get

Fields are `null` when the source does not provide them.

| Field family | Fields | Type and empty value | Description |
|---|---|---|---|
| Identity | `name` | string; not pushed without a name | Full name |
| Identity | `first_name`, `last_name` | string or `null` | Split name parts |
| Profile reference | `profile_url` | string | Canonical LinkedIn profile URL |
| Profile reference | `slug` | string or `null` | Vanity slug |
| Professional summary | `headline` | string or `null` | Rejected when it is only a site label, employer, school, or location |
| Professional summary | `job_title` | string or `null` | Multiple titles joined with `"; "` |
| Professional summary | `description` | string or `null` | About or summary text |
| Profile labels | `badges` | string array; `[]` when none | Labels such as `Authoritative`, `Creator`, `Top Voice`, `Influencer`, `Premium`, and `Verified` |
| Company | `current_company`, `current_company_url` | string or `null`; URL is `null` for OG-only records | Current employer and company page |
| Location | `location`, `country` | string or `null`; JSON-LD only | Region and country code |
| Education | `education` | array of `{name,url,start,end}`, or `null`; OG-only is `null` | Schools and LinkedIn-provided years |
| Experience | `experience` | array of `{name,url,start,end}`, or `null`; OG-only is `null` | `worksFor` and non-school `alumniOf` entries |
| Media | `photo_url` | string or `null` | Time-limited LinkedIn CDN link |
| Audience | `follower_count` | integer or `null`; OG-only is `null` | Followers when exposed |
| Social links | `same_as` | string array or `null`; OG-only is `null` | LinkedIn's `sameAs` list |
| Posts | `recent_posts` | array of `{headline,url,published}`; at most 10, or `null`; OG-only is `null` | Recent post stubs; `published` is ISO 8601 or `null` |
| Languages | `languages` | string array or `null`; OG-only is `null` | Returned language values |
| Parsing quality | `parse_path` | string | Extraction source and user-agent path |
| Parsing quality | `parse_confidence` | number from 0 to 1, rounded to 3 decimals | Confidence score |
| Warnings | `warnings` | string array; `[]` when none | Codes such as `no_work_history`, `no_location`, and `og_only` |

The parser first reads public server-rendered JSON-LD `Person` data and can fall back to Open Graph metadata. `parse_path` records the source and user-agent path, such as `jsonld:facebookexternalhit/1.1`.

Anonymous data does not provide full dated work history, skills, certifications, recommendations, or contact email or phone. Public work history can contain company stubs. JSON-LD records get `no_work_history` only when both `current_company` and `experience` are absent; confidence is reduced by 0.05. OG-only records get `og_only`; `experience` is `null`, while `current_company` can come from the OG `Experience:` segment.

### Sample output

The sample is shortened by dropping nonessential fields and list items.

```json
{
  "profile_url": "https://www.linkedin.com/in/williamhgates",
  "slug": "williamhgates",
  "name": "Bill Gates",
  "job_title": null,
  "current_company": "Gates Foundation",
  "current_company_url": "https://www.linkedin.com/company/gates-foundation",
  "location": "Seattle, Washington, United States",
  "country": "US",
  "education": [{"name":"Harvard University","url":"https://www.linkedin.com/school/harvard-university/","start":1973,"end":1975}],
  "experience": [{"name":"Gates Foundation","url":"https://www.linkedin.com/company/gates-foundation","start":null,"end":null}],
  "follower_count": 40779437,
  "parse_path": "jsonld:facebookexternalhit/1.1",
  "parse_confidence": 1,
  "warnings": []
}
```

### Pricing

This Actor uses pay-per-event pricing. Each returned profile record charges one `profile-record` event; a profile that produces no returned record is not charged.

| Plan | Price |
|---|---:|
| Free | $0.0039 per profile record |
| Silver | $0.003822 per profile record |
| Bronze | $0.003861 per profile record |
| Gold | $0.003783 per profile record |
| Platinum | $0.003744 per profile record |
| Diamond | $0.003705 per profile record |

Worked example: **1,000 results = $3.90 on the Free plan**; **10,000 results = $39.00 on the Free plan**.

The Apify Free plan includes **$5 of monthly platform usage credit**. At the Free-plan event price, the $5 credit covers **1,282 runs returning one profile record each (1,282 profile records)**. Free-plan runs process at most 25 distinct valid inputs per run. Paid plans do not have that per-run Free-plan cap.

This Actor does not use the Pay per event + usage option, so compute, proxy traffic, and storage are included in event prices and are not billed separately.

### How to use

1. Open the Actor in your Apify account.
2. Enter LinkedIn profile URLs or bare public IDs in `profileUrls`.
3. Set `maxProfiles` if you want to cap processing.
4. Leave the prefilled Apify Proxy configuration unless you need a custom setup.
5. Run the Actor and inspect the dataset for one record per returned profile.
6. Repeat the run through Apify Schedules; weekly is a starting point.

Export the dataset as JSON or CSV, or read it through the Apify dataset API for an automation step.

Example input:

```json
{
  "profileUrls": [
    "https://www.linkedin.com/in/williamhgates/",
    "satyanadella"
  ],
  "maxProfiles": 2,
  "disableUsageStats": false
}
```

Full URLs and bare slugs are normalized to `https://www.linkedin.com/in/{slug}`; query strings, fragments, and trailing slashes are removed.

### Input parameters

| Parameter | What it does | Default |
|---|---|---|
| `profileUrls` | Required array of LinkedIn profile URLs or bare public IDs | Required |
| `maxProfiles` | Caps profiles scraped; `0` means all provided, subject to the Free-plan cap | `0` |
| `proxyConfiguration` | Apify Proxy settings; the prefilled configuration uses the `RESIDENTIAL` group. If omitted, the Actor uses Apify datacenter proxy first and switches a profile to RESIDENTIAL after repeated blocks (RESIDENTIAL is also used if datacenter proxy is not available) | Prefilled Apify Proxy configuration |
| `disableUsageStats` | Set to `true` to turn off the run summary in Usage statistics | `false` |

### Limits and edge cases

| Condition | What you see | What to do |
|---|---|---|
| Empty, malformed, or unresolvable profile URL or ID | Input is skipped; no record or charge | Supply a LinkedIn `/in/` URL or bare public ID |
| LinkedIn blocks a request | Retries on fresh proxy IPs, up to 5 tries; then skips the profile without a record or charge | Retry later |
| Proxy rejects the selected group | With prefilled `RESIDENTIAL`, one shared ladder drops to datacenter for the rest of the run; direct access is disabled. If no rung remains, the profile is skipped and not charged | Keep Apify Proxy available and retry |
| JSON-LD has no public work history | `no_work_history` is added only when both work fields are absent; confidence is reduced by 0.05 | Treat work history as unavailable |
| Profile has no JSON-LD | An Open Graph record may be returned with `parse_path` beginning `og:`, `og_only`, and confidence 0.6 | Review `parse_path`, `parse_confidence`, and `warnings` |
| Anonymous data does not expose a field | The field is `null` or unavailable; unavailable fields are not fabricated | Use the fields present |
| No valid targets remain after validation | Run ends Succeeded with an empty dataset and `No valid LinkedIn profile URLs in the input; 0 profiles, nothing was charged.` | Correct `profileUrls` |
| URL does not open a public profile (LinkedIn answers HTTP 404, confirmed on 2 IPs) for every valid target | Run ends Succeeded with 0 records; status says the input URL(s) were not found on LinkedIn; nothing is charged | Fix the profile URLs; running again gives the same result |
| Every valid target fails for another cause | Run ends Failed; status and log name causes such as blocked, rate-limited, network error, no usable proxy, or no parseable data | Correct inputs or proxy setup and retry |
| Some targets fail | Run ends Succeeded; status counts pushed and failed profiles with causes; failed profiles are not charged | Review records and retry failed profiles |
| Proxy setup fails | Run ends Failed before requests; nothing is charged | Retry later or check Apify Proxy |
| Run aborts, times out, or crashes | Completed batches of 10 remain saved and charged; up to 9 records in the unfinished batch are not saved or charged | Check the dataset and rerun unfinished inputs |
| Free-plan cap is reached | At most the first 25 distinct valid inputs after validation and de-duplication are processed | Set `maxProfiles` lower or use a paid plan |
| Profiles are processed sequentially | Requests use a 3–6 second delay between profiles, each on a fresh IP; after the first block in a run the delay goes back to 8–15 seconds | Allow time for completion |

### FAQ

#### How much does it cost to scrape LinkedIn profiles?

The Free plan charges $0.0039 per returned `profile-record`; Silver is $0.003822, Bronze $0.003861, Gold $0.003783, Platinum $0.003744, and Diamond $0.003705. **1,000 results = $3.90 on the Free plan**.

#### What happens if the dataset is empty?

If validation leaves no valid targets, the run succeeds with an empty dataset and nothing is charged. If every valid target fails, the run fails and names the cause; successful partial runs remain Succeeded.

#### Does the Actor need LinkedIn cookies or an API key?

No. It uses public data served to social-preview bots and does not require a LinkedIn login, cookies, `li_at`, LinkedIn API key, external proxy key, or proxy credentials.

#### Why is a field null or incomplete?

Some information is not rendered anonymously. Work history can be limited to company stubs, while full dated work history, skills, certifications, recommendations, and contact email or phone are unavailable. Check `parse_path`, `parse_confidence`, and `warnings`.

#### Is collecting this public data legal or allowed?

The Actor collects public information that LinkedIn serves to anonymous social-preview bots. Whether a collection or use is legal or allowed depends on your use case, applicable law, and the source’s terms.

### Related actors

- [LinkedIn Company Scraper — Exact Employee Count](https://apify.com/bovi/linkedin-company-scraper) — related LinkedIn data for company-level records.
- [GitHub Email & Lead Enricher — Devs to CRM Leads | Official API](https://apify.com/bovi/github-profile-email-enricher) — related profile enrichment for developer and CRM lead workflows.
- [B2B Leads List Builder — Maps + Email + Phone Enrichment](https://apify.com/bovi/b2b-leads-list-builder) — related B2B lead-list and enrichment workflow.

### Usage statistics

This Actor creates a small, content-free summary at the end of each run, used only to monitor reliability and improve this Actor. No input values, credentials or scraped data are sent.

A copy of the exact record for your run is saved as `USAGE_STATS` in your own Apify key-value store.

To opt out, set `disableUsageStats` to `true` in the input. Nothing is sent then.

[Full list of collected fields](https://api.apify.com/v2/key-value-stores/v9rgHjMKLDGtHf8tD/records/index.html)

# Actor input Schema

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

LinkedIn profile URLs or bare public IDs to scrape. Accepts full URLs (https://www.linkedin.com/in/williamhgates/) or bare slugs (satyanadella). No login, no cookies, no API key. Returns the public data LinkedIn renders to social-link-preview bots (name, headline, current company, location, education, photo, followers). Each returned profile is one charge.

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

Cap the number of profiles scraped from the list (0 = all provided). Use to control cost on large inputs.

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

Apify proxy settings. RESIDENTIAL is the prefilled default — it is reliable everywhere and accessible on every Apify plan (datacenter IPs are a paid add-on not all accounts have). The scraper rotates IPs automatically on any block. Leave as-is unless you need a custom setup.

## `disableUsageStats` (type: `boolean`):

See the README Usage statistics section for details.

## Actor input object example

```json
{
  "profileUrls": [
    "https://www.linkedin.com/in/williamhgates/",
    "satyanadella"
  ],
  "maxProfiles": 0,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  },
  "disableUsageStats": false
}
```

# Actor output Schema

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

Dataset of profile records (name, headline, badges, job_title, current_company, location, education, photo_url, follower_count, parse_confidence).

# 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/",
        "satyanadella"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

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

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

```

## MCP server setup

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