# LinkedIn Company Posts Scraper (Public Company Feed) (`webdatatools/linkedin-company-posts-scraper`) Actor

Scrape the recent posts on public LinkedIn company pages without login: post text, posted time, reactions, comments, media, external links and post URL. Company feed only, no people profiles or commenters.

- **URL**: https://apify.com/webdatatools/linkedin-company-posts-scraper.md
- **Developed by:** [Murat Uzun](https://apify.com/webdatatools) (community)
- **Categories:** Marketing, Lead generation, Other
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.50 / 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

## LinkedIn Company Posts Scraper (Public Company Feed)

### What is LinkedIn Company Posts Scraper?

LinkedIn Company Posts Scraper reads the recent posts shown on public LinkedIn company pages, by URL or slug, and returns one row per post: text, posted time, reactions, comments, media URLs, outbound links and the post URL. It runs without a LinkedIn login and reads only the public company page (the guest view). It does not read people profiles, commenter or reactor lists, or email addresses.

### What data does LinkedIn Company Posts Scraper extract?

| Field | Type | Source on the public page |
|---|---|---|
| `companyName` | string | Company title |
| `postUrl` | string | Post link (`linkedin.com/posts/...`), else the `feed/update` link built from the post id |
| `postId` | string | Activity id in the card (`urn:li:activity:...`) |
| `postedAt` | string (ISO) | **Derived** from the activity id (id shifted right 22 bits = epoch ms). Not printed on the page; checked against the relative time below |
| `postedAgo` | string | Relative time on the card, e.g. `4h`, `1d`, `2w` (the "Edited" marker is removed) |
| `authorName`, `authorType` | string | Name and `company` on a company-authored post (the author link is not returned) |
| `text` | string | Post text as shown in the card (LinkedIn may cut long posts at "see more") |
| `reactions`, `comments` | number | Reaction and comment counts on the card |
| `hasVideo` | boolean | A native video is on the card (the video URL is not returned) |
| `mediaCount`, `mediaUrls` | number, array | Image URLs in the post (profile photos excluded) |
| `externalLinks` | array | Outbound links in the post text or article card (LinkedIn redirects unwrapped, `trk` tracking parameter removed). `lnkd.in` short links are kept as LinkedIn gives them |
| `slug`, `url`, `source`, `scrapedAt` | string | Company identity, source tag and UTC scrape time |

Fields the page does not show come back as `null` or `[]`. They are never guessed.

**Scope limits (stated plainly):**

- The public company page shows about the **10 most recent** posts. The scraper cannot read older posts without a login, so it returns at most that many per company per run.
- Posts written by a **person** (for example a CEO post shown on the company page) are skipped and counted in the run log as `personalSkipped`. The actor returns company-authored posts only. On the Microsoft page on 2026-10-23, 2 of 10 items were company-authored and 8 were person-authored.
- `postedAt` is derived from the id, not shown by LinkedIn. It matched the relative time on every checked row (for example `4h` and `1w` cards), but this is a check on a sample, not a guarantee.
- Reactions and comments are the counts shown at scrape time, not final values.

### How to use LinkedIn Company Posts Scraper

1. Open the actor in Apify Console and paste company URLs or slugs into **Companies**, for example `https://www.linkedin.com/company/openai` or `openai`.
2. Separate several companies with new lines or `;`. Up to 100 companies per run.
3. Leave **Proxy mode** on `auto`. The run uses direct requests and switches to the Residential proxy only if LinkedIn blocks them.
4. Start the run and download the dataset as JSON, CSV or Excel.
5. Set a **max total charge** if you want a cap. The run stops before it exceeds the cap; one charged item is one post.

### Example input

```json
{
    "companies": ["openai", "https://www.linkedin.com/company/vercel"],
    "proxyMode": "auto"
}
```

### Example output

One row from a real cloud run (run `Puqe9Beq9Jox1YgvL`, 2026-10-23, Apify datacenter, direct, no proxy). The text is shortened here:

```json
{
    "slug": "openai",
    "companyName": "OpenAI",
    "postUrl": "https://www.linkedin.com/feed/update/urn:li:activity:7518671289675603968/",
    "postedAt": "2026-10-21T13:55:25.616Z",
    "postedAgo": "1d",
    "authorName": "OpenAI",
    "authorType": "company",
    "text": "OpenAI models are coming to Google Cloud. We're partnering with Google Cloud to bring OpenAI's frontier models and Codex agentic workflows i…",
    "reactions": 3375,
    "comments": 171,
    "hasVideo": false,
    "mediaCount": 1,
    "externalLinks": ["https://lnkd.in/etZTxJrq"]
}
```

### Test results

- Cloud run `Puqe9Beq9Jox1YgvL` (2 inputs: `openai`, `vercel`): status SUCCEEDED, 20 rows, 0 problems, 0 person-authored items skipped.
- Default input (demo `microsoft`), smoke test on 2026-10-23: SUCCEEDED, 2 items (the other 8 items on that page are person-authored and skipped).
- Unit tests (`npm test`) run on the saved guest-page HTML of `microsoft`, `openai` and `vercel`, fetched 2026-10-23 with the same URLs the actor uses.

### Input parameters

| Parameter | Type | Default | Description |
|---|---|---|---|
| `companies` | array | `["microsoft"]` | Company URLs or slugs. Other input is skipped and reported in the log. |
| `proxyMode` | string | `auto` | `auto`, `never` (direct only) or `always` (Residential from the start). |

### Pricing

Pay per post: one charged event per post written to the dataset. The actor also pays a small per-run start event.

| Plan | Price per post (USD) |
|---|---|
| FREE, BRONZE | 0.0020, 0.0018 |
| SILVER | 0.0016 |
| GOLD, PLATINUM, DIAMOND | 0.0015 |

The platform stored the GOLD, PLATINUM and DIAMOND tiers at 0.0015 (the requested 0.0014 and 0.0013 were not kept; checked in the monetization response, not in the Console).

Cost side: direct runs of 2 companies took a few seconds at 512 MB. Compute cost per run was not measured (no billing read on the run). The estimate from run duration is far below the lowest tier price, so the margin is well above 70 percent for direct runs. Residential proxy use (in `auto` after blocks) would add cost and is not measured here.

### FAQ

**Does it need a LinkedIn account?** No. It reads only the public guest page.

**Why do I get fewer than 10 posts for a company?** The public page shows at most about 10 posts, and person-authored posts are skipped. Check the run log for `personalSkipped`.

**Can it read comments or who reacted?** No. Commenter and reactor lists and people profiles are out of scope.

# Actor input Schema

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

LinkedIn company page URLs or slugs, e.g. `https://www.linkedin.com/company/microsoft` or `microsoft`. The public page shows about the 10 most recent posts.

## `proxyMode` (type: `string`):

auto: direct first, Apify Residential proxy only if LinkedIn blocks. never: direct only. always: Residential from the start.

## Actor input object example

```json
{
  "companies": [
    "microsoft"
  ],
  "proxyMode": "auto"
}
```

# Actor output Schema

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

All posts from this run.

# 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 = {
    "companies": [
        "microsoft"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("webdatatools/linkedin-company-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 = { "companies": ["microsoft"] }

# Run the Actor and wait for it to finish
run = client.actor("webdatatools/linkedin-company-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 '{
  "companies": [
    "microsoft"
  ]
}' |
apify call webdatatools/linkedin-company-posts-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,webdatatools/linkedin-company-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/DZUgEqt7NWObBqdMB/builds/eKOQ177IBJ2euadVD/openapi.json
