# LinkedIn Profile Posts Scraper (`lightmoon/linkedin-profile-posts-scraper`) Actor

Posts from any LinkedIn person's public profile: text, exact publication time, reactions, comment counts, media and repost source. No login, no cookie. About a dozen recent posts per person, date and reaction filters so a repeat run only pays for what is new. Comment text optional.

- **URL**: https://apify.com/lightmoon/linkedin-profile-posts-scraper.md
- **Developed by:** [Stable](https://apify.com/lightmoon) (community)
- **Categories:** Social media, Lead generation, Marketing
- **Stats:** 2 total users, 1 monthly users, 66.7% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.35 / 1,000 posts

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

Paste LinkedIn profile addresses — `satyanadella`, `linkedin.com/in/reidhoffman`
— and get each person's recent posts as CSV, Excel or JSON: text, engagement,
media, repost source and the exact minute each one was published. No LinkedIn
account, no cookie, nothing to install.

```json
{ "profiles": ["satyanadella", "reidhoffman"], "maxPostsPerSource": 12 }
```

> **Unofficial.** This Actor is not affiliated with, endorsed by or sponsored by LinkedIn or Microsoft. It reads only publicly available pages, does not log in and does not bypass any access control. All product names and trademarks belong to their respective owners.

### What one row looks like

From a real run, unedited:

```json
{
  "postId": "7505114604155322368",
  "postUrl": "https://www.linkedin.com/feed/update/urn:li:activity:7505114604155322368/",
  "postedAt": "2026-09-14T04:05:59Z",
  "sourceType": "profile",
  "sourceKey": "satyanadella",
  "text": "Great example from Jeff Hollan showing how Foundry makes long-running agents enterprise-ready, starting with business outcomes and building security, safety guardrails, auditability, and FinOps into the system from the start, across the entire multi-agent, multi-model workflow.",
  "textLength": 278,
  "reactions": 868,
  "comments": 121,
  "topReactions": "LIKE, PRAISE, EMPATHY",
  "authorName": "Satya Nadella",
  "authorUrl": "https://www.linkedin.com/in/satyanadella",
  "authorType": "person",
  "isRepost": false,
  "mediaType": "image",
  "mediaUrl": "https://media.licdn.com/dms/image/v2/D5605AQG49bxsEGAXHw/videocover-high/…"
}
```

### The publication time is exact on every row

`postedAt` is a real timestamp — `2026-09-14T04:05:59Z` — on every post.
LinkedIn's public profile prints `1w` and `2mo`, never a date, so a column
built by reading the page cannot be sorted or grouped. This one is recovered
from the post itself and checked, wherever LinkedIn does print a time, against
theirs; on those posts they agree to the second.

That is what lets you ask "how often does this person post", "what did they
publish the week of the launch", or "which weekday gets them the most
reactions" — from a spreadsheet, without opening a single post.

### Fields

29 columns across a run; 12 on every row, the rest present when the post has
them.

| Field | What it is |
|---|---|
| `postId` · `postUrl` | The post's id, and a link that opens it |
| `postedAt` | Exact publication time, ISO 8601, UTC |
| `postedAtSource` | LinkedIn's own printed time, where it prints one — for checking the column above |
| `text` · `textLength` | The post body, and its length |
| `reactions` · `comments` | LinkedIn's own totals |
| `topReactions` | Which reactions, most used first: `LIKE, PRAISE, EMPATHY, INTEREST, ENTERTAINMENT, APPRECIATION` |
| `authorName` · `authorHeadline` · `authorUrl` · `authorType` | Who wrote it, and whether that is a person or a company |
| `isRepost` | Whether this is somebody else's content the person reshared |
| `repostedByName` | The person who reshared it, when they are not the author |
| `repostId` · `repostedAt` | The reshare's own id and time, which differ from the original's |
| `quotedAuthorName` · `quotedAuthorUrl` · `quotedText` | The post being quoted, when this one quotes another |
| `mediaType` | `video`, `image`, `link` or empty |
| `mediaUrl` · `mediaUrls` · `thumbnailUrl` | Direct links to what is attached |
| `linkUrl` · `linkTitle` | The shared link and its headline |
| `commentsFetched` · `commentList` | Comment text, author, time and likes — when you turn comments on |
| `sourceType` · `sourceKey` | Which of your inputs this row came from |
| `scrapedAt` | When this row was read |

#### A repost names both people

If a person reshares a colleague's post, `authorName` is the colleague who
wrote it and `repostedByName` is the person whose profile you asked about. If a
post quotes another post, `authorName` wrote the commentary and
`quotedAuthorName` wrote what is being commented on. "Who wrote this" and "who
chose to share it" are different questions and they stay different columns.

### Filters, and why they are the cheap part

Every filter is decided before a row is stored, so **a post a filter drops
costs you nothing** — no charge, and no second request for its comments.

| Filter | Use |
|---|---|
| `postedAfter` · `postedBefore` | Both bounds include the day you name |
| `minReactions` · `minComments` | Against LinkedIn's own totals |
| `skipReposts` | Only what they wrote themselves |
| `onlyWithText` | Drops a bare reshare with no words of its own |

`postedAfter` is the one that pays for itself. Set it to your last run's date
and a weekly check over fifty people costs the posts that are new, not the
whole window every time.

### Recipes

- **Watch the people who matter to you.** Fifty profiles, `postedAfter` = last
  Monday, on a weekly schedule: one table of what your prospects, customers or
  competitors' executives said this week.
- **Study a thought leader.** One profile, `skipReposts` on, sort by
  `reactions`: which topics and which `mediaType` earn the most.
- **Find who engages with someone.** Turn `includeComments` on for the posts
  that cleared `minReactions` — commenter names and profile links come back.
- **Build a content calendar benchmark.** Twenty profiles in your niche, group
  `postedAt` by weekday and hour.
- **Re-read one post.** Paste its link into `postUrls` to refresh its counts or
  pull its comments on their own.

### What it costs

Pay per event, and there are exactly two:

| Event | What it is | GOLD, per 1,000 |
|---|---|---|
| **Post** | One stored post, all its columns | **$1.35** |
| **Post with comments** | The extra request that fetches comment text | $1.35 |

**Comments are charged once per post, not once per comment.** Turning comments
off — the default — means that event never fires.

| Plan | Per 1,000 posts |
|---|---|
| Free | $2.00 |
| Bronze | $2.00 |
| Silver | $1.70 |
| Gold · Platinum · Diamond | $1.35 |

A run over three people is 3 requests and about 10 seconds for 36 posts.
Apify's $5 of monthly free credit is roughly **2,500 posts** on the free plan,
or about 1,250 with comments on.

### Limits

Said plainly, because they are properties of what LinkedIn shows the public
and no scraper can talk them away.

- **About a dozen recent posts per person.** A public profile shows a fixed
  window and offers no way to ask for more. For the full history you would
  need a signed-in session, which this Actor does not use and does not ask for.
  Run it on a schedule with `postedAfter` and the history accumulates.
- **Comments are the ones a signed-out visitor sees** — usually the first
  handful, not all of them. The full count is in `comments` on every row
  regardless, and `commentsFetched` says how many actually came back.
- **No reactor list.** Counts and the top reaction types, not the names of the
  people who reacted.
- **Long-form articles are not posts.** A person's `/pulse/` articles are a
  different content type with no exact timestamp and are left out.
- **A profile that hides its activity** from visitors comes back empty, is
  named in the run summary and is never charged for.
- **Free-plan users** get everything the paid plans do, at the Free rate above,
  until their platform credit runs out; the run then stops cleanly and keeps
  what it collected.

### Output

CSV, Excel, JSON, XML or the API. Four ready-made views: **Posts** (what was
said), **Engagement** (how it did), **Authors** (who said it) and **Media**.
Every run also writes a `RUN_SUMMARY` with what was stored, what was filtered
out and why, and how much traffic it used.

### FAQ

#### Do I need a LinkedIn account or cookie?

No. This reads the pages LinkedIn serves to a visitor who is not signed in.
There is no field to paste a session into, and nothing about your account is
involved.

#### How much does it cost to scrape 1,000 people's latest posts?

About 12 posts each is 12,000 rows: $16.20 on Gold, $24.00 on the Free plan.
With comment text on, double it.

#### Can I get everything a person has ever posted?

No, and no listing that reads the public profile can: it shows about a dozen
recent posts. This one says so, and a weekly run with `postedAfter` builds the
history from here on.

#### Does it work for company pages too?

Yes — paste a company address into the same field and up to 70 posts come
back with the same columns. There is a separate listing built around company
pages if that is your main use.

#### What happens on a profile that does not exist?

It is reported in the run summary by name and not charged for.

# Actor input Schema

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

One per line: `https://www.linkedin.com/in/satyanadella` or just `satyanadella`. A person's public page shows about a dozen recent posts and no more - that is what this reads, without logging in. Company pages work here too if you paste one.

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

A post link copied from LinkedIn, in any form: the long `…-activity-7496332962540589056-vQLE` address, `https://www.linkedin.com/feed/update/urn:li:activity:7496332962540589056/`, or the bare id.

## `maxPosts` (type: `integer`):

Across every source. 0 means no limit. Free-plan runs are capped by your own Apify usage, so start small.

## `maxPostsPerSource` (type: `integer`):

A person's public page holds about a dozen recent posts, so anything above 12 has the same effect as 12 for people. Company pages pasted here give up to 70.

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

Off by default because it is the expensive half: comments need a second request to a page ten times heavier than the post itself, and they are billed as a separate event. Every post already carries its **comment count** without this. What you get here is the comments LinkedIn shows a signed-out visitor — usually the first handful, not all of them.

## `maxComments` (type: `integer`):

How many comments to keep per post when the switch above is on.

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

`2026-08-01`, or a full timestamp. **This is the filter that makes a repeating run cheap**: the publication date is known before anything else is fetched, so an older post is skipped without being billed and without its comments being fetched.

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

`2026-08-31`, or a full timestamp. Both bounds include the day you name.

## `minReactions` (type: `integer`):

A post whose counter LinkedIn did not render is not treated as zero: it is skipped and reported separately in the run summary.

## `minComments` (type: `integer`):

Counted from LinkedIn's own total, not from how many comments are readable.

## `skipReposts` (type: `boolean`):

Keeps only what this company or person wrote themselves. A repost still names the original author in the row, so leave this off if you want to see who they amplify.

## `onlyWithText` (type: `boolean`):

Drops a repost shared without any words of its own.

## Actor input object example

```json
{
  "profiles": [
    "satyanadella",
    "https://www.linkedin.com/in/reidhoffman"
  ],
  "postUrls": [
    "https://www.linkedin.com/feed/update/urn:li:activity:7496332962540589056/"
  ],
  "maxPosts": 200,
  "maxPostsPerSource": 12,
  "includeComments": false,
  "maxComments": 10,
  "postedAfter": "2026-08-01",
  "postedBefore": "2026-08-31",
  "minReactions": 100,
  "minComments": 10,
  "skipReposts": false,
  "onlyWithText": false
}
```

# Actor output Schema

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

No description

## `engagement` (type: `string`):

No description

## `authors` (type: `string`):

No description

## `media` (type: `string`):

No description

## `all` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

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

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "profiles": [
        "satyanadella"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("lightmoon/linkedin-profile-posts-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "profiles": ["satyanadella"] }

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

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "profiles": [
    "satyanadella"
  ]
}' |
apify call lightmoon/linkedin-profile-posts-scraper --silent --output-dataset

```

## MCP server setup

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