# LinkedIn Profile Posts Scraper & Content Monitor (`generous_fog/linkedin-profile-posts-scraper-content-monitor`) Actor

Scrape recent LinkedIn posts from exact profile URLs with no-cookie public discovery, optional authenticated depth, keyword/date/content filters, engagement metrics, media links, deduplication, and resilient proxy retries. Built for content research, creator tracking, sales signals, and monitoring.

- **URL**: https://apify.com/generous\_fog/linkedin-profile-posts-scraper-content-monitor.md
- **Developed by:** [Cracks API](https://apify.com/generous_fog) (community)
- **Categories:** Lead generation, Social media, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$0.75 / 1,000 profile posts

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## LinkedIn Profile Posts Scraper

Extract structured posts and recent activity from exact LinkedIn profile URLs for recruiting research, sales intelligence, creator analysis, content research, and CRM enrichment.

This Actor combines conservative public no-cookie collection, public search-index fallback, and optional account-authorized enrichment. It returns one dataset record per successful post with post text, URL, publication date, content type, engagement counts when exposed, media, external links, author evidence, and source mode.

**Unofficial — not affiliated with LinkedIn.** This independent Actor is not endorsed by, sponsored by, or connected with LinkedIn. LinkedIn is a trademark of LinkedIn Corporation.

### Why use this LinkedIn profile posts scraper?

- Start from exact `https://www.linkedin.com/in/...` profile URLs.
- Deduplicate and normalize submitted profiles automatically.
- Collect public post and article signals without cookies where they are directly available.
- Fall back to directly visible public search-result evidence when LinkedIn's public activity page is unavailable.
- Optionally use the caller's own authorized `li_at` and `JSESSIONID` values for deeper feed responses.
- Filter by date range, keywords, and content type.
- Return one clean, integration-friendly record per post.
- Capture reactions, comments, reposts, media, and external links when the selected source exposes them.
- Use stable proxy sessions, polite jitter, retries, backoff, and explicit rate-limit handling.
- Respect a local maximum charge and leave errors, empty results, limits, and dry runs uncharged.

### Best use cases

#### Recruiting and talent intelligence

Track the public professional activity of candidates, speakers, executives, and domain experts. Use recent posts to understand topics, interests, writing style, and visible expertise before outreach or interviews.

#### Sales research and account planning

Collect activity from known stakeholders and analyze visible themes, launches, hiring signals, partnerships, problems, and priorities. Export structured posts to Sheets, a CRM, n8n, Zapier, Make, or a warehouse.

#### Creator and thought-leadership analysis

Study publishing frequency, content mix, recurring topics, engagement signals, media usage, and external links across a curated list of creators.

#### Content monitoring

Run the Actor on a schedule for the same profile URLs, deduplicate by `postUrl` or `postId`, and alert a team when a matching keyword or content type appears.

### Collection modes

#### Public no-cookie

The Actor checks the public recent-activity and profile pages for directly exposed structured data, JSON-LD, and post links. If those pages do not expose usable posts, it searches public search-result pages and reports only visible result titles, snippets, and LinkedIn post URLs as `public_index` evidence.

#### Authenticated session

Provide both secret `liAt` and `jsessionId` input values from an account you are authorized to use. The Actor requests profile and feed endpoints with that session, extracts post-like entities from normalized response structures, and never writes the secret values to the dataset or logs.

Authenticated endpoints and response shapes can change. `auto` mode falls back to public collection if the authorized route is unavailable. `authenticated` mode reports an uncharged error instead of silently changing source mode.

### Input

| Field | Purpose |
| --- | --- |
| `profileUrls` | Required exact LinkedIn `/in/` profile URLs. |
| `maxProfiles` | Process 1-1,000 unique valid profiles. Default: 100. |
| `maxPostsPerProfile` | Return 1-500 posts after filters. Default: 10. |
| `mode` | `public`, `auto`, or `authenticated`. |
| `liAt`, `jsessionId` | Optional secret values from the caller's own authorized session. |
| `dateFrom`, `dateTo` | Optional ISO date or timestamp boundaries. Undated posts are excluded when date filtering is active. |
| `keywords` | Optional case-insensitive OR filter across visible post text and links. |
| `contentTypes` | Optional values: `all`, `post`, `article`, `video`, `image`, `document`, `poll`, `newsletter`, `repost`, `unknown`. |
| `requestDelaySecs` | Base polite delay with automatic jitter. Default: 2 seconds. |
| `maxRetries` | 0-5 retries for transient failures, 429, 5xx, and 999 responses. |
| `maxTotalCharge` | Optional local USD cap. No paid result is emitted after the next result would exceed it. |
| `dryRun` | Validate and normalize input without network requests or paid events. |
| `proxyConfiguration` | Apify proxy settings. Residential proxies are recommended for reliable compliant runs. |

#### Example input

```json
{
  "profileUrls": [
    "https://www.linkedin.com/in/satyanadella/"
  ],
  "maxPostsPerProfile": 20,
  "mode": "public",
  "keywords": ["AI", "cloud"],
  "contentTypes": ["post", "article", "video"],
  "requestDelaySecs": 2,
  "maxRetries": 2,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": ["RESIDENTIAL"]
  }
}
```

### Output

Every successful record represents one post or article.

```json
{
  "postId": "urn:li:activity:1234567890",
  "postUrl": "https://www.linkedin.com/feed/update/urn:li:activity:1234567890/",
  "profileUrl": "https://www.linkedin.com/in/example/",
  "publicIdentifier": "example",
  "authorName": "Example Person",
  "text": "Visible post text...",
  "publishedAt": "2026-08-25T08:00:00.000Z",
  "contentType": "post",
  "reactionsCount": 120,
  "commentsCount": 18,
  "repostsCount": 4,
  "media": [
    { "type": "image", "url": "https://..." }
  ],
  "externalLinks": ["https://example.com/article"],
  "sourceMode": "authenticated",
  "sourceEvidence": {
    "voyager": true,
    "authorizedSession": true
  },
  "status": "success"
}
```

Counts and dates are `null` when the chosen source does not expose them. The Actor does not invent engagement, timestamps, text, or media.

### Dataset views

- **Successful posts**: primary post fields for exports and automations.
- **Engagement metrics**: reactions, comments, reposts, date, and source.
- **Media and external links**: normalized media objects, outbound links, and source evidence.
- **Errors, empty results, and limits**: uncharged operational records, including dry runs.

### Pricing event

A successful post is written atomically with exactly one event:

| Event key | Recommended price | Meaning |
| --- | ---: | --- |
| `profile-post` | $0.75 / 1,000 posts | One successfully extracted post or article. |

`Actor.pushData(record, "profile-post")` keeps data persistence and charging together. Before charging, a post must include a valid URL, meaningful text, an author, a verified publication timestamp, and at least one rich signal from engagement counts, media, or external links. Thin indexed snippets are emitted as `insufficient_data` with no paid event. Error, `no_results`, `limit_reached`, and `dry_run` records are also uncharged. The Store price is configured in Apify Console; this repository only emits the stable event key.

### Reliability and data quality

- Exact `/in/` URL validation prevents broad people-search behavior.
- Duplicate profile inputs and duplicate post records are removed.
- Proxy session IDs contain only safe letters, digits, and underscores.
- A profile keeps one proxy session across public, search-index, and authorized requests.
- Retries use `Retry-After` when available and otherwise use increasing backoff plus jitter.
- Search-index results are labeled `public_index` and include the search engine and visible evidence.
- A hard paid-quality gate rejects undated or evidence-thin snippets as uncharged `insufficient_data` records.
- Date filters exclude undated posts instead of assuming a date.
- Keyword and content filters run after normalization and before charging.
- `maxTotalCharge` is checked before every paid event. Apify's platform charge limit remains authoritative.

### Scheduling and deduplication

For monitoring, schedule the Actor daily or weekly and store seen `postUrl` or `postId` values in your workflow or destination. A new-post alert can then trigger only when the current run contains a previously unseen ID.

### API and integrations

Run this Actor from Apify Console, REST API, JavaScript/Python clients, schedules, webhooks, n8n, Zapier, Make, Google Sheets, or a custom CRM pipeline. The default dataset API exposes JSON, CSV, XML, Excel, HTML, and RSS-compatible export options supported by Apify.

### Limitations and responsible use

Public pages, public search indexes, authorized endpoints, and response shapes vary by account, region, profile privacy, and time. Some profiles expose no recent posts; public-index snippets can be incomplete; engagement counts can be absent or stale; and authenticated endpoints can change without notice.

The Actor does not bypass CAPTCHA, authentication walls, access controls, or platform restrictions. Use only data you are permitted to collect and process. Follow applicable law, privacy requirements, LinkedIn's terms, and your organization's policies. Do not use the output for spam, harassment, discrimination, invasive profiling, or other harmful activity.

### Local validation

```bash
npm install
npm run check
```

# Actor input Schema

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

Exact public LinkedIn /in/ profile URLs. Duplicate and invalid URLs are removed automatically.

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

Maximum unique valid profile URLs to process.

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

Maximum successful post records returned for each profile after filters and deduplication.

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

Auto uses an authorized session only when both secret values are supplied and safely falls back to public collection.

## `liAt` (type: `string`):

Optional secret from your own authorized LinkedIn session. Used only for authenticated collection.

## `jsessionId` (type: `string`):

Optional CSRF session value from your own authorized LinkedIn session.

## `dateFrom` (type: `string`):

Optional ISO date or timestamp. Posts without a directly available publication date are excluded when a date filter is active.

## `dateTo` (type: `string`):

Optional ISO date or timestamp. Date-only values include the full UTC day.

## `keywords` (type: `array`):

Optional case-insensitive keywords. A post is returned when its visible text or links match at least one keyword.

## `contentTypes` (type: `array`):

Optional values: all, post, article, video, image, document, poll, newsletter, repost, or unknown.

## `requestDelaySecs` (type: `number`):

Base polite delay. A small random jitter and exponential retry backoff are added automatically.

## `maxRetries` (type: `integer`):

Retries for transient network errors, rate limits, server errors, and LinkedIn 999 responses.

## `maxTotalCharge` (type: `number`):

Optional local USD cap. No paid post event is emitted after this cap would be exceeded. Apify run limits remain authoritative.

## `dryRun` (type: `boolean`):

Normalize and validate profile URLs, then emit planned records with no network requests or paid events.

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

Optional Apify proxy. Residential proxy groups provide the best availability for compliant public collection and daily tests.

## Actor input object example

```json
{
  "profileUrls": [
    "https://www.linkedin.com/in/satyanadella/"
  ],
  "maxProfiles": 100,
  "maxPostsPerProfile": 10,
  "mode": "public",
  "keywords": [],
  "contentTypes": [
    "all"
  ],
  "requestDelaySecs": 2,
  "maxRetries": 2,
  "dryRun": false,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

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

No description

## `successfulPosts` (type: `string`):

No description

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

No description

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

No description

## `errors` (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 = {
    "profileUrls": [
        "https://www.linkedin.com/in/satyanadella/"
    ],
    "mode": "public",
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("generous_fog/linkedin-profile-posts-scraper-content-monitor").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/satyanadella/"],
    "mode": "public",
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("generous_fog/linkedin-profile-posts-scraper-content-monitor").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/satyanadella/"
  ],
  "mode": "public",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call generous_fog/linkedin-profile-posts-scraper-content-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,generous_fog/linkedin-profile-posts-scraper-content-monitor"
        }
    }
}

```

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/qpdg68gQF2jJXaol9/builds/IpUmOpJ2l4ZZjdWkI/openapi.json
