# LinkedIn Company Scraper: Details, Posts & Jobs (`danthedataman/linkedin-company-activity-ledger`) Actor

Scrape LinkedIn company pages without login: stated employee and follower counts, About, office addresses, founded year when shown, recent posts and guest job cards.

- **URL**: https://apify.com/danthedataman/linkedin-company-activity-ledger.md
- **Developed by:** [Eli J](https://apify.com/danthedataman) (community)
- **Categories:** Lead generation, Social media, Jobs
- **Stats:** 2 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.80 / 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.
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 scraper: profiles, posts and jobs

Use this LinkedIn company scraper without login or cookies to export LinkedIn's stated employee and follower counts, About text, office addresses, founded year when shown, about ten recent company posts and open job cards. Input is company page URLs or vanity names.

Join company, post and job rows by `organizationId` and `vanityName`. Select a previous snapshot to find post and job identities absent from that dataset; company facts are not filtered. With `cacheMaxAgeHours` set, rows can come from a page read up to that many hours earlier; `scrapedAt` says when.

Posts are the public company root's recent Updates, not full history. No person profiles, employee lists, comment text or commenter/reactor identities are returned.

### Try the prefilled company pages

The form prefills Microsoft and Google with facts, posts and up to two job cards each; retained prefill runs returned 26 rows. Row counts depend on what LinkedIn delivers. API calls do not inherit the prefill: an empty `companies` input exits successfully and makes no source requests.

### LinkedIn company scraper input

```json
{"companies":["microsoft","google"],"includeFacts":true,"includePosts":true,"includeJobs":true,"maxJobsPerCompany":2}
```

| Field | Type | Default | What it does |
| --- | --- | --- | --- |
| `companies` | array | none | Company root URLs or vanity names. The example companies are prefill only. Omitted, null or \[] exits successfully with guidance and makes no source requests. |
| `includeFacts` | boolean | true | Emit company facts. Null means true. |
| `includePosts` | boolean | true | Emit the public root Updates slice. Null means true. |
| `includeJobs` | boolean | true | Read guest job cards filtered by the root's public organization ID. Null means true. |
| `maxJobsPerCompany` | integer | 25 | Maximum distinct job cards inspected per company, including jobs already in a baseline. Prefill is 2. Zero skips jobs; facts and posts can still run. |
| `cacheMaxAgeHours` | integer | none | Reuse pages this Actor read in your earlier runs while they are younger than this many hours (0-720), kept in the `linkedin-company-activity-ledger-cache` key-value store it creates in your own account. Omitted, null or 0 reads every page live and leaves the store unopened. Rows from the cache carry `cached: true` and the `scrapedAt` of the live read that stored the page; expiry counts from that time. If the store cannot be used, the run logs a warning and reads live. A root whose Updates are absent is not stored in the cache. The cache saves time, not money; cached rows are billed like live rows. |
| `maxRunSeconds` | integer | 210 | Run time budget, counted from the Actor's start (after the container pull). Once it is reached, no further two-company batch starts; the batch in progress, including its job pages, finishes first. Companies not started are listed in ERRORS and are not charged. Slow reads can still reach the run timeout. 0 disables it; raise the run timeout as well before raising or disabling it. |
| `baselineDatasetId` | string | none | Read-only dataset picker for a previous full snapshot from this Actor. When supplied, posts/jobs emit only identities absent from that dataset, marked `new`. Company facts are not filtered. Null means snapshot mode. |

All three toggles off makes no requests. Duplicate vanity inputs, including equivalent URL/name pairs, are processed once. Only HTTPS company root URLs are accepted; `/posts/`, `/about/`, person URLs and other hosts are unsupported.

### Company, post and job output

The dataset contains `company`, `post` and `job` records. Fields for a different record type are absent. Missing scalar source values are null and missing lists are empty. Company counts are integers in people, as LinkedIn states them; post engagement stays display text, with its source abbreviations and labels. No abbreviated count or absolute time is inferred.

| Field | What it is |
| --- | --- |
| `recordType` | `company`, `post` or `job`. |
| `organizationId`, `vanityName` | Public organization ID (string) and canonical input vanity, on every row. |
| `sourceUrl` | Company root URL, on every row. |
| `name`, `industry`, `sizeBand`, `headquarters`, `website`, `specialties` | Labeled company facts as strings. `sizeBand` retains the employee band and its units. |
| `description`, `tagline`, `companyType` | About text, the Organization JSON-LD slogan when present, and the labeled company type. Strings or null. |
| `employeeCount`, `followerCount`, `followerCountText` | Employee and follower counts as integers, parsed from the full figures LinkedIn prints, plus the original top-card subline with the follower count and sometimes headquarters. These are LinkedIn's stated counts, not independently verified workforce totals. Abbreviated follower text leaves the integer null. |
| `foundedYear` | The labeled Founded calendar year as an integer, or null when absent. |
| `headquartersAddress` | JSON-LD address object with stated `streetAddress`, `addressLocality`, `addressRegion`, `postalCode` and `addressCountry`; absent components stay absent. Postal codes remain strings. |
| `locations` | Every office address delivered in the root, including entries hidden by its show-more control. Each object contains `addressLines` (ordered strings) and `isPrimary` (boolean). No city, state or postal components are guessed from an unstructured line. This is what the page lists, not a verified inventory of every office. |
| `affiliatedPages`, `similarPages` | Delivered company cards: `name`, `url`, `industry`, `headquarters` and `logoUrl`. These linked pages are not fetched. |
| `logoUrl`, `coverImageUrl`, `jobSearchUrl` | Delivered company image URLs and the displayed jobs link. The jobs link can include affiliates; extraction uses the company's own organization ID instead. |
| `funding` | Labeled funding text when a logged-out page shows it, otherwise null. No measured logged-out root showed funding, including Stripe, Anthropic and Clay. Funding behind login is unavailable. |
| `postUrl`, `postUrn` | Visible post link when present and the root card's company activity URN. On a repost, the link can point to the original activity while the URN identifies the company's repost. |
| `postedAgeText`, `postedAt` | Displayed age and, only when stated by the page, its date/date-time string. A job may state only a calendar date. |
| `text`, `reactionCountText`, `commentCountText`, `repostCountText` | Root-card post text and displayed aggregate counts. Text may be truncated or include repost framing. No repost count was established; that field is null. |
| `hasMedia`, `hasLink` | Whether the post card has a recognized image/video container or a link inside its text. Avatars and reaction icons do not count as media. |
| `jobId`, `title`, `location`, `jobUrl` | Guest job card ID, title, displayed location and public job URL. Tracking query parameters are removed. |
| `changeStatus` | `new` on posts/jobs emitted in baseline mode; absent on snapshot rows and company facts. |
| `scrapedAt`, `cached` | UTC time (`YYYY-MM-DDTHH:MM:SSZ`) of the live read of the row's page, and whether the row came from your cache. A cache hit keeps that `scrapedAt`; a page re-read live gets a new one. |

Selected fields from a retained local facts-only run on October 10, 2026:

```json
{"recordType":"company","organizationId":"1035","vanityName":"microsoft","sourceUrl":"https://www.linkedin.com/company/microsoft/","name":"Microsoft","industry":"Software Development","sizeBand":"10,001+ employees","headquarters":"Redmond, Washington","employeeCount":231350,"companyType":"Public Company","foundedYear":null,"followerCountText":"Redmond, Washington 29,264,616 followers","followerCount":29264616,"funding":null}
```

### Monitor new company activity

Run once without a baseline, then select that run's dataset as `baselineDatasetId` in a subsequent run with the same companies and job cap. `new` means an identity was absent from the selected snapshot, not that its publication time falls after the earlier run. Edited text or counts on the same identity do not emit `changed` rows. Missing posts/jobs do not emit closed/deleted rows.

Keep a full snapshot as the baseline. A monitor delta is incomplete and is rejected when it contains monitor rows. An empty baseline treats every visible post/job as new. A smaller prior job cap or a newly added company can likewise make existing activity appear new relative to that snapshot. Older posts outside the root slice are not searched.

### Pricing and billing units

Pay per event, priced per 1,000 results. Each company, post or job record is one result; the two-company prefill returns about 26. Unchanged posts/jobs in baseline mode and ERRORS records are not charged. An Actor Start event applies per run as shown on the listing. When the maximum charge binds, the Actor stops source reads.

### Limits, reliability and errors

Company facts come from the logged-out root. Founded and funding stay null when absent; the measured Stripe, Anthropic and Clay roots showed no funding section. The public Updates slice does not provide older history. `/posts/` and `/about/` redirect to login when logged out and are never read. The Actor never signs in. Phone numbers are not printed on the logged-out company pages we measured and are not returned. Employee counts and size bands are not employee-distribution statistics; those sit behind login. Verified, showcase and paid-page flags are not stated on the root and are not returned.

The browserless path is primary, with isolated headless Playwright as the default fallback after exhausted HTTP retries for blocks, login redirects, missing required markers or connection failures. A definitive source answer, including HTTP 400, HTTP 404 or an empty jobs page, prevents fallback; the final failure must be a block or network class. HTTP 403, 429 and 999 are block classes. Transient HTTP 500–599 responses retry and enter the browser only if a later final attempt is a block or network failure. Both paths use the same parsers and row contract. A private environment diagnostic can force the browser; there is no input toggle. The run summary logs browser fallback read attempts, and each HTTP page attempt logs one timing line with its request type, attempt, status, session setup, pacing wait and request seconds, without URLs. Allocate at least 512 MB for runs that need a browser; a forced-fallback run at 512 MB peaked at 385 MB.

Both routes use anonymous datacenter proxies, bounded retries and pacing; no residential route is enabled. They request only the company root and company-filtered guest jobs, follow server redirects as an anonymous visitor would, and check the final URL and source identity before parsing. A login destination is a block: nothing from it is parsed or emitted and no follow-up navigation occurs. Affiliated/similar links remain data without being visited.

Guest jobs are a bounded open-job-card slice, not a guarantee of every vacancy, ordering or coverage in every region. An empty page ends pagination. A repeated page stops it, retains earlier jobs and records the partial failure in ERRORS.

A company that still resolves to login or 404 contributes no dataset rows and writes a reason to the run's **ERRORS** key-value record. If its public Updates are missing or unreadable (no Updates, or an Update card without its company activity ID or with an unexpected post URL), that no longer stops the company: requested facts and jobs are delivered as usual, and when posts were requested, **ERRORS** records them as absent with that reason. A page served from the cache is not re-checked live. A later jobs failure retains valid facts/posts and earlier jobs. Errors never become billable dataset rows. If every attempted company fails and no result is delivered, the run fails; valid empty inputs succeed with guidance.

If ERRORS cannot be written, the run logs a short warning and still stops source reads when the maximum charge binds.

Per company, from owner runs on 2026-10-23 at 512 MB: about 4 to 9 s with the prefill's two job cards and about 14 s with the default 25. By inference from the 20-company run, each page that needs the browser fallback adds about 20 s. At those rates the default 210-second `maxRunSeconds` covers roughly 15 companies with default job cards and 25 or more with two; past the budget no further batch starts, and the rest are listed in ERRORS. Run time includes container start and the Apify SDK import, about 1.4 s at the median. In 20 two-company facts-only runs at 512 MB the median run took 3.9 s. The first runs after a new build can spend 13 to 18 s pulling the container image. One of 40 measured facts-only runs spent 17.8 s fetching pages; its cause was not identified. With `cacheMaxAgeHours` set, five one-company facts-only runs at 512 MB on 2026-10-19 took a median 5.9 s from the API call to the dataset read when the page was read live, and five more took a median 2.9 s when it came from the cache.

### Help with a company run

Check the run log and ERRORS record for unreadable companies or partial jobs. When reporting an issue, include the run ID and the company page URL. Never send credentials or cookies.

# Changelog

This Actor's version history is a separate document: https://apify.com/danthedataman/linkedin-company-activity-ledger/changelog.md

# Actor input Schema

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

LinkedIn company root URLs or vanity names. Omitted, null or \[] makes no source requests.

## `includeFacts` (type: `boolean`):

Include logged-out About text, exact employee/follower counts (people), type, founded year, delivered offices and related company pages. Omitted or null means true.

## `includePosts` (type: `boolean`):

Include public company-root Updates, about ten recent cards, not full history. Omitted or null means true.

## `includeJobs` (type: `boolean`):

Include guest job cards filtered by the public organization ID. Omitted or null means true.

## `maxJobsPerCompany` (type: `integer`):

Maximum current job identities inspected per company; also bounds emitted jobs. Omitted or null means 25. Zero skips jobs; facts/posts still run.

## `cacheMaxAgeHours` (type: `integer`):

Reuse pages this Actor read in your earlier runs, kept in a key-value store it creates in your own account, while they are younger than this many hours. Omitted, null or 0 reads every page live. Cached rows carry cached true and their original scrapedAt, and are charged like live rows.

## `maxRunSeconds` (type: `integer`):

Stop starting new companies after this many seconds, counted from the Actor's start. The two-company batch in progress, including its job pages, finishes first; companies not started are listed in ERRORS and are not charged. Omitted or null means 210, which keeps typical long lists inside Apify's 300-second default run timeout; slow reads can still reach it. 0 disables the budget; raise the run timeout as well before raising or disabling it.

## `baselineDatasetId` (type: `string`):

Optional previous full snapshot dataset from this Actor. Read only. Emit only post/job identities absent from it, marked new; facts stay current. Use the same companies and job cap; monitor deltas are not full baselines. Omitted or null means snapshot mode.

## Actor input object example

```json
{
  "companies": [
    "microsoft",
    "google"
  ],
  "includeFacts": true,
  "includePosts": true,
  "includeJobs": true,
  "maxJobsPerCompany": 2
}
```

# Actor output Schema

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

Company facts with exact counts in people, public root Updates and bounded guest job cards. Browser fallback reads are counted in the run log summary.

## `errors` (type: `string`):

Handled request failures, when present; never result 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 = {
    "companies": [
        "microsoft",
        "google"
    ],
    "includeFacts": true,
    "includePosts": true,
    "includeJobs": true,
    "maxJobsPerCompany": 2
};

// Run the Actor and wait for it to finish
const run = await client.actor("danthedataman/linkedin-company-activity-ledger").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",
        "google",
    ],
    "includeFacts": True,
    "includePosts": True,
    "includeJobs": True,
    "maxJobsPerCompany": 2,
}

# Run the Actor and wait for it to finish
run = client.actor("danthedataman/linkedin-company-activity-ledger").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",
    "google"
  ],
  "includeFacts": true,
  "includePosts": true,
  "includeJobs": true,
  "maxJobsPerCompany": 2
}' |
apify call danthedataman/linkedin-company-activity-ledger --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,danthedataman/linkedin-company-activity-ledger"
        }
    }
}
```

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/hJPgYXtrwyXwSk6SZ/builds/9akzbnVkE0iUaWjlh/openapi.json
