# LinkedIn Company Scraper & Change Monitor (No Login) (`kizukidata/linkedin-company-monitor`) Actor

Scrape public LinkedIn company pages: one row per company with name, About, industry, employee band, website, followers and founded year when shown. Use it to watch accounts. Only changes returns only changed companies, with old and new values. No employees or posts. Not affiliated with LinkedIn.

- **URL**: https://apify.com/kizukidata/linkedin-company-monitor.md
- **Developed by:** [Kizuki](https://apify.com/kizukidata) (community)
- **Categories:** Social media, Business, Lead generation
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.20 / 1,000 companies

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 & Change Monitor (No Login)

**LinkedIn Company Scraper & Change Monitor** reads public LinkedIn **company** overviews as any visitor sees them and returns one clean row per company — name, About summary, industry, organisation type, employee band, headquarters, founded year, website, follower count and specialties — as **JSON or CSV**. With **`onlyChanges`** it becomes a monitor: a scheduled run returns **only the companies whose fields changed**, with the old and the new value of each field — follower count, employee band, summary, industry, headquarters, founded year, website, type and specialties. Under **`onlyChanges`**, a company with nothing new produces no row and no charge.

It needs **no login, no account and no cookie**, runs no browser and executes no JavaScript on the page. Pages it could not read come back as a row with a fixed reason and are **not charged**, so a wall or a timeout never looks like "no change".

This Actor is an independent tool. It is **not affiliated with, endorsed by or sponsored by** LinkedIn or Microsoft. Try it with an empty input: it reads the sample company `apify` and returns its overview.

### Use cases

- **Competitor watch.** Follow a list of companies and get a row only when followers, headcount band, description or industry move.
- **Account research.** One row per company with industry, size, headquarters, website and specialties, ready for a CRM or a sheet.
- **Market mapping.** Build a table of companies in one industry and compare employee bands and specialties.
- **Sales triggers.** Use the follower jump or the employee-band change as the trigger for an alert, not a full re-read of every company.
- **Agent research.** Let an assistant pull a company's public profile, and only what changed since its last run.

### What data can you get?

| Field | Description |
|---|---|
| `id`, `type`, `source` | Row ID (a hash of the slug), `company`, and the source label |
| `status` | `ok` for successfully read companies, or `failed` for uncharged status rows |
| `slug`, `url` | The company slug and the page that was read (`https://www.linkedin.com/company/<slug>/`) |
| `name` | Company name as on the page |
| `description` | The About us summary, up to 5,000 characters, as the company wrote it |
| `industry` | The industry LinkedIn lists |
| `companyType` | Organization type as shown (`Public Company`, `Privately Held`, …) or null |
| `companySize`, `employeesMin`, `employeesMax` | The employee band as shown (`10,001+ employees`) and its numeric bounds (`max` is null for an open band) |
| `headquarters` | Headquarters as shown (`Mountain View, CA`) |
| `foundedYear` | The year the page says the company was founded, or null (many pages do not show it) |
| `website` | The company's own website, unwrapped from LinkedIn's redirect link |
| `followers` | Followers on LinkedIn at the time of the run |
| `specialties` | The specialties the page lists (up to 30) |
| `capturedAt` | When the run read the page |
| `changes` | With `onlyChanges`: `{baseline, previousCapturedAt, changedFields, diffs: [{field, from, to}]}`, otherwise null |
| `errorCode`, `errorMessage` | For a status row: `authwall`, `blocked`, `not_found`, `timeout` or `unexpected`, with a fixed sentence; null for a company row |

Every row has every field; a value the page does not show is `null`. Employee lists and personal profiles are **not** part of this Actor.

A company whose page LinkedIn has moved is read at its new address (`mistral-ai` → `mistralai`) and its row keeps the slug you asked for. When the new address belongs to a different company, the row is `not_found` with an explanatory message: that slug no longer names a company, and reading the other company's page would put its data under your slug.

### How to use LinkedIn Company Scraper

1. Put company URLs or slugs into **Companies** (`companies`: `apify`, `https://www.linkedin.com/company/google/`). Up to 100.
2. For a one-off overview, leave **Only changes since the previous run** (`onlyChanges`) off: every named company is returned in full.
3. For monitoring, turn it on, set `maxItems` to bound how many companies one run reads, and schedule the Actor (hourly, daily or weekly). Each run then returns only what changed.
4. Send the rows to your sheet, warehouse, alert or model. A `changes.diffs` entry carries `field`, `from` and `to`.

#### Input example

```json
{
    "companies": ["apify", "https://www.linkedin.com/company/google/"],
    "onlyChanges": true,
    "maxItems": 10
}
```

### Input

| Field | Default | What it does |
|---|---|---|
| `companies` | `["apify"]` | Company slugs or page URLs to read (up to 100). An empty input reads the sample company |
| `onlyChanges` | `false` | Return only companies whose tracked fields changed since the previous run, with the old and new value. The first run for a company returns it in full |
| `maxItems` | `10` | Maximum companies one run reads (1 to 100). A row for a company the Actor could not read counts towards it, so the run never reads more than this many companies. A company it skipped as unchanged does not count, so with `onlyChanges` on, a run may read the first `maxItems` companies and return only the changed ones. The companies after the limit are listed in OUTPUT as unchecked and cost nothing |
| `proxyConfiguration` | Apify residential proxy | Proxy settings (country, groups, custom proxy URLs) |

### Output

One row per company. With `onlyChanges` the row also says what moved: `changedFields` lists the fields and `diffs` carries `field`, `from` and `to`.

#### Output example

Values below are illustrative.

```json
{
    "id": "1f0c2de6a4b39c88",
    "type": "company",
    "source": "LinkedIn public company page",
    "status": "ok",
    "slug": "apify",
    "url": "https://www.linkedin.com/company/apify/",
    "name": "Apify",
    "description": "Thousands of Actors to automate your business, get real-time web data, and equip your AI agents.",
    "industry": "Technology, Information and Internet",
    "companyType": "Privately Held",
    "companySize": "201-500 employees",
    "employeesMin": 201,
    "employeesMax": 500,
    "headquarters": "Prague",
    "foundedYear": 2016,
    "website": "https://apify.com/",
    "followers": 46813,
    "specialties": ["Web scraping", "Browser automation", "AI agents"],
    "capturedAt": "2026-11-06T04:40:00.000Z",
    "changes": {
        "baseline": false,
        "previousCapturedAt": "2026-11-05T04:40:00.000Z",
        "changedFields": ["followers", "companySize"],
        "diffs": [
            { "field": "followers", "from": 46510, "to": 46813 },
            { "field": "companySize", "from": "51-200 employees", "to": "201-500 employees" }
        ]
    },
    "errorCode": null,
    "errorMessage": null
}
```

A company the Actor could not read is a row with `status: "failed"`, its `errorCode` and a fixed `errorMessage` — never a charged row and never a missing one.

### Only changes, run after run

`onlyChanges` keeps one record per company (its tracked field values and when the record was last written) in the named key-value store `state-linkedin-company-<actor id>` of your Apify account. The order matters: a record is written only after the company row was written to the dataset, so a company left out by `maxItems` or by the spending limit is read and returned next run, and a company that was walled or blocked keeps its old record, so the next successful run compares against the last successful one.

- **First run for a company**: `changes.baseline` is `true`, `diffs` is empty, the row carries the full overview.
- **A field changed**: the row is returned, `changedFields` and `diffs` name the fields with the old and new value.
- **Nothing changed**: no row, no charge. The run says so in its status message (`no_new_items`) and OUTPUT counts the unchanged companies.
- **A snapshot cannot be read**: the company is returned without a comparison this run, and no record is written, so a failed read never overwrites a good record.
- Do not run the same input concurrently: two runs can both see the old record and both return the same change.

### How much does it cost?

LinkedIn Company Scraper uses **pay-per-event** pricing: you pay only for the company rows returned, not for the time it runs. At the Free tier 1,000 company rows cost $2.00; at Gold, $1.20.

| Apify plan (discount tier) | Price per 1,000 companies | Price per company |
|---|---|---|
| **Free** | $2.00 | $0.002 |
| **Starter (Bronze)** | $1.80 | $0.0018 |
| **Scale (Silver)** | $1.50 | $0.0015 |
| **Business (Gold)** | $1.20 | $0.0012 |

Apify's Free plan includes USD 5 of monthly usage, which covers about 2,500 companies at the Free tier price,
run start fees aside.

- **Run start fee**: the platform default per run (see the Pricing tab).
- **1 company row = 1 `company` event.** A status row (Authwall, refusal, 404, timeout, unreadable page) is written for visibility and is **not charged**.
- With `onlyChanges`, an unchanged company is not written and **not charged**, so monitoring unchanged profiles incurs zero row cost.

### Use with AI agents (MCP)

LinkedIn Company Scraper works as a tool for AI agents such as Claude, Cursor and n8n agents through the Apify MCP server. Load only this Actor with this server URL:

```text
https://mcp.apify.com?tools=kizukidata/linkedin-company-monitor
```

- **Claude Code**: run `claude mcp add apify "https://mcp.apify.com?tools=kizukidata/linkedin-company-monitor" -t http`.
- **Claude (web or desktop app)**: open **Customize → Connectors**, choose **Add custom connector**, name it `Apify` and paste the server URL above.
- **Cursor**: add the server to `~/.cursor/mcp.json` (or `.cursor/mcp.json` in a project):

```json
{
    "mcpServers": {
        "apify": { "url": "https://mcp.apify.com?tools=kizukidata/linkedin-company-monitor" }
    }
}
```

- **n8n**: add the Apify node (on self-hosted n8n, install the community package `@apify/n8n-nodes-apify`) and choose **Run an Actor and get dataset**; for an n8n AI Agent, add **Apify Tool** as a tool.

The server signs you in to Apify with OAuth. A client that cannot use OAuth can send your Apify API token in an `Authorization: Bearer <APIFY_TOKEN>` header instead.

#### What to send from an agent

Pass a small `companies` array (the default is the sample company) and keep `maxItems` small so the call returns inside the agent's timeout. Each row is a self-contained company overview, and with `onlyChanges: true` it also says what changed since the last run.

### Integrations and API

Run the Actor from your own code or from an automation tool. Replace `<APIFY_TOKEN>` with your API token.

#### Zapier, Make and n8n

Use the Apify integration, choose **Run Actor**, and pass `companies` with `onlyChanges: true`. Add **Get dataset items**. Run the workflow on a schedule: a run with no changes returns no items, so the next step simply does not fire. Map `slug`, `followers` and `changes.diffs` to Slack, Airtable, Google Sheets or your CRM.

#### LangChain

Use `description` as `page_content` and `name`, `slug`, `industry`, `companySize`, `headquarters`, `followers` and `url` as metadata. `ApifyDatasetLoader` from the Apify LangChain integration works with the run's dataset ID; load only the changed companies by running with `onlyChanges: true`.

#### cURL

```bash
curl -X POST "https://api.apify.com/v2/acts/kizukidata~linkedin-company-monitor/runs" \
  -H "Authorization: Bearer <APIFY_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"companies":["apify","google"],"onlyChanges":true,"maxItems":10}'
```

Poll `GET https://api.apify.com/v2/actor-runs/<RUN_ID>` for the status, read `defaultDatasetId`, then fetch `GET https://api.apify.com/v2/datasets/<DATASET_ID>/items?format=json`.

#### Python

```python
from apify_client import ApifyClient

client = ApifyClient("<APIFY_TOKEN>")
run = client.actor("kizukidata/linkedin-company-monitor").call(run_input={
    "companies": ["apify", "google"],
    "onlyChanges": True,
    "maxItems": 10,
})
for row in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(row["slug"], row["followers"], [d["field"] for d in (row["changes"] or {}).get("diffs", [])])
```

#### JavaScript

```js
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: '<APIFY_TOKEN>' });
const run = await client.actor('kizukidata/linkedin-company-monitor').call({
    companies: ['apify', 'google'],
    onlyChanges: true,
    maxItems: 10,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.map((row) => `${row.name}: ${row.followers} followers`));
```

### Limitations

- **Company pages only.** Employee lists, personal profiles, posts, comments and jobs are not read, and nothing is written to LinkedIn.
- **Paced reads.** Requests are spaced and retried automatically; if several companies in a row are refused, the run stops and lists the companies it did not reach in OUTPUT `uncheckedInputs`.
- `foundedYear` is null on many pages; the Actor does not guess.
- The summary is the company's own text, up to 5,000 characters; it is returned as written, not summarised.
- The employee band is LinkedIn's own band, not a headcount; `employeesMin`/`employeesMax` are the band bounds.
- A guest page can answer with the sign-in wall (Authwall) at any time. The Actor retries twice and then reports it; it never logs in. A wall row is not charged, so if it keeps coming back, run again later.
- Two runs of the same input at the same time can both report the same change.
- **Changes on the page.** The reader extracts structured fields from the public page. If LinkedIn changes them, rows come back as `unexpected` (not charged) and the Actor needs an update; report it in the Issues tab.
- Each run writes an `OUTPUT` record with the counts, the inputs it skipped or did not reach, and a `linkedinCompany` summary (companies requested, read, failed, rows returned, changed, baseline, unchanged).

### FAQ

#### Why is `onlyChanges` off by default?

Without it the Actor is a plain overview reader: every company you name comes back in full. Turn it on for scheduled monitoring, where the point is to hear about changes only.

#### Why did a company come back with `errorCode: "authwall"`?

LinkedIn answered with the sign-in wall instead of the company page. This Actor does not log in, so it reports the wall (not charged) and the next run tries again.

#### Why did a company come back with `errorCode: "not_found"` although the page exists?

LinkedIn moves a renamed page and answers the old slug with a redirect. When the slug you named now redirects to a *different* company, the Actor does not read that page (its rows would be another company's data), so the row says the address no longer names a company page. Use the company's current LinkedIn URL. A redirect to the renamed page of the same company is followed and read normally.

#### Why is `foundedYear` null?

The page does not show a Founded row for every company. The Actor returns what the page shows and never guesses.

#### Does `followers` change every run?

It can. That is exactly what `onlyChanges` reports; the follower count is one of the tracked fields, next to the employee band, the summary, industry, headquarters, founded year, website, type and specialties.

#### Is it legal to use this data?

The Actor reads company pages that are public to any visitor, without logging in. LinkedIn's User Agreement prohibits automated means to scrape or copy data from the service, and whether your use is permitted is your decision. If you publish the rows, keep the `url` link and name LinkedIn as the source. This is not legal advice.

### Related Kizuki Actors

Other Actors from Kizuki that work well with this one:

- [LinkedIn Jobs Scraper](https://apify.com/kizukidata/linkedin-jobs-feed) — the jobs those companies post, with pay and seniority.
- [Google News Scraper](https://apify.com/kizukidata/google-news-feed) — news about the companies you are watching.

### Your feedback

Bugs, a field that stopped coming back, and feature requests go to the Actor's **Issues** tab in Apify Console. A company owner who wants a page left out can open an Issue with the company URL: the Actor keeps an exclusion list, and a request from a third party is not enough.

# Actor input Schema

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

LinkedIn company URLs or slugs, up to 100, for example \["apify", "https://www.linkedin.com/company/google/"]. The slug is the last part of the company page URL. If an old slug redirects to the company's renamed page, the Actor follows it; if it now points to a different company, it is reported as not found. Every company is read once, with requests paced and retried automatically. The default reads the sample company apify, so an empty input still returns a row.

## `onlyChanges` (type: `boolean`):

Return only the companies whose tracked fields differ from the previous run with this Actor: followers, employee band, summary, industry, headquarters, founded year, website, type and specialties. Each row carries changes.diffs with the old and the new value. The first run saves the baseline and returns the companies in full; a company with nothing changed returns no row and is not charged. The baseline is kept per company in a named key-value store in your Apify account.

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

Maximum companies one run reads, from 1 to 100. One company overview is one row, and a row for a company the Actor could not read counts too; a company it skipped as unchanged does not count. The companies after the limit are not fetched and are listed in OUTPUT as unchecked, and nothing is charged for them. The default of 10 keeps an MCP call inside its answer timeout.

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

The default uses the Apify residential proxy group, and so does {"useApifyProxy": true} on its own. Name the datacenter group to use it (this run moves to residential after its first refusal), or set {"useApifyProxy": false} to run without a proxy.

## Actor input object example

```json
{
  "companies": [
    "apify",
    "https://www.linkedin.com/company/google/"
  ],
  "onlyChanges": true,
  "maxItems": 10,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

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

The company rows of this run, one per company that was read (with its changes when onlyChanges is on).

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

Status code, counters, skipped input entries, and the linkedinCompany record of this run (companies read, failed, changed, unchanged, baselined and the state store used).

# 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": [
        "apify"
    ],
    "onlyChanges": false,
    "maxItems": 10,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("kizukidata/linkedin-company-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 = {
    "companies": ["apify"],
    "onlyChanges": False,
    "maxItems": 10,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("kizukidata/linkedin-company-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 '{
  "companies": [
    "apify"
  ],
  "onlyChanges": false,
  "maxItems": 10,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call kizukidata/linkedin-company-monitor --silent --output-dataset

```

## MCP server setup

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