# LinkedIn Posts Scraper - Profiles & Companies, No Login (`mochiboo/linkedin-posts-public-scraper`) Actor

Latest posts from public LinkedIn profiles, company pages and single post URLs: text, date, likes, author, link. Optional comment count, image or video URL, author followers. No account or cookies. No comments or commenters; emails and phones in text are redacted.

- **URL**: https://apify.com/mochiboo/linkedin-posts-public-scraper.md
- **Developed by:** [mochi](https://apify.com/mochiboo) (community)
- **Categories:** Social media, Lead generation, Automation
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 results

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?

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 Posts Scraper: latest posts from profiles and company pages (no login)

Reads the posts that LinkedIn shows to visitors who are **not signed in**: on a person's public profile, on a company page and on a single post page. One row per post with the full text, publish date, like count, author and link. No LinkedIn account, no cookies, no login.

Price: pay per result, at the rate shown in the pricing panel of this page, plus a very small per-run start fee. You are charged only for the results delivered.

### Read this first: you get the most recent posts, not the archive

LinkedIn shows a signed-out visitor only the latest posts of a profile or company page. On 2026-10-20 the five pages we tested showed **8 posts (Bill Gates), 7 (Satya Nadella), 4 (Andrew Ng), 5 (Stripe) and 2 (Microsoft)**. A wider sample of 25 pages the same day showed 2 to 10 posts, median 8. LinkedIn does not put every post a page shows into the page data this actor reads. On 2026-10-20 one of the 12 profiles we tried listed 10 of its own posts but returned no rows; pages that only repost other people's content or show only comments also return nothing. These pages are counted in `noPostsOnPage`; single posts of such a profile can still be fetched with `postUrls`. Posts LinkedIn hides from signed-out visitors are never read. This actor is for **monitoring recent activity** (what did these people and companies post lately?), not for downloading a full post history. If you need 50 or more posts per person, this is the wrong tool.

To keep a running record, start the actor regularly and keep `activityId` (LinkedIn's post id) as your key. `postedAfter` skips posts older than a date; the Apify schedule itself sends a fixed input, so use a scheduler or an integration (Make, n8n, Zapier, your own code) if the date has to move forward every day.

### What you get per post

One row per post (and, if you ask for them, per article):

| field | meaning |
|---|---|
| `postUrl`, `activityId` | link to the post, LinkedIn's numeric post id (kept as text: the id has 19 digits, too long to be a safe number) |
| `type` | `post`, or `article` (LinkedIn newsletter / Pulse articles, person profiles only, `includeArticles`) |
| `authorName`, `authorUrl`, `authorType` | who wrote it; `person` or `company` |
| `authorFollowers` | the author's follower count. Person profiles: shown on the page. Company pages: only with `includeDetails` |
| `datePublished` | ISO time in UTC |
| `title` | the headline of an article, `null` for posts |
| `text` | the full text of the post (emails and phone numbers replaced, see below). `null` for articles and for posts that have no caption (image or video only; 4 of 182 posts in our 2026-10-20 sample) |
| `likes` | like count |
| `commentCount` | **only the number** of comments, with `includeDetails` or a `postUrls` entry |
| `mediaType`, `imageUrl`, `videoUrl`, `videoThumbnailUrl`, `videoDuration` | `image`, `video` or `text` and the media links, with `includeDetails` or a `postUrls` entry |
| `detailStatus` | `not_requested`, `ok`, `failed` or `blocked` (see "Details" below) |
| `source`, `input`, `scrapedAt` | the page that produced the row, your input string, the time of the request |

### What is NOT collected

- **No comments.** No comment text, no commenter names, no commenter profiles. Only the number of comments.
- **No reactions list.** Only the like count. Nobody who liked or reshared is listed.
- **Emails and phone numbers are removed (common formats; unusual or short local numbers can slip through).** If a post text, an article title or an author name contains an email address, it is replaced with `[email redacted]`; phone numbers in post text and titles become `[phone redacted]`. Ordinary links are left alone; the address or number inside a `mailto:`, `tel:` or `wa.me` link is replaced.
- Nothing behind a login: no private profiles, no "contact info", no connections or follower lists, no search results, no feed.

### Inputs

- `profiles`: person profile URLs or slugs (`https://www.linkedin.com/in/satyanadella`, `satyanadella`).
- `companies`: company page URLs or slugs (`https://www.linkedin.com/company/stripe`, `stripe`).
- `postUrls`: single post URLs (`linkedin.com/posts/...` or `linkedin.com/feed/update/urn:li:activity:...`). One row each, with the details.
- `includeArticles` (default off): adds the articles shown on a person profile as rows with `type: article`.
- `includeDetails` (default off): one extra request per post for `commentCount`, media type and links, and the author's follower count.
- `postedAfter`: `YYYY-MM-DD` (UTC), keeps only posts published on or after that day. It is applied before the detail requests and the cap, and also to `postUrls`. Posts without a date are dropped when it is set.
- `maxPosts`: 1 to 5000 (default 500), counted in rows.
- `proxyConfiguration`: default is the Apify datacenter proxy, which is what the actor was tested with.

At least one of `profiles`, `companies` or `postUrls` is needed. Order of work: all profiles, then all companies, then all post URLs. A post that appears twice (for example on a profile and in `postUrls`) is returned once, from the first source; the duplicate in `postUrls` costs no request. Duplicate inputs are collapsed.

### Details (`includeDetails`)

Every post needs one more request, so the run is slower. If the detail page of a post cannot be read, the row is **still delivered** with the values from the list page and `detailStatus` `failed` (or `blocked` if LinkedIn blocked that request); it is charged like any other row. If the run ends early (see "Run summary"), the posts whose detail page was not reached are delivered the same way. Articles have no detail page and stay `not_requested`.

### Speed and memory (measured 2026-10-20, Apify platform, default datacenter proxy)

- Requests go out one at a time with a 2 second pause.
- Example input, 3 profiles and 2 companies, no details: 26 posts from 5 requests in 22.8 s at 512 MB and in 17.8 s at 1024 MB. Peak memory 67 MB (512 MB run) and 97 MB (1024 MB run). Recommended memory: 512 MB.
- Same input with `includeDetails` and `maxPosts` 10: 10 posts from 12 requests in 40.8 s at 512 MB, peak memory 120 MB.
- Two single post URLs: 5.6 s at 512 MB.
- Large runs: the actor stops starting new requests shortly before the Apify run timeout (25% of the timeout, at most 60 s), saves the rows it has and ends normally. Raise the run timeout for big `includeDetails` runs. Checked on 2026-10-20 with an oversized input (3 profiles, 2 companies, `includeDetails`, `maxPosts` 5000) and the run timeout set to 45 s: the run ended `SUCCEEDED` after 38.8 s with `terminationReason` `time_budget`, `complete` false and 8 rows kept (5 with details, 3 whose detail page was not reached).

### If LinkedIn says no

- A refusal (HTTP 429, 403, other 4xx, or a sign-in wall) is recorded in `FAILURES` and **never retried**. The actor does not rotate proxies, change headers or try to get around it. If more than 20% of the requests so far are refused (checked after every 10 requests) the run stops with a clear message and keeps the rows already written.
- HTTP 999 is a non-standard LinkedIn status. It may mean that the profile does not exist, that the member hides the public view, or that the request was refused; LinkedIn does not say which. It is recorded as `unavailable` and never retried. Ten 999 answers in a row stop the run.
- In our test on 2026-10-20 a profile name that does not exist came back as 999 (`unavailable`) and a company that does not exist as 404 (`not_found`); the other input in the same run was read normally.
- Server errors and network errors are retried twice (after 5 s and 15 s), then recorded as `request_failed`. One redirect to another `/in/`, `/company/` or `/posts/` page is followed.
- Failed inputs are never charged: only rows are charged.

### Run summary and failures

- `RUN_SUMMARY` (key-value store): `complete`, `terminationReason` (`finished`, `max_results`, `charge_limit`, `time_budget`, `blocked`, `request_failed`), `rowsPushed`, plus `requests`, `blocked`, `unavailable` and `noPostsOnPage` (pages that were readable but where no posts were found in their data).
- `FAILURES` (key-value store): one entry per input or detail request that did not work: `input`, `kind` (`profile`, `company`, `post`), `status`, `httpStatus`. Statuses: `unsupported_input`, `not_found` (404), `unavailable` (999), `blocked`, `no_profile_data` (HTTP 200 but no profile, company or post data on the page), `redirect_unresolved`, `request_failed`.
- `complete` says whether the run reached the end of your inputs (or your `maxPosts`). Inputs that failed are listed in `FAILURES` and do not change it, except `request_failed`.

### Example row (Stripe company post, from a `postUrls` run on 2026-10-20; text and media links shortened here only)

```json
{
  "postUrl": "https://www.linkedin.com/posts/stripe_fundraise-directly-into-your-stripe-treasury-activity-7516531847267311616-ltsY",
  "activityId": "7516531847267311616",
  "type": "post",
  "authorName": "Stripe",
  "authorUrl": "https://www.linkedin.com/company/stripe",
  "authorType": "company",
  "authorFollowers": 1776660,
  "datePublished": "2026-10-15T16:14:02.796Z",
  "title": null,
  "text": "Fundraise directly into your Stripe Treasury account. Deposits are automatically matched to each SAFE. ...",
  "likes": 27,
  "commentCount": 24,
  "mediaType": "video",
  "imageUrl": null,
  "videoUrl": "https://dms.licdn.com/playlist/vid/v2/D4E05AQHXFm_iyt9J7A/...",
  "videoThumbnailUrl": "https://media.licdn.com/dms/image/v2/D4E05AQHXFm_iyt9J7A/...",
  "videoDuration": "PT23S",
  "detailStatus": "ok",
  "source": "https://www.linkedin.com/posts/stripe_fundraise-directly-into-your-stripe-treasury-activity-7516531847267311616-ltsY",
  "input": "https://www.linkedin.com/posts/stripe_fundraise-directly-into-your-stripe-treasury-activity-7516531847267311616-ltsY",
  "scrapedAt": "2026-10-20T02:59:12.691Z"
}
```

Counts such as likes and followers change by the minute.

### Good for

linkedin posts scraper, linkedin post scraper, linkedin company posts, linkedin profile posts, linkedin activity, linkedin content monitoring, linkedin no cookies, social listening linkedin, competitor posts, thought leadership tracking.

Use of the data is your responsibility; check LinkedIn's terms and the laws that apply to you.

# Actor input Schema

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

e.g. https://www.linkedin.com/in/satyanadella or satyanadella. Returns the posts LinkedIn shows on the public profile (2 to 10, usually 6 to 9).

## `companies` (type: `array`):

e.g. https://www.linkedin.com/company/stripe or stripe. Returns the posts shown on the public company page (2 to 10, usually 6 to 9).

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

linkedin.com/posts/... or linkedin.com/feed/update/urn:li:activity:... One row with full details per URL.

## `includeArticles` (type: `boolean`):

Adds up to 10 newsletter/Pulse articles per profile as rows with type 'article'.

## `includeDetails` (type: `boolean`):

Adds comment count, media type, image or video URL and author followers. One extra request per post, so runs are slower.

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

YYYY-MM-DD (UTC). Leave empty for all posts shown on the page. Use with a daily schedule to collect only new posts.

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

Hard cap 5000.

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

Apify Proxy settings. The default shared datacenter proxy is what this actor was tested with.

## Actor input object example

```json
{
  "profiles": [
    "https://www.linkedin.com/in/satyanadella",
    "williamhgates"
  ],
  "companies": [
    "stripe"
  ],
  "includeArticles": false,
  "includeDetails": false,
  "postedAfter": "",
  "maxPosts": 500,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

One row per post or article: author, date, text, likes and, with details on, comment count and media.

## `runSummary` (type: `string`):

What the run wrote and how it ended (the RUN_SUMMARY record in the default key-value store).

# 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": [
        "https://www.linkedin.com/in/satyanadella",
        "williamhgates"
    ],
    "companies": [
        "stripe"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("mochiboo/linkedin-posts-public-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": [
        "https://www.linkedin.com/in/satyanadella",
        "williamhgates",
    ],
    "companies": ["stripe"],
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("mochiboo/linkedin-posts-public-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": [
    "https://www.linkedin.com/in/satyanadella",
    "williamhgates"
  ],
  "companies": [
    "stripe"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call mochiboo/linkedin-posts-public-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,mochiboo/linkedin-posts-public-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/8cMwryGPL6WtMAPPy/builds/nrsDSEYSGuH5c0xhX/openapi.json
