# LinkedIn Profile Engagement Timeline Scraper (`dataspryva/linkedin-profile-engagement-timeline-scraper`) Actor

Pull a LinkedIn profile's posts, reposts, comments, and reactions in one run, with the original post's author, text, and link attached to each activity. Needs your own LinkedIn session cookie.

- **URL**: https://apify.com/dataspryva/linkedin-profile-engagement-timeline-scraper.md
- **Developed by:** [Dataspryva](https://apify.com/dataspryva) (community)
- **Categories:** Social media, Lead generation, Automation
- **Stats:** 1 total users, 0 monthly users, 0.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 Profile Engagement Timeline Scraper

Collect a LinkedIn profile's **recent public activity** in one run: the posts and reposts they published, the comments they left on other people's posts, and the reactions they gave. Each record carries the target post's author, text, URL, and engagement totals, so a like or comment arrives with the context it needs.

No cookie, login, or LinkedIn account is required. The actor reads the public version of the profile page that LinkedIn serves to logged-out visitors and search engines.

### What this actor does

- Collects the three public activity tabs: **Posts**, **Comments**, and **Reactions**.
- Returns one record per activity, with the target post's text, author, URL, reaction total, and comment total.
- Opens each target post, when available, for exact dates, media, and the comment that the profile wrote.
- Supports one or many profile URLs or usernames in a single run.
- Filters by activity type and an optional date window.
- Emits a diagnostic row for a profile that is private, unavailable, or returns an auth wall, instead of failing the whole run.
- Empty fields are omitted, never returned as `null` or an empty string.

### Output per activity

- `recordType` — `activity`, `error`, or `noResults`
- `activityType` — `post`, `repost`, `comment`, or `reaction`
- `profileName`, `profileUsername`, `inputProfileUrl` — the profile that performed the activity
- `activityTimeAgo` — LinkedIn's relative activity age (`2w`, `3mo`) for posts and comments. Reposts omit it because only the original post's age is public.
- `originalPostTimeAgo` — LinkedIn's relative age of the target post for posts, reposts, and reactions. Omitted for comments, whose card only shows the comment age.
- `reactedAt` — exact ISO activity time when public: an original post's publish time or a matched comment time. Omitted for reactions, reposts, and comments whose time is not visible.
- `originalPostUrl`, `originalPostPermalink`, `originalPostId` — canonical feed URL, the public post link, and the post ID
- `originalPostedAt` — exact target-post publish time, or a timestamp decoded from the post ID when the post page is unavailable
- `originalAuthorName`, `originalAuthorProfileUrl`, `originalAuthorAvatar` — the target post's author
- `originalAuthorHeadline` — kept for compatibility; public pages do not expose it, so it is normally omitted
- `content` — the target post's text, full when the post page is available and a preview otherwise
- `reactionsCount`, `commentsCount` — engagement totals for the target post
- `targetPostUrl`, `targetAuthor` — target-post context for comments, reactions, and reposts
- `commentText` — the comment the profile wrote
- `commentIsReply` — `true` when the profile replied to another comment
- `commentReactionsCount` — reactions on the matched comment
- `reactionType` — `like` when the public card explicitly says "liked this"; omitted for the generic "reacted on this" label because the exact reaction type is not public
- `mediaItems` — images, video, or article attachments when the public post exposes them
- `dataSource` — `postPage` or `activityCard`, showing whether page-level details were available
- `scrapedAt` — when the record was collected

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `profileUrls` | array | — | Public LinkedIn profile URLs or usernames. Required; up to 500 per run. |
| `activityTypes` | array | `posts`, `comments`, `reactions` | `posts`, `comments`, `reactions`. Posts includes reposts; comments includes replies. The legacy value `shares` is accepted and returned as posts/reposts. |
| `includePostDetails` | boolean | `true` | Open each target post for full text, exact dates, counts, media, and comment times. |
| `minDate` | string | — | Keep posts and comments on or after this date. Reactions have no public event date and are kept. |
| `maxDate` | string | — | Keep posts and comments on or before this date. Reactions are kept. |
| `maxActivitiesPerProfile` | integer | `50` | Maximum records per profile across selected types (1–500). |
| `maxItems` | integer | `500` | Maximum activity records for the whole run (1–10,000). Error rows do not count. |

Older saved tasks that send `cookie`, `language`, or `proxyConfiguration` still run; those keys are ignored with a note. Legacy `activityType` no longer filters the run because the schema supplies the default `activityTypes`; set `activityTypes` explicitly. The old `shares` value is rejected by the current schema; use `posts`.

#### Example: everything public for one profile

```json
{
  "profileUrls": ["https://www.linkedin.com/in/satyanadella/"],
  "activityTypes": ["posts", "comments", "reactions"],
  "includePostDetails": true,
  "maxActivitiesPerProfile": 50
}
```

#### Example: comments in a date window

```json
{
  "profileUrls": ["https://www.linkedin.com/in/satyanadella/"],
  "activityTypes": ["comments"],
  "minDate": "2026-01-01",
  "maxDate": "2026-10-23",
  "maxActivitiesPerProfile": 20
}
```

#### Example: reactions for a batch of profiles

```json
{
  "profileUrls": [
    "https://www.linkedin.com/in/satyanadella/",
    "https://www.linkedin.com/in/williamhgates/",
    "adam-grant"
  ],
  "activityTypes": ["reactions"],
  "includePostDetails": false,
  "maxItems": 100
}
```

### Use cases

- **Sales and ABM research** — see which topics a prospect comments on or reacts to before outreach.
- **Influencer and executive audits** — compare what a leader publishes with what they engage with.
- **Competitive research** — track the public posts and reactions of a competitor's team.
- **Content strategy** — study the posts that draw comments and reactions from a target audience.
- **CRM enrichment** — add recent public engagement signals to a contact record.

### Limitations

- **Recent public activity only.** A public profile page renders one pageful per tab: typically 0–9 posts, 0–3 top-level comments, and 0–9 reactions. The "See all activities" link requires a LinkedIn sign-in, so older history is not available without a login.
- **Reaction times and exact reaction types are not public.** A reaction card says "liked this" or the generic "reacted on this". The actor records `reactionType: "like"` only for the explicit "liked this" label; for "reacted on this" it omits the field rather than guessing celebrate, love, or another type. `reactedAt` is omitted for all reactions.
- **Reaction cards have two labels.** Both "liked this" and "reacted on this" are returned as `activityType: "reaction"`, including when the card appears in the Posts panel.
- **Comment times are conditional.** The profile page shows only a relative age. An exact comment time is filled when the comment is visible among the target post's public comments; otherwise `reactedAt` is omitted and `activityTimeAgo` remains.
- **Comment replies have no public parent-post link.** They are returned with `commentIsReply: true` and without a target post URL.
- **Some profiles are private.** LinkedIn may return an auth wall or HTTP 999 for a profile whose public visibility is restricted, or temporarily block a request. The actor retries, then emits a `blocked_or_private` error row for that profile. After repeated 999 responses it may retry through Apify's automatic datacenter proxy; no proxy input is requested from you.
- **Target posts can disappear.** If a post was deleted or made private after the activity card was rendered, the record keeps the card data and sets `dataSource: "activityCard"`.
- **Repost/share counts, views, reaction breakdowns, and the names of reactors are not public.** Reposts themselves are returned as `repost`; the missing numbers are not fabricated.
- **A public profile link may return 999 to a plain HTTP client** while still opening normally in a browser. That is LinkedIn's access behaviour, not a broken record.

### FAQ

**Do I need a LinkedIn cookie or account?**
No. The actor never asks you for a cookie, login, or proxy. It reads only the public profile and post pages; after repeated 999 blocks it may use Apify's automatic datacenter proxy.

**Why do I get fewer rows than I asked for?**
LinkedIn renders a fixed recent-activity section per profile. There is no public "load more" or pagination, so the maximum is one pageful per tab even when `maxActivitiesPerProfile` is higher.

**Why is `reactedAt` missing on a reaction?**
LinkedIn does not expose when a reaction was given. The target post's own date is in `originalPostedAt` and `originalPostTimeAgo`; they are not reaction times. `reactionType` is filled only when the card explicitly says "liked this".

**Why is `reactedAt` sometimes missing on a comment?**
The exact comment time comes from the target post's public comment list. If the comment is not among the visible comments, only the relative `activityTimeAgo` is available.

**Can I collect older posts or a complete history?**
No. The public page exposes only the recent activity section. Schedule the actor and deduplicate on `originalPostId` plus `profileUsername` to build history forward.

**Can I see who reacted to a post?**
No. Reactor identities are visible only to signed-in LinkedIn members, so they are not part of this actor.

**Why did one profile return an error row?**
That profile may hide its activity from logged-out visitors, may not exist, or LinkedIn may have returned a temporary 999. The row's `status` and `error` fields explain which case applied; other profiles in the same run continue.

**Why do some comments have no target post?**
Those are replies to another comment. The public card shows the reply text and age but not the parent post link.

**Is this affiliated with LinkedIn?**
No. This is an independent third-party actor that reads public pages. It is not operated by or affiliated with LinkedIn.

# Actor input Schema

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

Public LinkedIn profile URLs or usernames. Accepts https://www.linkedin.com/in/name, country subdomains, and bare usernames.

## `activityTypes` (type: `array`):

Which public activity tabs to collect. Posts includes reposts; comments includes replies. The legacy value shares is accepted and returned as posts/reposts.

## `includePostDetails` (type: `boolean`):

Open each target post for full text, exact dates, engagement totals, media, and comment timestamps. Turn off for a faster card-only run.

## `minDate` (type: `string`):

Keep posts and comments whose exact activity date is on or after this date. Reactions have no public event date and are kept.

## `maxDate` (type: `string`):

Keep posts and comments whose exact activity date is on or before this date. Reactions have no public event date and are kept.

## `maxActivitiesPerProfile` (type: `integer`):

Maximum records per profile across the selected activity types. Public profiles show one pageful per tab.

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

Maximum activity records for the whole run. Error rows do not count toward this limit.

## Actor input object example

```json
{
  "profileUrls": [
    "https://www.linkedin.com/in/satyanadella/"
  ],
  "activityTypes": [
    "posts"
  ],
  "includePostDetails": true,
  "minDate": "",
  "maxDate": "",
  "maxActivitiesPerProfile": 1,
  "maxItems": 1
}
```

# Actor output Schema

## `activities` (type: `string`):

Dataset containing scraped public LinkedIn profile activity records.

# 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/"
    ],
    "activityTypes": [
        "posts"
    ],
    "includePostDetails": true,
    "minDate": "",
    "maxDate": "",
    "maxActivitiesPerProfile": 1,
    "maxItems": 1
};

// Run the Actor and wait for it to finish
const run = await client.actor("dataspryva/linkedin-profile-engagement-timeline-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/satyanadella/"],
    "activityTypes": ["posts"],
    "includePostDetails": True,
    "minDate": "",
    "maxDate": "",
    "maxActivitiesPerProfile": 1,
    "maxItems": 1,
}

# Run the Actor and wait for it to finish
run = client.actor("dataspryva/linkedin-profile-engagement-timeline-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/satyanadella/"
  ],
  "activityTypes": [
    "posts"
  ],
  "includePostDetails": true,
  "minDate": "",
  "maxDate": "",
  "maxActivitiesPerProfile": 1,
  "maxItems": 1
}' |
apify call dataspryva/linkedin-profile-engagement-timeline-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,dataspryva/linkedin-profile-engagement-timeline-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/ENtqoTbxwWPCmRg9X/builds/TlgvwYEC6d1fur7Dm/openapi.json
