# LinkedIn Scraper: Profiles, Companies, Posts & Jobs (`data_forge_org/linkedin-scraper`) Actor

Scrape public LinkedIn profiles, company pages, posts and jobs from their URLs. No account, no cookies, no browser. Get names, experience, company size, industry, post text, reactions, comments, and job listings with descriptions and salaries. Export to JSON, CSV or Excel, or use the real-time API.

- **URL**: https://apify.com/data\_forge\_org/linkedin-scraper.md
- **Developed by:** [Data Forge](https://apify.com/data_forge_org) (community)
- **Categories:** Social media, Lead generation, Jobs
- **Stats:** 5 total users, 4 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $2.10 / 1,000 company page scrapeds

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
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

**This LinkedIn scraper extracts public LinkedIn profiles, company pages, posts, comments and job listings from their URLs**, without a LinkedIn account, without cookies and without a browser. Paste LinkedIn profile, company, post or job URLs, or a LinkedIn jobs search URL, and get clean LinkedIn data as JSON, CSV or Excel: names, headlines, work experience, education, company size, industry, headquarters, followers, post text, reactions, comments, job descriptions and salaries.

It works as a **LinkedIn profile scraper**, a **LinkedIn company scraper**, a **LinkedIn post scraper** and a **LinkedIn jobs scraper** in one Actor, and as a real-time **LinkedIn data API** with no login. Field names are flat and stable (`li_profile_url`, `li_company_url`, `linkedin_post_url`, `linkedin_job_url`), so results drop straight into a spreadsheet, CRM or database.

> This Actor is an independent tool. It is not affiliated with, endorsed by or sponsored by LinkedIn Corporation. "LinkedIn" is a trademark of LinkedIn Corporation and is used here only to describe which public web pages the Actor reads.

### What does this LinkedIn scraper do?

It reads the pages that LinkedIn serves to **logged-out visitors** and turns them into structured data:

- 👤 **LinkedIn profiles**: name, headline, About section, location, current and past positions, the work experience and education a member made public, and follower count.
- 🏢 **LinkedIn company pages**: the full About section (website, industry, company size, headquarters, founding year, type, specialties), office locations, follower count, logo, the employees featured on the page and similar companies.
- 📝 **LinkedIn posts**: text, author, publication date, post type, media, reaction and comment counts.
- 💬 **LinkedIn comments**: the first public comments of a post, with each commenter's name, headline and profile URL.
- 💼 **LinkedIn jobs**: job listings from any LinkedIn jobs search URL or from a company's open positions (title, company, location, date posted), and full job postings with description, employment type, seniority, industry, pay range when shown, applicants, Easy Apply and job poster.

**Try it now:** open the **Input** tab, keep the prefilled company and profile, and click **Start**. You get two results in a few seconds.

You provide the URLs: this Actor doesn't search for people, companies or posts. For jobs, paste a LinkedIn jobs search URL with your keywords, location and filters, and it returns the matching job listings. As an Apify Actor, it comes with API access, scheduling, monitoring, integrations (n8n, Make, Zapier, Google Sheets, webhooks) and export to JSON, CSV, Excel, XML or HTML.

**Fast and cheap to run.** Plain HTTP requests, no headless browser, and **one request per profile, company or post**. Recent posts, featured employees and comments come from the same page at no extra request. Job lists take one request per 10 jobs, plus one small request per job when you want its full details. See [how much it costs to scrape LinkedIn](#how-much-does-it-cost-to-scrape-linkedin).

### What can you use LinkedIn data for?

- 🎯 **Lead generation and CRM enrichment**: turn a list of LinkedIn profile and company URLs into complete person and company records for your CRM.
- 🔎 **Account research and market mapping**: collect company firmographics (industry, headcount, headquarters, office locations) and find similar companies.
- 📣 **Content and brand monitoring**: track what a company or a public figure posts on LinkedIn, and how many reactions and comments each post gets.
- 🧑‍💼 **Recruiting and talent research**: read the public work history and education of candidates you already found, and see which roles competitors are hiring for and how they describe them.
- 📊 **Job market intelligence**: collect the jobs of a LinkedIn search for a role or a region, then compare titles, seniority, employment types, pay ranges and applicant counts over time.
- 📈 **"Who's hiring" lead signals**: a company that opens new positions is growing or has a new need. Track the open jobs of your target accounts, or a daily search for the roles your product serves, and hand the hiring companies to your sales team.
- 🔁 **Pay per result**: replace per-seat or credit-based LinkedIn data tools and pay only for the results you get.
- 🤖 **AI agents and automations**: call the real-time LinkedIn API from n8n, Make, Zapier, LangChain or your own backend, and get JSON back from every call.

### What data can you extract from LinkedIn?

Every result has **provenance fields** plus the fields of **one** entity: a profile, a company, a post, a comment, a job, or an error. `entity_type` tells you which. Field names are flat snake\_case: `li_*` names for people and companies, and `linkedin_*` names for posts, comments and jobs.

**A field is omitted when the public page doesn't show it.** The Actor never returns `null` or `""` placeholders, and it never guesses or fills a value from another source. What you get for a person depends on that member's public-visibility settings: from a full CV down to a name and an About section.

#### Provenance fields in every result

| Field         | Description                                                                                                                                                                                                                                                   |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `entity_type` | `person`, `company`, `post`, `comment`, `job` or `error`                                                                                                                                                                                                      |
| `operation`   | `people.enrich`, `companies.enrich`, `posts.enrich`, `people.posts` / `companies.posts` for the recent posts shown on a profile or company page, `jobs.enrich` for a job URL, `jobs.search` for a jobs search URL, `companies.jobs` for a company's open jobs |
| `input`       | The value from your input (URL or handle) that led to this result                                                                                                                                                                                             |
| `source_url`  | The LinkedIn page actually downloaded (for posts fetched without comments, the lightweight `/embed/` page; for jobs, LinkedIn's public job page or list page). On `INVALID_INPUT` errors: the value as given                                                  |
| `scraped_at`  | ISO 8601 timestamp of the extraction                                                                                                                                                                                                                          |

#### LinkedIn profile data

`entity_type: "person"`.

| Field                                                        | Description                                                                                                                                                      |
| ------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `li_profile_id`                                              | Numeric LinkedIn member id                                                                                                                                       |
| `li_profile_url`, `li_profile_handle`                        | Canonical profile URL and public identifier                                                                                                                      |
| `full_name`, `first_name`, `last_name`                       | Name                                                                                                                                                             |
| `headline`                                                   | Text shown under the name                                                                                                                                        |
| `summary`                                                    | The **About** section                                                                                                                                            |
| `location`, `li_profile_country`                             | Displayed location and ISO-2 country code                                                                                                                        |
| `li_profile_image_url`                                       | Profile photo (signed URL that expires)                                                                                                                          |
| `job_title`, `company_name`, `li_company_url`                | Current position                                                                                                                                                 |
| `past_job_title`, `past_company_name`, `past_li_company_url` | Most recent past position                                                                                                                                        |
| `experiences[]`                                              | `title`, `company_name`, `li_company_url`, `location`, `date` (for example `"2000 - Present"`), `job_time_period` (for example `"26 years"`), `company_logo_url` |
| `has_more_experiences`                                       | `true` when the page says more positions exist                                                                                                                   |
| `education[]`, `school_name`, `li_school_url`                | `school_name`, `li_school_url`, `degree_name`, `date`                                                                                                            |
| `skills[]`                                                   | `{ "name": … }`, when the member made skills public                                                                                                              |
| `languages[]`, `volunteer_experiences[]`                     | When public                                                                                                                                                      |
| `li_number_followers`, `li_number_connections`               | Follower count, and the connection count as displayed (LinkedIn caps the display at 500+)                                                                        |

The Actor returns **every** experience, education and skill entry the public page shows, with no cap.

#### LinkedIn company data

`entity_type: "company"`.

| Field                                                               | Description                                                                                                                                                                              |
| ------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `company_name`, `li_company_url`, `li_company_id`                   | Name, canonical page URL, and numeric id when the page exposes it                                                                                                                        |
| `tagline`, `description`                                            | Slogan and About text                                                                                                                                                                    |
| `website`, `domain`                                                 | Website from the About section, and its hostname without `www.`                                                                                                                          |
| `industry`, `industries[]`, `specialties[]`                         | Industry and specialties                                                                                                                                                                 |
| `type`, `founded_on`                                                | For example `"Privately Held"`, and the founding year as a number                                                                                                                        |
| `employees_range`, `number_employees`                               | Company size bracket (`"201-500"`) and the employee count on LinkedIn                                                                                                                    |
| `headquarters`, `country`, `city`, `postal_code`, `geographic_area` | Headquarters                                                                                                                                                                             |
| `locations[]`, `number_of_locations`                                | Office locations: `address`, `street`, `city`, `geographic_area`, `postal_code`, `country`                                                                                               |
| `logo_url`                                                          | Logo (signed URL that expires)                                                                                                                                                           |
| `li_followers_count`                                                | Followers                                                                                                                                                                                |
| `li_employees_url`, `li_job_search_url`                             | Links to the LinkedIn search of the company's employees (opening it needs a login) and to its jobs page                                                                                  |
| `affiliates[]`                                                      | Affiliated and showcase pages shown on the company page                                                                                                                                  |
| `featured_employees[]`                                              | Employees featured on the page (up to 4 for logged-out visitors), as employee rows (`full_name`, `first_name`, `last_name`, `summary` = headline, `li_profile_url`, `profile_image_url`) |
| `similar_pages[]`                                                   | Similar companies: `{ company_name, li_company_url }`                                                                                                                                    |

#### LinkedIn post data

`entity_type: "post"`.

| Field                                                                  | Description                                                                                                                                                                                           |
| ---------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `linkedin_post_id`, `linkedin_post_url`                                | Numeric activity id, and the canonical `https://www.linkedin.com/feed/update/urn:li:activity:<id>` URL. Pulse articles have no `linkedin_post_id`, and their `linkedin_post_url` is the `/pulse/` URL |
| `published_date`                                                       | ISO 8601 date-time                                                                                                                                                                                    |
| `content_text`                                                         | Post text                                                                                                                                                                                             |
| `linkedin_post_type`                                                   | `text`, `image`, `video`, `document`, `article`, `poll`, `repost`, `job`, `event` or `other`                                                                                                          |
| `reaction_count`, `comment_count`                                      | Engagement counts, when shown (repost counts are not shown to logged-out visitors)                                                                                                                    |
| `hyperlinks[]`, `post_media_url[]`                                     | Links and media in the post                                                                                                                                                                           |
| `full_name`, `first_name`, `last_name`, `linkedin_profile_url`         | Author (member)                                                                                                                                                                                       |
| `linkedin_original_post`, `linkedin_job_url`                           | Original post of a repost, and shared job                                                                                                                                                             |
| `author_type`, `company_name`, `linkedin_company_url`, `article_title` | Extensions: author kind (`person` or `company`), author company page, and article title                                                                                                               |

#### LinkedIn comment data

`entity_type: "comment"`, with `comment_text`, `comment_time`, `comment_like_count`, `linkedin_comment_id`, `linkedin_comment_url`, `full_name`, `first_name`, `last_name`, `linkedin_profile_url`, `linkedin_profile_handle`, `linkedin_profile_picture`, `summary` (the commenter's headline), and `linkedin_post_url` / `linkedin_post_id` (the parent post). Each field is filled when shown.

#### LinkedIn job data

`entity_type: "job"`. A job from a job URL, or from a list with **Include job details** on, has every field its posting shows. A **job row** from a search or a company's open positions has the fields marked ✓ in the *Row* column.

| Field                                                                    | Row | Description                                                                                                         |
| ------------------------------------------------------------------------ | --- | ------------------------------------------------------------------------------------------------------------------- |
| `linkedin_job_id`, `linkedin_job_url`                                    | ✓   | Numeric job id and canonical `https://www.linkedin.com/jobs/view/<id>` URL                                          |
| `title`                                                                  | ✓   | Job title                                                                                                           |
| `company_name`, `linkedin_company_url`                                   | ✓   | Hiring company and its LinkedIn page                                                                                |
| `company_logo_url`                                                       | ✓   | Company logo (extension; signed URL that expires)                                                                   |
| `linkedin_company_id`                                                    |     | Numeric company id (string)                                                                                         |
| `job_location`                                                           | ✓   | Location as displayed, for example `"Paris, Île-de-France, France"`                                                 |
| `posted_at`                                                              | ✓   | Date the job was listed, `YYYY-MM-DD` (search results only, see the [FAQ](#why-do-job-urls-return-no-posting-date)) |
| `posted_time_ago`                                                        | ✓   | Age as displayed, for example `"2 weeks ago"` (extension)                                                           |
| `job_description`                                                        |     | Full description, with line breaks and `- ` list items                                                              |
| `type`, `field`                                                          |     | Employment type (`"Full-time"`) and industry                                                                        |
| `seniority_level`, `job_function`                                        |     | Seniority level and job function (extensions)                                                                       |
| `salary`, `salary_min`, `salary_max`, `salary_currency`, `salary_period` |     | Pay range as displayed, when the employer shows one, and parsed: `250000`, `380000`, `"USD"`, `"year"` (extensions) |
| `applicants`, `applicants_count`                                         |     | Applicants as displayed (`"Over 200 applicants"`, extension), and the number when LinkedIn shows an exact count     |
| `easy_apply`                                                             |     | `true` for LinkedIn Easy Apply, `false` when you apply on the company's site (extension)                            |
| `closed`                                                                 |     | `true` when the job no longer accepts applications (extension)                                                      |
| `profile_full_name`, `linkedin_profile_url`, `profile_headline`          |     | The person who posted the job, when LinkedIn shows one (about half of postings; headline is an extension)           |

**Not shown to logged-out visitors**, so never filled: `skills`, `remote_allowed` (the remote flag), `linkedin_job_application_url` (the external apply link), `reposted_at`, the poster's `linkedin_profile_id`, and `posted_at` on job pages, which only show an age such as "2 weeks ago".

#### Errors

An input that fails produces an **error result** (`entity_type: "error"`). The run doesn't crash, and the input is never silently dropped. The result has `error_label` and `message`:

| `error_label`    | Meaning                                                                                                      |
| ---------------- | ------------------------------------------------------------------------------------------------------------ |
| `INVALID_INPUT`  | The value isn't a usable LinkedIn URL, or the page type is out of scope (school, directory, job collections) |
| `NOT_FOUND`      | LinkedIn answered 404: the profile, company, post or job doesn't exist or was removed                        |
| `BLOCKED`        | LinkedIn refused the page on every retry with a fresh IP. Try again later                                    |
| `INTERNAL_ERROR` | Unexpected failure. Please report it in the Issues tab                                                       |

Errors are **never charged**.

### How to scrape LinkedIn profiles, companies and posts

1. Open the Actor's **Input** tab.
2. Paste LinkedIn profile, company and/or post URLs. For jobs, open the *Jobs* section (see [how to scrape LinkedIn jobs](#how-to-scrape-linkedin-jobs)).
3. Optionally turn on recent posts or comments in *Options*.
4. Click **Start**. Results appear in the **Output** tab while the run is going.
5. Download them as JSON, CSV, Excel, XML or HTML, send them to Google Sheets, or fetch them through the API.

You can combine profiles, companies, posts and jobs in one run. Each result says which `operation` and which `input` produced it.

#### How to scrape LinkedIn profiles

Paste profile URLs or public identifiers (the part after `/in/`):

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

#### How to scrape LinkedIn company pages and their posts

Paste company page URLs or universal names (the part after `/company/`). Turn on `includeCompanyPosts` to also get the recent posts shown on each company page:

```json
{ "companyUrls": ["https://www.linkedin.com/company/apify", "microsoft"], "includeCompanyPosts": true }
```

#### How to scrape LinkedIn posts and comments

Paste post URLs in any form (`/posts/…`, `/feed/update/…`, `/embed/…`, `/pulse/…`) or bare activity ids. Turn on `includeComments` to also get the first public comments:

```json
{
    "postUrls": ["https://www.linkedin.com/feed/update/urn:li:activity:7509678332976820224"],
    "includeComments": true
}
```

#### All input options

| Field                 | Type           | Default     | What it does                                                                                        |
| --------------------- | -------------- | ----------- | --------------------------------------------------------------------------------------------------- |
| `profileUrls`         | string\[]       | `[]`        | Profile URLs or public identifiers → `person`                                                       |
| `companyUrls`         | string\[]       | `[]`        | Company or showcase URLs, or universal names → `company`                                            |
| `postUrls`            | string\[]       | `[]`        | `/posts/…`, `/feed/update/urn:li:…`, `/embed/…`, `/pulse/…` URLs or bare activity ids → `post`      |
| `includeProfilePosts` | boolean        | `false`     | Also output the recent activity shown on profile pages (no extra request)                           |
| `includeCompanyPosts` | boolean        | `false`     | Also output the recent posts shown on company pages (no extra request)                              |
| `includeComments`     | boolean        | `false`     | Use the full post page and output its public comments. `false` = light embed page, about 7× smaller |
| `jobUrls`             | string\[]       | `[]`        | Job URLs (`/jobs/view/…`, any URL with `currentJobId=`) or bare job ids → `job` with details        |
| `jobSearchUrls`       | string\[]       | `[]`        | LinkedIn jobs search URLs or job listing pages → one `job` row per result                           |
| `includeCompanyJobs`  | boolean        | `false`     | Also output the open jobs of each company in `companyUrls`                                          |
| `includeJobDetails`   | boolean        | `false`     | Download each listed job's page for its full details (+1 small request per job)                     |
| `maxJobs`             | integer 1–1000 | `25`        | Max jobs per jobs search URL and per company. Job URLs are not limited                              |
| `proxyConfiguration`  | proxy          | Residential | Proxy for LinkedIn pages                                                                            |
| `maxConcurrency`      | integer 1–50   | `10`        | Parallel downloads                                                                                  |
| `maxRequestRetries`   | integer 0–10   | `6`         | Retries on a fresh IP after a block                                                                 |

### How to scrape LinkedIn jobs

The Actor reads the job pages that LinkedIn shows on [linkedin.com/jobs](https://www.linkedin.com/jobs) to logged-out visitors. There are three ways in:

- **Job URLs** (`jobUrls`): each job comes back with its full details.
- **Jobs search URLs** (`jobSearchUrls`): one row per job (title, company, location, date posted), up to `maxJobs` per search.
- **Company pages** (`companyUrls` + `includeCompanyJobs`): the company's open positions, as rows.

Rows take **one request per 10 jobs**. Turn on `includeJobDetails` to also get the full posting of every row, for one more small request per job. A job that several searches or companies of the same run find is returned once.

#### How to get a LinkedIn jobs search URL

1. Open [linkedin.com/jobs/search](https://www.linkedin.com/jobs/search). You don't need to log in.
2. Enter keywords and a location, and set the filters you want, such as date posted, company or Easy Apply.
3. Copy the URL from the address bar and paste it into **Jobs search URLs**.

```json
{ "jobSearchUrls": ["https://www.linkedin.com/jobs/search?keywords=Data%20Engineer&location=France"], "maxJobs": 100 }
```

A `/jobs/search` URL without a location covers the whole world, whatever the proxy's country. Job listing pages such as `https://www.linkedin.com/jobs/data-engineer-jobs` work too, but LinkedIn searches them in the location it picks for the page (for example the United States): to search another location, add `location=` to the URL (`…/jobs/data-engineer-jobs?location=France`) or use a `/jobs/search` URL. Only the search parameters of a URL are used (keywords, location, filters, distance and sort order); tracking and sign-in parameters, such as those of job-alert e-mails, are dropped. LinkedIn shows at most **1,000 results per search**: to get more, split the search by location, date posted or another filter.

#### How to scrape all open jobs of a company

Put the company page in **Company URLs** and turn on `includeCompanyJobs`. The jobs come from the *See jobs* button of the company page, which also covers the pages LinkedIn groups under the company (for Microsoft, its subsidiaries). A company with no open jobs has no such button: you get the company and no jobs, and no extra request is made.

```json
{ "companyUrls": ["https://www.linkedin.com/company/microsoft"], "includeCompanyJobs": true, "maxJobs": 50 }
```

The jobs have `operation: "companies.jobs"` and the company's input in `input`, so you can join them to the company.

#### How to get full job descriptions and salaries

Paste job URLs into **Job URLs**: `https://www.linkedin.com/jobs/view/<id>` with or without the title, any LinkedIn jobs URL with `currentJobId=<id>`, or bare job ids. For searches and companies, turn on `includeJobDetails`:

```json
{
    "jobSearchUrls": ["https://www.linkedin.com/jobs/search?keywords=Software%20Engineer&location=United%20States"],
    "includeJobDetails": true,
    "maxJobs": 25
}
```

Each job then takes one more request to LinkedIn's public job page (about 25–80 KB) and is charged as a detailed job instead of a row. The salary fields are filled only when the employer shows a pay range. A job that no longer accepts applications is still returned, with `closed: true`; a job id that doesn't exist gives a `NOT_FOUND` error.

#### How to monitor new LinkedIn jobs every day

1. On LinkedIn, set **Date posted** to *Past 24 hours*. This adds `f_TPR=r86400` to the search URL; you can also add it yourself.
2. Paste the URL into **Jobs search URLs**, set `maxJobs` to more than a day's worth of jobs, and save the input as a **Task**.
3. **Schedule** the task to run every day, and send the results to Google Sheets, Slack or a webhook with an integration.

```json
{
    "jobSearchUrls": ["https://www.linkedin.com/jobs/search?keywords=Data%20Engineer&location=France&f_TPR=r86400"],
    "maxJobs": 200
}
```

Logged out, LinkedIn lists results in its own order and ignores the sort option (`sortBy`), so the filter, not the order, is what keeps each run to new jobs. Two runs a day apart can share a few jobs: deduplicate on `linkedin_job_id`.

### How does it scrape LinkedIn without login?

1. **Public pages only.** The Actor downloads the same public pages that any logged-out visitor, or a search engine, sees: `/in/<id>`, `/company/<name>`, `/posts/…`, `/feed/update/…`, `/embed/feed/update/…`, `/pulse/…`, and the job pages and job search results that [linkedin.com/jobs](https://www.linkedin.com/jobs) loads for logged-out visitors. It never logs in, never uses cookies or accounts, never solves captchas and never pretends to be a search-engine crawler.
2. **HTTP only, no browser.** Each page is one HTTP request with a real-browser TLS fingerprint. The page is parsed from its structured data (JSON-LD) first and then from stable HTML attributes. A page takes about a second and only a few milliseconds of CPU.
3. **Residential proxy with automatic retry.** LinkedIn blocks datacenter IPs quickly, so each request goes through an Apify **Residential** IP. When LinkedIn answers with its sign-in wall, an HTTP 999 or a 429, the Actor retries on a fresh IP (up to `maxRequestRetries`). Blocked answers are small, so retries cost little.
4. **One request per profile, company or post.** Recent posts, featured employees, similar pages and comments come from the same page. Posts requested without comments are read from LinkedIn's 24 KB embed page instead of the ~170 KB post page; the exact publication time is decoded from the post id. Jobs are read from LinkedIn's lightweight job pages: one request per 10 jobs of a list, and one 25–80 KB request per job with details instead of the 260–340 KB full job page.
5. **Deduplication.** Inputs that point to the same page (for example `jbalada`, `JBalada` and `https://fr.linkedin.com/in/jbalada/`) are downloaded and charged once; the first input is echoed in `input`. A job found by several searches or companies of the same run is output and charged once.

### LinkedIn scraper output examples

The examples below are abbreviated. They were extracted from the public LinkedIn pages of Apify and Bill Gates on 2026-09-27, and from public LinkedIn job pages on 2026-09-28.

**All result types share one dataset.** Filter on `entity_type`. This is deliberate: `run-sync-get-dataset-items` and the Export button only return the default dataset, so one synchronous API call returns people, companies, posts, jobs and errors together. The Output tab views (*People*, *Companies*, *Posts*, *Comments*, *Jobs*, *Job details*, *Errors*) only choose columns. To filter results, use for example `jq '.[] | select(.entity_type == "company")'`.

#### LinkedIn company data example

```json
{
    "entity_type": "company",
    "operation": "companies.enrich",
    "input": "https://www.linkedin.com/company/apify",
    "source_url": "https://www.linkedin.com/company/apify",
    "scraped_at": "2026-09-27T10:00:00.000Z",
    "company_name": "Apify",
    "li_company_url": "https://www.linkedin.com/company/apify",
    "tagline": "Thousands of Actors to automate your business, get real-time web data, and integrate your apps and agents.",
    "description": "Apify is the largest marketplace of tools for AI. More than 50,000 Actors to automate your business. …",
    "website": "https://apify.com/",
    "domain": "apify.com",
    "industry": "Technology, Information and Internet",
    "industries": ["Technology, Information and Internet"],
    "type": "Privately Held",
    "founded_on": 2016,
    "employees_range": "201-500",
    "number_employees": 272,
    "headquarters": "Prague",
    "country": "CZ",
    "city": "Prague",
    "postal_code": "11100",
    "specialties": [
        "Web scraping",
        "Browser automation",
        "AI agents",
        "API integration",
        "Data pipelines",
        "No-code tools",
        "Actor marketplace",
        "Developer platform"
    ],
    "locations": [
        {
            "address": "Vodickova 704/36 Prague, 11100, CZ",
            "street": "Vodickova 704/36",
            "city": "Prague",
            "postal_code": "11100",
            "country": "CZ"
        },
        { "address": "San Francisco, CA, US" }
    ],
    "number_of_locations": 2,
    "li_followers_count": 38579,
    "logo_url": "https://media.licdn.com/dms/image/v2/D4D0BAQF7OnC-r1njIA/company-logo_200_200/…",
    "similar_pages": [
        { "company_name": "n8n", "li_company_url": "https://www.linkedin.com/company/n8n" },
        { "company_name": "Make", "li_company_url": "https://www.linkedin.com/company/itsmakehq" }
    ]
}
```

#### LinkedIn profile data example

```json
{
    "entity_type": "person",
    "operation": "people.enrich",
    "input": "https://www.linkedin.com/in/williamhgates",
    "source_url": "https://www.linkedin.com/in/williamhgates",
    "scraped_at": "2026-09-27T10:00:00.000Z",
    "li_profile_handle": "williamhgates",
    "li_profile_url": "https://www.linkedin.com/in/williamhgates",
    "full_name": "Bill Gates",
    "first_name": "Bill",
    "last_name": "Gates",
    "headline": "Chair, Gates Foundation and Founder, Breakthrough Energy",
    "summary": "Chair of the Gates Foundation. Founder of Breakthrough Energy. Co-founder of Microsoft. Voracious reader. Avid traveler. Active blogger.",
    "location": "Seattle, Washington, United States",
    "li_profile_country": "US",
    "li_profile_image_url": "https://media.licdn.com/dms/image/v2/D5603AQF-RYZP55jmXA/profile-displayphoto-shrink_200_200/…",
    "job_title": "Co-chair",
    "company_name": "Gates Foundation",
    "li_company_url": "https://www.linkedin.com/company/gates-foundation",
    "li_number_followers": 40683385,
    "experiences": [
        {
            "title": "Co-chair",
            "company_name": "Gates Foundation",
            "li_company_url": "https://www.linkedin.com/company/gates-foundation",
            "date": "2000 - Present",
            "job_time_period": "26 years"
        },
        {
            "title": "Founder",
            "company_name": "Breakthrough Energy",
            "li_company_url": "https://www.linkedin.com/company/breakthrough-energy",
            "date": "2015 - Present",
            "job_time_period": "11 years"
        },
        {
            "title": "Co-founder",
            "company_name": "Microsoft",
            "li_company_url": "https://www.linkedin.com/company/microsoft",
            "date": "1975 - Present",
            "job_time_period": "51 years"
        }
    ],
    "education": [
        {
            "school_name": "Harvard University",
            "li_school_url": "https://www.linkedin.com/school/harvard-university",
            "date": "1973 - 1975"
        },
        { "school_name": "Lakeside School" }
    ],
    "school_name": "Harvard University",
    "li_school_url": "https://www.linkedin.com/school/harvard-university"
}
```

#### LinkedIn post data example

```json
{
    "entity_type": "post",
    "operation": "posts.enrich",
    "input": "https://www.linkedin.com/feed/update/urn:li:activity:7509678332976820224",
    "source_url": "https://www.linkedin.com/embed/feed/update/urn:li:activity:7509678332976820224",
    "scraped_at": "2026-09-27T10:00:00.000Z",
    "linkedin_post_id": "7509678332976820224",
    "linkedin_post_url": "https://www.linkedin.com/feed/update/urn:li:activity:7509678332976820224",
    "published_date": "2026-09-26T18:20:37.656Z",
    "content_text": "Hannah and her team at Our World in Data do an incredible job of putting the world’s progress into perspective—and showing just how much has changed for the better, even as we face real challenges. Worth the watch.",
    "linkedin_post_type": "video",
    "reaction_count": 452,
    "comment_count": 65,
    "full_name": "Bill Gates",
    "first_name": "Bill",
    "last_name": "Gates",
    "linkedin_profile_url": "https://www.linkedin.com/in/williamhgates",
    "author_type": "person"
}
```

#### LinkedIn comment data example

The commenter is anonymised in this example.

```json
{
    "entity_type": "comment",
    "operation": "posts.enrich",
    "input": "https://www.linkedin.com/feed/update/urn:li:activity:7509678332976820224",
    "source_url": "https://www.linkedin.com/feed/update/urn:li:activity:7509678332976820224",
    "scraped_at": "2026-09-27T10:00:00.000Z",
    "linkedin_post_id": "7509678332976820224",
    "linkedin_post_url": "https://www.linkedin.com/feed/update/urn:li:activity:7509678332976820224",
    "full_name": "Jane Doe",
    "comment_text": "…",
    "comment_time": "2026-09-26T19:08:43.824Z",
    "comment_like_count": 7
}
```

#### LinkedIn job search row example

One row of a jobs search (`jobSearchUrls`, details off):

```json
{
    "entity_type": "job",
    "operation": "jobs.search",
    "input": "https://www.linkedin.com/jobs/search?keywords=data%20engineer&location=France",
    "source_url": "https://www.linkedin.com/jobs-guest/jobs/api/seeMoreJobPostings/search?keywords=data+engineer&location=France&start=0",
    "scraped_at": "2026-09-28T10:00:00.000Z",
    "linkedin_job_id": 4446387035,
    "linkedin_job_url": "https://www.linkedin.com/jobs/view/4446387035",
    "title": "Senior Data Engineer - BeReal",
    "company_name": "BeReal.",
    "linkedin_company_url": "https://www.linkedin.com/company/bereal-app",
    "job_location": "Paris, Île-de-France, France",
    "posted_at": "2026-09-10",
    "posted_time_ago": "2 weeks ago",
    "company_logo_url": "https://media.licdn.com/dms/image/v2/C4D0BAQG16tpcM1rf-A/company-logo_100_100/…"
}
```

#### LinkedIn job details example

A job URL (`jobUrls`). A search job with `includeJobDetails` has the same fields, plus `posted_at` from its row:

```json
{
    "entity_type": "job",
    "operation": "jobs.enrich",
    "input": "https://www.linkedin.com/jobs/view/4446500774",
    "source_url": "https://www.linkedin.com/jobs-guest/jobs/api/jobPosting/4446500774",
    "scraped_at": "2026-09-28T10:00:00.000Z",
    "linkedin_job_id": 4446500774,
    "linkedin_job_url": "https://www.linkedin.com/jobs/view/4446500774",
    "title": "Principal Software Engineer",
    "company_name": "Snowflake",
    "linkedin_company_url": "https://www.linkedin.com/company/snowflake-computing",
    "linkedin_company_id": "3653845",
    "job_location": "Bellevue, WA",
    "job_description": "At Snowflake, we are powering the era of the agentic enterprise. …\n\nWhat You Will Do\n\nAs a software engineer on the team, you will play a central role in delivering the next generation of tools and evolve our developer infrastructure and tooling to be scalable, highly performant, and AI-first.\n\n- Design and build AI-first platforms, tools, and best practices to significantly enhance the developer experience for Snowsight\n- …",
    "type": "Full-time",
    "field": "Software Development",
    "applicants": "118 applicants",
    "applicants_count": 118,
    "seniority_level": "Not Applicable",
    "job_function": "Engineering and Information Technology",
    "posted_time_ago": "2 weeks ago",
    "salary": "$250,000.00/yr - $380,000.00/yr",
    "salary_min": 250000,
    "salary_max": 380000,
    "salary_currency": "USD",
    "salary_period": "year",
    "easy_apply": false,
    "closed": false,
    "company_logo_url": "https://media.licdn.com/dms/image/v2/D560BAQF3mYS3WqCB3g/company-logo_100_100/…"
}
```

When LinkedIn shows the person who posted the job, the item also has `profile_full_name`, `linkedin_profile_url` and `profile_headline`.

#### Error example

```json
{
    "entity_type": "error",
    "operation": "people.enrich",
    "input": "https://www.linkedin.com/in/this-profile-does-not-exist",
    "source_url": "https://www.linkedin.com/in/this-profile-does-not-exist",
    "scraped_at": "2026-09-27T10:00:00.000Z",
    "error_label": "NOT_FOUND",
    "message": "LinkedIn answered HTTP 404."
}
```

### LinkedIn API alternative: call it from your code

LinkedIn's official APIs are reserved for approved partners and don't return public profile or company data for any URL you choose. This Actor gives you that data through the Apify API, in three ways. All of them authenticate with your Apify API token (`Authorization: Bearer <APIFY_TOKEN>`). In the examples, replace `<username>` with the username of the account that owns the Actor.

#### Synchronous API call: results in the response

Use this for small jobs, for example tens of URLs, when the run finishes in under 5 minutes. The response body is the list of results. Pass `maxTotalChargeUsd` to cap what a run can cost you.

```bash
curl -X POST "https://api.apify.com/v2/actors/<username>~linkedin-scraper/run-sync-get-dataset-items?maxTotalChargeUsd=0.50&clean=true" \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"companyUrls": ["https://www.linkedin.com/company/apify"], "profileUrls": ["williamhgates"]}'
```

Add `&format=csv` or `&format=xlsx` to get a CSV or Excel file instead of JSON. If a run takes longer than 300 seconds, the API answers **408**. For larger jobs, use an asynchronous run.

#### Asynchronous runs for bulk LinkedIn scraping

```bash
## 1) Start the run (returns data.id and data.defaultDatasetId)
curl -X POST "https://api.apify.com/v2/actors/<username>~linkedin-scraper/runs?maxTotalChargeUsd=5" \
  -H "Authorization: Bearer $APIFY_TOKEN" -H "Content-Type: application/json" \
  -d @input.json

## 2) Wait for it (repeat until status is SUCCEEDED), or register a webhook on ACTOR.RUN.SUCCEEDED
curl "https://api.apify.com/v2/actor-runs/<RUN_ID>?waitForFinish=60" -H "Authorization: Bearer $APIFY_TOKEN"

## 3) Download the results
curl "https://api.apify.com/v2/datasets/<DATASET_ID>/items?clean=true&format=json" -H "Authorization: Bearer $APIFY_TOKEN"
```

#### Scrape LinkedIn with Python

```python
from apify_client import ApifyClient

client = ApifyClient("<APIFY_TOKEN>")
run = client.actor("<username>/linkedin-scraper").call(
    run_input={"profileUrls": ["https://www.linkedin.com/in/williamhgates"], "companyUrls": ["apify"]}
)
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["entity_type"], item.get("full_name") or item.get("title") or item.get("company_name"))
```

#### Scrape LinkedIn with JavaScript and Node.js

```js
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('<username>/linkedin-scraper').call({ profileUrls: ['williamhgates'] });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

#### Real-time LinkedIn API (Standby endpoints)

The Actor also runs as a web server (Apify **Standby** mode) at `https://<username>--linkedin-scraper.apify.actor`. Each endpoint takes one LinkedIn URL and returns JSON: a profile, company, post or job in about a second, and a list of jobs a little later (one request per 10 jobs). Errors come back as a JSON body with `error_label`, `message` and `status_code`. Status codes: 400 (bad or out-of-scope URL), 402 (`LIMIT_REACHED`: the run's maximum cost was reached), 404 (unknown endpoint or LinkedIn 404), 405 (not a GET), 502 (`BLOCKED`), 500, and 504 (`TIMEOUT`: no answer within 240 s). A call is charged only after its answer has been sent. If you disconnect first, the Actor stops waiting, starts no further attempt and charges nothing. The **Endpoints** tab has an interactive OpenAPI console.

```bash
curl -G "https://<username>--linkedin-scraper.apify.actor/v1/people/enrich" \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  --data-urlencode "li_profile_url=https://www.linkedin.com/in/williamhgates"

curl -G "https://<username>--linkedin-scraper.apify.actor/v1/companies/enrich" \
  -H "Authorization: Bearer $APIFY_TOKEN" --data-urlencode "li_company_url=https://www.linkedin.com/company/apify"

curl -G "https://<username>--linkedin-scraper.apify.actor/v1/posts/enrich" \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  --data-urlencode "li_post_url=https://www.linkedin.com/feed/update/urn:li:activity:7509678332976820224" \
  --data-urlencode "include_comments=true"

curl -G "https://<username>--linkedin-scraper.apify.actor/v1/jobs/enrich" \
  -H "Authorization: Bearer $APIFY_TOKEN" --data-urlencode "li_job_url=https://www.linkedin.com/jobs/view/4446500774"

curl -G "https://<username>--linkedin-scraper.apify.actor/v1/jobs/search" \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  --data-urlencode "li_job_search_url=https://www.linkedin.com/jobs/search?keywords=Data%20Engineer&location=France" \
  --data-urlencode "page=1" --data-urlencode "page_size=50"

curl -G "https://<username>--linkedin-scraper.apify.actor/v1/companies/enrich" \
  -H "Authorization: Bearer $APIFY_TOKEN" --data-urlencode "li_company_url=https://www.linkedin.com/company/apify" \
  --data-urlencode "include_jobs=true" --data-urlencode "max_jobs=25"
```

| Endpoint                   | Parameters                                                                                                                                                                   | Returns                     |
| -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------- |
| `GET /v1/people/enrich`    | `li_profile_url` (required), `include_posts`                                                                                                                                 | Person (+ `posts`)          |
| `GET /v1/companies/enrich` | `li_company_url` (required), `include_posts`, `include_jobs`, `max_jobs` (1–100, default 25), `include_job_details`                                                          | Company (+ `posts`, `jobs`) |
| `GET /v1/posts/enrich`     | `li_post_url` (required), `include_comments`                                                                                                                                 | Post (+ `comments`)         |
| `GET /v1/jobs/enrich`      | `li_job_url` (required; or `linkedin_job_url`)                                                                                                                               | Job with details            |
| `GET /v1/jobs/search`      | `li_job_search_url` (required; or `linkedin_job_search_url`), `page` (from 1), `page_size` (10–100 in steps of 10, default 50), `max_results` (1–100), `include_job_details` | Array of jobs               |

`/v1/jobs/search` is paged: request `page=1`, `2`, … until you get `[]`, which means past the last result or past LinkedIn's limit of 1,000 results per search. To get just the first jobs in one call, pass `max_results=n` (any number from 1 to 100) instead of `page_size`. With `include_jobs=true`, a company without open jobs gets `"jobs": []`.

Standby runs start on demand and stay warm between calls. A profile, company, post or job call is a single direct page download (a live test answered a post-with-comments call in 0.5 s). Job lists fetch up to 5 pages at a time within the 240 s budget. When a result is partial, on any endpoint (job-list pages blocked or failing on every retry, out of time, or posts, comments or jobs left out because the run's maximum cost was reached), the call still answers 200 with what it has and sets the headers `X-Results-Incomplete: true` and `X-Results-Incomplete-Reason` (`blocked`, `error`, `timeout` or `budget`). The maximum cost is checked before anything is downloaded: a call the run can't pay for answers 402 without any request to LinkedIn, and a job list stops at the jobs the run can still pay for. The first call after an idle period also waits a few seconds for a run to start. `GET /` lists the endpoints. There are no credits or plan quotas: the only limits are Apify's (2,000 requests/s per account for Standby) and the run's maximum cost.

### How does it compare with other ways to get LinkedIn data?

|                                        | This LinkedIn scraper               | LinkedIn official API | Cookie-based scrapers    |
| -------------------------------------- | ----------------------------------- | --------------------- | ------------------------ |
| Needs a LinkedIn account or cookies    | **No**                              | Partner approval      | Yes, your session cookie |
| Risk to your LinkedIn account          | **None**                            | None                  | Restriction or ban       |
| Any public profile, company or job URL | **Yes**                             | No                    | Yes                      |
| People search, employee lists, emails  | No                                  | Limited               | Often                    |
| Pricing                                | **Pay per result, no subscription** | Partner agreement     | Varies                   |
| Real-time API                          | Yes (Standby)                       | Yes                   | Rarely                   |

Choose this Actor when you already have the LinkedIn URLs, or a LinkedIn jobs search, and want their public data cheaply and without risking an account.

### What this LinkedIn scraper can't do

It only sees what logged-out visitors see:

- **No people, company or content search.** You provide the URLs; the Actor doesn't search LinkedIn or search engines for them. LinkedIn jobs search URLs that you paste are supported.
- **Jobs: at most 1,000 results per search**, in LinkedIn's order. Job pages show no skills, remote flag, application link or posting date to logged-out visitors (search results carry the date).
- **No full employee lists.** You get the employees featured on the company page (up to 4) and `li_employees_url`, the LinkedIn search that lists them, which needs a login to open.
- **Only the first public comments** (usually about 10 per post) and **no lists of reactions**: you get the reaction count only.
- **Short post history.** A company page shows about 10 recent posts, and a profile only its few most recent activity items. Older posts aren't shown to logged-out visitors.
- **Hidden profile sections stay hidden.** Members choose what logged-out visitors see. Skills, languages and certifications are often private. The Actor never fills hidden fields from other sources.
- **No emails or phone numbers.** LinkedIn doesn't show contact details to logged-out visitors.
- **Out of scope:** school pages (`/school/…`), the public directory (`/pub/dir/…`), activity feeds (`/in/…/recent-activity/…`, which is read as the profile itself) and job collections (`/jobs/collections/…`). LinkedIn blocks these for logged-out visitors anyway.
- **Image URLs expire.** LinkedIn image URLs are signed. Download photos or logos promptly if you need to keep them.
- **Typical success rates** after automatic retries (estimates based on public reports, not guarantees) are roughly **85–95 % for profiles**, which are the most protected pages, and higher for company pages and posts. Failures are reported as `BLOCKED` errors, never as empty results, and are not charged. Re-running the failed inputs later usually succeeds.

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

You pay **per result**, with no subscription. Failed and blocked pages are free.

| Result                                                | Event      | Price per 1,000 results |
| ----------------------------------------------------- | ---------- | ----------------------- |
| LinkedIn profile                                      | `person`   | **$3.50**               |
| LinkedIn company page                                 | `company`  | **$3.00**               |
| LinkedIn post                                         | `post`     | **$1.50**               |
| LinkedIn comment                                      | `comment`  | **$0.50**               |
| LinkedIn job with details                             | `job`      | **$1.00**               |
| LinkedIn job row (search or company list, no details) | `job_card` | **$0.30**               |

Bronze, Silver and Gold subscribers pay less: see the **Pricing** tab. Always pass `maxTotalChargeUsd` on API calls to cap your spend; the run stops cleanly when the cap is reached.

**LinkedIn jobs:** a full job posting (description, seniority, employment type, pay range when shown, applicants, job poster, Easy Apply or external, closed flag) costs **$1.00 per 1,000**, down to $0.70 on Gold. Rows from a job search or a company's open positions, without the description, cost **$0.30 per 1,000** (down to $0.21). Each job is charged once: as a detailed job or as a row, never both. A row that is saved because its details couldn't be fetched costs the row price.

**Example:** 1,000 company pages cost **$3.00**. The same 1,000 companies with their recent posts (about 10 each) cost $3.00 + 10,000 × $0.0015 = **$18.00**. A jobs search of 1,000 results costs **$0.30** as rows, or **$1.00** with full details.

#### Is scraping LinkedIn free?

Apify's free plan includes **$5 of credit every month**, which covers about **1,400 LinkedIn profiles, 1,600 company pages, 4,900 full job postings or 16,000 job search rows a month** with this Actor, at no cost.

#### Why it's cheap to run

| Resource                   | Usage                                                                                                                                     |
| -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| Memory                     | 512 MB by default (HTTP only, no browser). The allowed range is 256–1,024 MB                                                              |
| Time                       | About 0.7–1 second per page; 10 pages in parallel by default                                                                              |
| Data per page (compressed) | Profile ≈ 120–170 KB, company ≈ 20–100 KB, full post ≈ 20–35 KB, embed post ≈ 5 KB, job list page (10 jobs) ≈ 3 KB, job details ≈ 6–10 KB |
| Storage                    | One dataset write per result. No screenshots, no HTML snapshots and no key-value store writes per result                                  |

#### Tips to make runs faster and cheaper

1. **Leave `includeComments` off** unless you need comments. Posts are then read from the embed page, which is about 7× smaller.
2. **Leave `includeProfilePosts` / `includeCompanyPosts` off** unless you need posts: they add no request but add charged results.
3. **Batch your inputs.** One run with 500 URLs is much cheaper than 500 runs with one URL, because every run has a start-up cost. For one-at-a-time lookups from an app, use the **Standby** API.
4. **Memory:** 512 MB is the safe default for batches of profiles and companies at 10 pages in parallel. Small runs with few parallel pages, or posts only, work in 256 MB. For very large batches, 1,024 MB can finish up to 2× faster for about the same compute units, because Apify allocates CPU in proportion to memory.
5. **Leave `includeJobDetails` off** when title, company, location and date are enough: rows take one request per 10 jobs and cost less than a third of a detailed job. Set `maxJobs` to what you need.
6. **Standby idle time costs compute units.** A warm 512 MB run uses 0.5 CU per hour while it waits. Most Standby calls handle one page (job lists at most 5 small pages at a time), so 256 MB (0.25 CU per hour) is enough for light traffic. For sporadic traffic, create a Task, open its Endpoints tab and set a short **idle timeout**, for example 60 s.

### Is it legal to scrape LinkedIn?

*This section is general information, not legal advice. Consult your own counsel.*

**What the Actor does and doesn't do.** It reads only pages that LinkedIn serves to logged-out visitors. It uses no login data: no account, no cookies, no fake profiles, no account pools. It solves no captchas, doesn't impersonate search-engine crawlers and doesn't bypass visibility settings. When a member hides a section from the public, the Actor doesn't return it. Every result records where and when it was collected (`source_url`, `scraped_at`, `input`).

**LinkedIn's terms.** LinkedIn's User Agreement prohibits scraping, and LinkedIn actively enforces it, mostly against operators that use fake or logged-in accounts (hiQ Labs, Proxycurl, ProAPIs). US courts have found that scraping *public* pages while logged out is unlikely to violate the CFAA (hiQ v. LinkedIn, 9th Cir. 2019/2022), and that a platform's terms did not bar logged-off scraping of public data (Meta v. Bright Data, N.D. Cal. 2024). Guest-only collection is the lowest-risk approach, but it does not remove the contractual and commercial risk. You use the Actor at your own risk.

#### GDPR: you are the data controller

Profiles, employees, posts and comments contain personal data. So can job details: about half of the postings show the name, profile URL and headline of the person who posted the job. Job rows from a search or a company carry no personal data. When you run this Actor, **you** decide why and how that data is processed, so the GDPR obligations are yours:

- **Lawful basis (Art. 6).** Usually legitimate interest, supported by a documented legitimate-interest assessment. Collect only what your purpose needs: for example, leave comments, profile posts and job details off if you don't need them. Don't collect special-category data (health, religion, politics, trade-union membership) from free text.
- **Inform people (Art. 14).** When you collect personal data indirectly, you must tell the people concerned within a reasonable period, at the latest within one month or at your first contact with them. Tell them what you hold, the source (their public LinkedIn page, with `source_url`), your purpose and their rights, in their language.
- **Right to object and opt-out.** Keep a suppression list, and remove people who object from your inputs **before** future runs. Answer access requests (Art. 15) with the actual source and date. Every result carries them.
- **Retention.** Data in a run's default dataset is deleted automatically after your Apify plan's data-retention period. Named datasets and exports are kept until you delete them. Set your own limit, for example the French CNIL's 3-year reference for B2B prospects, and don't "refresh" old records just to extend their life.
- **B2B prospecting** by e-mail in France can rely on legitimate interest with a simple opt-out, provided the message relates to the person's job. This Actor doesn't collect e-mail addresses.

**CNIL v. KASPR (December 2024, €240,000).** The French regulator fined a tool that harvested LinkedIn contact details. The breaches were: collecting data of members who had restricted its visibility, retaining it for too long, informing people years late (and only in English), and answering access requests vaguely. This Actor is designed to avoid the first point (public data only, visibility respected) and to help with the others (provenance on every result). The remaining obligations are yours as the controller.

### FAQ

#### Can I scrape LinkedIn without an account or cookies?

Yes. This LinkedIn scraper reads only the pages LinkedIn shows to logged-out visitors. It never logs in and never asks for credentials or cookies.

#### Is my LinkedIn account at risk?

No. The Actor never uses your LinkedIn account, so it can't be restricted or banned because of it.

#### Can I scrape LinkedIn jobs without logging in?

Yes. LinkedIn shows job postings and job search results to logged-out visitors on [linkedin.com/jobs](https://www.linkedin.com/jobs), and the Actor reads them the same way: no account, no cookies. Paste job URLs, LinkedIn jobs search URLs or company pages. See [how to scrape LinkedIn jobs](#how-to-scrape-linkedin-jobs).

#### How many jobs can I get from one LinkedIn search?

Up to 1,000. LinkedIn shows at most 1,000 results per jobs search, so `maxJobs` goes up to 1,000 (default 25). To collect more, split the search into several URLs, for example by location or by date posted.

#### Why do job URLs return no posting date?

Logged out, LinkedIn's job page shows only an age such as "2 weeks ago", which you get in `posted_time_ago`. The date appears only in search results: job rows have `posted_at` (`YYYY-MM-DD`), and so do jobs from a search with `includeJobDetails` on, which keep it from their row.

#### Does it return the apply link of a job?

No. LinkedIn shows the external application link only to logged-in members, so `linkedin_job_application_url` is never filled. `easy_apply` tells you whether the job uses LinkedIn Easy Apply (`true`) or the company's own site (`false`), and `linkedin_job_url` opens the job on LinkedIn.

#### How do I get new LinkedIn jobs every day?

Add `f_TPR=r86400` (*Date posted: Past 24 hours*) to a LinkedIn jobs search URL, save it in a Task and schedule the Task daily. See [how to monitor new LinkedIn jobs every day](#how-to-monitor-new-linkedin-jobs-every-day).

#### Can I scrape all employees of a company?

No. LinkedIn lists a company's employees only to logged-in members. For each company you get the employees featured on its public page (up to 4) and `li_employees_url`, the LinkedIn search that lists them. If you already have employee profile URLs, add them to **Profile URLs** to get each full profile.

#### Does it find emails or phone numbers?

No. It returns what the public LinkedIn page shows, and LinkedIn doesn't show contact details to logged-out visitors.

#### How many LinkedIn profiles can I scrape for free?

About 1,400 profiles a month with the $5 of monthly credit included in Apify's free plan, or about 4,900 full job postings or 16,000 job search rows. See [pricing](#how-much-does-it-cost-to-scrape-linkedin).

#### Why are some profile fields missing?

The member hid those sections from logged-out visitors, or LinkedIn doesn't show them to guests. The Actor omits unknown fields instead of returning `null`.

#### Why did some results come back as BLOCKED?

LinkedIn refused the page on every retry from a fresh residential IP. Make sure the proxy is set to **Residential**, then re-run the failed inputs later. Blocked results are never charged, and raising `maxRequestRetries` can help on bad days.

#### How fast is this LinkedIn scraper?

About 0.7–1 second per page, 10 pages in parallel by default, and about half a second for a single Standby API lookup on a warm run.

#### Can I export LinkedIn data to Excel, CSV or Google Sheets?

Yes. Download results from the **Output** tab as Excel, CSV, JSON, XML or HTML, use `&format=xlsx` or `&format=csv` on the API, or send them to Google Sheets with Apify's integration.

#### Does it work with n8n, Make, Zapier or AI agents?

Yes. Use Apify's integrations for n8n, Make and Zapier, the Apify API clients for Python and JavaScript, or call the Standby endpoints directly over HTTP from any agent or backend.

#### Why is everything in one dataset?

So that one synchronous API call returns everything. Filter by `entity_type`.

#### Can I scrape LinkedIn school pages or search results?

School pages, the people directory and LinkedIn's people, company and content search are out of scope, and LinkedIn blocks them for logged-out visitors. LinkedIn **jobs** search results are supported: paste the search URL (see [how to scrape LinkedIn jobs](#how-to-scrape-linkedin-jobs)).

#### Something is broken or missing. Where do I report it?

Open an issue in the **Issues** tab and include the run URL and the input that failed.

# Changelog

This Actor's version history is a separate document: https://apify.com/data\_forge\_org/linkedin-scraper/changelog.md

# Actor input Schema

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

Add LinkedIn profile URLs (<code>https://www.linkedin.com/in/\<id></code>) or bare public identifiers such as <code>williamhgates</code>. Country subdomains and tracking parameters are cleaned up automatically. Each profile produces one <code>person</code> item. A value that can't be read produces an <code>INVALID\_INPUT</code> error item and the rest of the run continues.

## `companyUrls` (type: `array`):

Add LinkedIn company page URLs (<code>https://www.linkedin.com/company/\<name></code>) or bare universal names such as <code>apify</code>. Each page produces one <code>company</code> item. Numeric company IDs can't be resolved without a login.

## `postUrls` (type: `array`):

Add LinkedIn post URLs in any form: <code>/posts/…-activity-\<id>-…</code>, <code>/feed/update/urn:li:activity:\<id></code>, <code>/embed/feed/update/…</code> or <code>/pulse/\<article></code>. Each URL produces one <code>post</code> item. To also get comments, turn on <i>Include comments</i>.

## `includeProfilePosts` (type: `boolean`):

Also output the recent activity shown on each profile page as <code>post</code> items (operation <code>people.posts</code>). No extra page download is needed. Only the few posts that LinkedIn shows to logged-out visitors are available.

## `includeCompanyPosts` (type: `boolean`):

Also output the recent updates shown on each company page (usually about 10) as <code>post</code> items (operation <code>companies.posts</code>). No extra page download is needed.

## `includeComments` (type: `boolean`):

When this is on, the full post page is downloaded and the first comments that LinkedIn shows publicly (usually about 10) are output as <code>comment</code> items. When it is off, the light embed page is used instead. It is about 7× smaller, so it is faster and cheaper, but it has no comments.

## `jobUrls` (type: `array`):

Add LinkedIn job URLs (<code>https://www.linkedin.com/jobs/view/\<id></code>, with or without the title in the URL, or any LinkedIn jobs URL with <code>currentJobId=\<id></code>) or bare job IDs such as <code>4446500774</code>. Each job produces one <code>job</code> item with its details: description, employment type, seniority, industry, salary (when shown), applicants, Easy Apply and whether it is still open. It takes 1 small request per job and is charged as event <code>job</code>. An expired job is still returned, with <code>closed: true</code>; a job ID that doesn't exist produces a <code>NOT\_FOUND</code> error item.

## `jobSearchUrls` (type: `array`):

Paste LinkedIn jobs search URLs. To get one, search on <a href="https://www.linkedin.com/jobs/search" target="_blank">linkedin.com/jobs</a>, set the keywords, location and filters you want (for example date posted, company or Easy Apply), then copy the URL from the address bar. Every filter in the URL (keywords, location, <code>f\_…</code> filters, distance, sort) is passed on to LinkedIn as it is; tracking and sign-in parameters are dropped. Job listing pages such as <code>https://www.linkedin.com/jobs/data-engineer-jobs</code> and the <i>See jobs</i> link of a company page work too. Each URL returns up to <i>Max jobs per search or company</i> jobs, in LinkedIn's order; LinkedIn shows at most 1,000 results per search. A <code>/jobs/search</code> URL without a location covers the whole world. A job listing page is searched in the location LinkedIn picks for it (for example the United States): to search another location, add <code>location=…</code> to it or use a <code>/jobs/search</code> URL. Each job is a <code>job</code> item: rows cost 1 request per 10 jobs (event <code>job\_card</code>); with <i>Include job details</i>, each job takes 1 more small request and is charged as event <code>job</code> instead.

## `includeCompanyJobs` (type: `boolean`):

Also output the open jobs of each company in <i>Company URLs</i> as <code>job</code> items (operation <code>companies.jobs</code>), up to <i>Max jobs per search or company</i>. They come from the <i>See jobs</i> button of the company page, which also covers the pages LinkedIn groups under the company. A company with no open jobs has no such button, so it returns no jobs and no extra request is made. Rows cost 1 request per 10 jobs (event <code>job\_card</code>); with <i>Include job details</i>, each job takes 1 more small request and is charged as event <code>job</code> instead.

## `includeJobDetails` (type: `boolean`):

For jobs found through search URLs and company pages, also download each job's page to get the same details as a job URL: description, employment type, seniority, industry, salary (when shown), applicants, Easy Apply, whether it is still open, and the name and profile of the person who posted it (when shown). Each job takes 1 more small request (about 25–80 KB) and is charged as event <code>job</code> instead of <code>job\_card</code>. Job URLs always come with details.

## `maxJobs` (type: `integer`):

Set the maximum number of jobs returned for each jobs search URL and for each company. Jobs are listed 10 per request, so 25 jobs take 3 requests (plus 1 per job with <i>Include job details</i>). LinkedIn shows at most 1,000 results per search. Job URLs are not limited.

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

Select the proxies used for LinkedIn pages. LinkedIn blocks datacenter IPs almost immediately, so keep <b>Residential</b> proxies.

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

Set the maximum number of pages downloaded in parallel. Each page in flight takes a few MB (plain HTTP, no browser), so 10 fits in the default 512 MB. Higher values finish large runs faster but need more memory.

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

Set how many times a blocked or failed page is retried on a fresh residential IP. When all retries fail, a <code>BLOCKED</code> error item is written. Blocked responses are tiny (about 1.5 KB), so retries cost little.

## Actor input object example

```json
{
  "profileUrls": [
    "https://www.linkedin.com/in/williamhgates",
    "satyanadella"
  ],
  "companyUrls": [
    "https://www.linkedin.com/company/apify",
    "microsoft"
  ],
  "postUrls": [
    "https://www.linkedin.com/feed/update/urn:li:activity:7509678332976820224"
  ],
  "includeProfilePosts": false,
  "includeCompanyPosts": false,
  "includeComments": false,
  "jobUrls": [
    "https://www.linkedin.com/jobs/view/4446500774"
  ],
  "jobSearchUrls": [
    "https://www.linkedin.com/jobs/search?keywords=Data%20Engineer&location=France"
  ],
  "includeCompanyJobs": false,
  "includeJobDetails": false,
  "maxJobs": 25,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  },
  "maxConcurrency": 10,
  "maxRequestRetries": 6
}
```

# Actor output Schema

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

Every dataset item (all entity types) with flat snake\_case field names plus provenance fields (entity\_type, operation, input, source\_url, scraped\_at).

## `people` (type: `string`):

Dataset items shown with person columns (entity\_type person).

## `companies` (type: `string`):

Dataset items shown with company columns (entity\_type company).

## `posts` (type: `string`):

Dataset items shown with post columns (entity\_type post).

## `comments` (type: `string`):

Dataset items shown with comment columns (entity\_type comment).

## `jobs` (type: `string`):

Dataset items shown with job columns (entity\_type job): job URLs, LinkedIn jobs searches and company open jobs.

## `job_details` (type: `string`):

Dataset items shown with detailed job columns (entity\_type job): description, employment type, seniority, pay range, applicants and job poster.

## `errors` (type: `string`):

Dataset items shown with error columns (entity\_type error): inputs that were invalid, not found or blocked, or failed.

# 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"
    ],
    "companyUrls": [
        "https://www.linkedin.com/company/apify"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

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

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

```

## MCP server setup

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