# LinkedIn Activity Feed Scraper with Engagement Filters (`nimbryxo-studio/linkedin-activity-feed-scraper-with-engagement`) Actor

Pull posts, articles, and reposts from LinkedIn profile activity feeds, with reaction/comment/date filters and a per-reaction-type breakdown. Requires a LinkedIn session cookie.

- **URL**: https://apify.com/nimbryxo-studio/linkedin-activity-feed-scraper-with-engagement.md
- **Developed by:** [Nimbryxo-studio](https://apify.com/nimbryxo-studio) (community)
- **Categories:** Social media, Lead generation, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.49 / 1,000 results

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

## LinkedIn Activity Feed Scraper with Engagement Filters

Scrape **LinkedIn profile posts, company posts, and individual public post URLs** without cookies, logins, or an API key. Get the full post text, author name and profile URL, exact publish date, reaction and comment totals, the visible comments, media, and clear original/quote/repost flags.

### What this actor does

- **Three modes:** `postUrls` for specific public posts, `profiles` for a profile's recent posts, and `companies` for a company's recent posts.
- **Cookieless by design:** reads public LinkedIn pages only, so there is no session cookie to export, expire, or leak.
- **Exact post data:** full text, author identity, ISO publish date, reaction total, comment total, media URLs, and the comments visible on the public page.
- **Correct reshare semantics:** `postType` separates `original`, `quote`, and `repost`; `repostOf` names the original author and text when LinkedIn exposes them.
- **Filters:** media type, post type, relative date window, minimum reactions, minimum comments, and a global record cap. Empty fields are omitted.

### Output per post

- `postId`, `postUrl`, `postedAt` — numeric activity ID, canonical post URL, exact ISO publish date
- `authorName`, `authorProfileUrl`, `authorType`, `authorAvatar`, `authorFollowers` — author identity; `authorType` is `Person` or `Organization`
- `content` — full post text
- `hashtags` — hashtags found in the post text
- `mediaType` — `text`, `image`, `video`, `article`, `repost`; legacy values `document`, `carousel`, `poll`, and `event` are accepted by the filter but are not exposed on public pages
- `mediaUrls`, `mediaItems` — image URLs, or a video URL with thumbnail, duration, and embed URL
- `articleTitle` — live article/link title when the post is an article-style or image/link share
- `comments` — up to 10 visible comments, each with `authorName`, `text`, `postedAt`, and `reactionsCount`
- `commentsCount` — total comment count on the post
- `reactionsCount` — total reaction count (likes plus other reactions)
- `postType` — `original`, `quote`, or `repost`
- `isRepost`, `isQuotePost` — flags for reshared and quote posts
- `repostOf` — original `authorName`, `authorProfileUrl`, `authorType`, `content`, and `postUrl` when available
- `inputUrl` — the post or target URL that produced this record
- `inputProfileUrl` — profile URL (mode=`profiles`)
- `inputCompanyUrl` — company URL (mode=`companies`)
- `dataSource` — `public-post`, `public-profile`, or `public-company`
- `partial` — present and `true` when only the discovery stub could be read (for example, the post page was blocked)
- `recordType` — `post` for post records, `error` for diagnostic rows
- `scrapedAt` — ISO 8601 run timestamp

Legacy compatibility fields remain part of the output contract but are omitted when the public page does not expose them: `authorHeadline`, `articleUrl`, `linkPreview`, `documentUrl`, `pollOptions`, `mentionedPeople`, `mentionedCompanies`, `reactionBreakdown`, `repostsCount`, and `viewsCount`. See **Limitations**.

Diagnostic rows have `recordType: "error"`, `targetType`, `inputUrl`, `status` (`invalid`, `not_found`, `blocked`, `empty`, or `fetch_failed`), `error`, optional `httpStatus`, and `scrapedAt`. They are not counted toward `maxItems`.

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `mode` | string | `postUrls` | `postUrls`, `profiles`, or `companies` |
| `postUrls` | array | – | Public post URLs, feed-update URLs, activity URNs, or numeric activity IDs (mode=`postUrls`) |
| `profileUrls` | array | – | Profile URLs or public identifiers, e.g. `https://www.linkedin.com/in/williamhgates/` or `williamhgates` (mode=`profiles`) |
| `companyUrls` | array | – | Company URLs or slugs, e.g. `https://www.linkedin.com/company/google/` or `google` (mode=`companies`) |
| `maxPostsPerProfile` | integer | `50` | Maximum posts kept per profile or company (1–500); public pages expose about 6–8 profile posts and 2–10 company posts |
| `mediaType` | array | – | Keep only `text`, `image`, `video`, `article`, or `repost`; empty keeps all |
| `postTypes` | array | `["text","image","video","article","document","repost"]` | Legacy type filter; left at its default it changes nothing, otherwise it is merged with `mediaType` |
| `datePosted` | string | Any time | `""` (any time), `r86400` (24 hours), `r604800` (week), or `r2592000` (month) |
| `minReactions` | integer | `0` | Minimum reaction count |
| `minComments` | integer | `0` | Minimum comment count |
| `includeReposts` | boolean | `true` | Keep quote posts and reposts; set to `false` to drop them |
| `maxItems` | integer | `1000` | Global cap on post records (1–10000); error rows do not count |
| `proxyConfiguration` | object | – | Optional. The actor works without a proxy |

#### Example: one public post URL

```json
{
  "mode": "postUrls",
  "postUrls": [
    "https://www.linkedin.com/posts/williamhgates_how-better-data-helped-us-cut-child-mortality-activity-7329207003942125568-_gfJ"
  ]
}
```

#### Example: recent posts from several profiles

```json
{
  "mode": "profiles",
  "profileUrls": [
    "https://www.linkedin.com/in/satyanadella/",
    "https://www.linkedin.com/in/adam-grant/"
  ],
  "maxPostsPerProfile": 8
}
```

#### Example: company posts with engagement filters

```json
{
  "mode": "companies",
  "companyUrls": ["https://www.linkedin.com/company/google/"],
  "datePosted": "r2592000",
  "minReactions": 100,
  "includeReposts": false
}
```

#### Example: a mixed list of posts, with comments and media

```json
{
  "mode": "postUrls",
  "postUrls": [
    "https://www.linkedin.com/feed/update/urn:li:activity:7518622101692436482/",
    "https://www.linkedin.com/posts/nvidia_ai-is-technology-activity-7518633624649252864-9XSo",
    "https://www.linkedin.com/feed/update/urn:li:activity:7518239029990023168/"
  ],
  "mediaType": ["image", "video", "article"],
  "minComments": 5,
  "maxItems": 50
}
```

### Use cases

- **Content research** — collect full posts and visible comments from experts, creators, and companies you track.
- **Competitive intelligence** — monitor the latest company posts and compare engagement across peers.
- **Lead generation** — find active, high-engagement authors and the posts that sparked discussion.
- **Influencer vetting** — check real reaction and comment counts before a sponsorship or partnership.
- **Campaign reporting** — export exact dates, post URLs, and engagement totals for a reporting window.
- **Dataset building** — assemble a clean post corpus for topic modelling, sentiment analysis, or LLM training.

### Limitations

- **Public surfaces only.** Private profiles, member-only posts, and pages that return a login wall or HTTP 999 are reported as diagnostic error rows instead of being bypassed.
- **Profile depth is about 6–8 posts.** A public profile page shows only its latest posts and has no "load more" for logged-out visitors. `maxPostsPerProfile` cannot unlock older history.
- **Company depth is about 2–10 posts.** It comes from the company home page, not the `/posts/` tab, which is login-gated. Older company posts are not available.
- **Comments are the visible subset.** The public page exposes up to 10 comments, with text, author name, date, and likes. It does not expose replies, comment permalinks, or commenter profile URLs.
- **These fields are not public:** shares/reposts counts, views/impressions, per-reaction-type breakdown, reactor or liker identities, and author headlines. They are omitted rather than estimated.
- **Some media labels are not public:** document, carousel, poll, and event posts are not distinguishable on the public post page, so they are reported as `text`, `image`, `video`, or `article` based on what the page exposes.
- **Date filters apply to what was discovered.** In profile and company modes, only the latest page of posts is filtered; the filter does not make older posts appear.
- **A failed hydration can produce a `partial` record.** When a profile or company page lists a post but its public post page is blocked, the record keeps the listing text, date, author, and reaction count and is marked `partial: true`.

### FAQ

**Do I need a LinkedIn cookie or login?**
No. This actor reads public LinkedIn pages only. There is no cookie field to fill and no account risk.

**How many posts can I get per profile?**
About 6–8 recent posts per profile. That is the public page's own limit; setting `maxPostsPerProfile` higher does not add pagination.

**How many posts can I get per company?**
About 2–10 recent posts from the company home page. Older company posts are behind a login wall.

**Why are shares, views, or the reaction breakdown missing?**
LinkedIn does not publish those values on public post pages. The actor deliberately omits them instead of returning zeros or guesses.

**What is the difference between `quote` and `repost`?**
A `quote` adds the sharer's own commentary above the original content. A `repost` shares the original without new commentary. `repostOf` carries the original author and text when LinkedIn exposes them.

**Are the comments the full thread?**
No. They are the up-to-10 comments LinkedIn renders on the public page, in the order LinkedIn shows them. Replies and commenter profile URLs are not included.

**Why did I get an error row instead of a post?**
The post, profile, or company was private, deleted, rate-limited, or returned a login wall. The row's `status` and `error` fields say which case occurred; other targets in the same run still return data.

**Can I scrape a specific old post?**
Yes, if it is public. Put its URL, feed-update URL, activity URN, or numeric ID in `postUrls`.

**Can I run this on a schedule?**
Yes. A daily run with one public post URL is enough to return data and keep a dataset fresh.

**Is this affiliated with LinkedIn?**
No. This is an independent, third-party actor; it is not operated by or affiliated with LinkedIn.

# Actor input Schema

## `mode` (type: `string`):

Choose what to scrape: specific public post URLs, a profile's recent posts, or a company's recent posts.

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

Public LinkedIn post URLs, feed-update URLs, activity URNs or numeric activity IDs. (mode=postUrls)

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

LinkedIn profile URLs or public identifiers, e.g. https://www.linkedin.com/in/williamhgates/ or williamhgates. (mode=profiles)

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

LinkedIn company URLs or slugs, e.g. https://www.linkedin.com/company/google/ or google. (mode=companies)

## `maxPostsPerProfile` (type: `integer`):

Maximum posts to keep per profile or company. Public profile pages expose about 6-8 recent posts and company home pages 2-10, so larger values do not unlock more history.

## `mediaType` (type: `array`):

Only keep posts of these media types. Public pages reliably expose text, image, video and article; repost is a pure reshare. Leave empty to keep all.

## `postTypes` (type: `array`):

Legacy type filter. Left at its default of six types it changes nothing; any other selection is merged with mediaType.

## `datePosted` (type: `string`):

Keep posts published within this window, measured from the run time.

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

Only keep posts with at least this many reactions. Posts without a readable reaction count are treated as zero.

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

Only keep posts with at least this many comments. Posts without a readable comment count are treated as zero.

## `includeReposts` (type: `boolean`):

When enabled, reposts and quote posts are kept with their postType and repostOf fields. When disabled, they are dropped.

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

Global cap on post records in the dataset. Diagnostic error rows are not counted toward this cap.

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

Optional. The actor works without a proxy. Supply one only if your runs are consistently blocked.

## Actor input object example

```json
{
  "mode": "postUrls",
  "postUrls": [
    "https://www.linkedin.com/posts/williamhgates_how-better-data-helped-us-cut-child-mortality-activity-7329207003942125568-_gfJ"
  ],
  "maxPostsPerProfile": 50,
  "postTypes": [
    "text",
    "image",
    "video",
    "article",
    "document",
    "repost"
  ],
  "datePosted": "",
  "minReactions": 0,
  "minComments": 0,
  "includeReposts": true,
  "maxItems": 1000
}
```

# Actor output Schema

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

Dataset containing all scraped LinkedIn posts and any diagnostic error rows.

# 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 = {
    "mode": "postUrls",
    "postUrls": [
        "https://www.linkedin.com/posts/williamhgates_how-better-data-helped-us-cut-child-mortality-activity-7329207003942125568-_gfJ"
    ],
    "includeReposts": true
};

// Run the Actor and wait for it to finish
const run = await client.actor("nimbryxo-studio/linkedin-activity-feed-scraper-with-engagement").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 = {
    "mode": "postUrls",
    "postUrls": ["https://www.linkedin.com/posts/williamhgates_how-better-data-helped-us-cut-child-mortality-activity-7329207003942125568-_gfJ"],
    "includeReposts": True,
}

# Run the Actor and wait for it to finish
run = client.actor("nimbryxo-studio/linkedin-activity-feed-scraper-with-engagement").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 '{
  "mode": "postUrls",
  "postUrls": [
    "https://www.linkedin.com/posts/williamhgates_how-better-data-helped-us-cut-child-mortality-activity-7329207003942125568-_gfJ"
  ],
  "includeReposts": true
}' |
apify call nimbryxo-studio/linkedin-activity-feed-scraper-with-engagement --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,nimbryxo-studio/linkedin-activity-feed-scraper-with-engagement"
        }
    }
}
```

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/CIxdgqOHBfl4nTwzB/builds/2J31IX5LPEdINxWgA/openapi.json
