# LinkedIn Job Monitor & Hiring Signal Alerts (`marielise.dev/linkedin-job-change-monitor`) Actor

Monitor LinkedIn job changes, new hiring and closed roles. Get scored B2B hiring signals and daily job alerts for recruiting, sales and market research.

- **URL**: https://apify.com/marielise.dev/linkedin-job-change-monitor.md
- **Developed by:** [Marielise](https://apify.com/marielise.dev) (community)
- **Categories:** Lead generation, Automation, Social media
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $10.00 / 1,000 qualified job signals

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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## LinkedIn Job Monitor & Hiring Signal Alerts — Not Another CSV

**Find out when a company starts hiring, changes a role, or stops advertising it on LinkedIn.** This LinkedIn job monitor turns public job-search results into a recurring hiring-signal feed for recruiters, staffing firms, sales teams, and competitive-intelligence analysts. It is a LinkedIn job alert and change tracker for teams that need actionable hiring signals rather than another one-off CSV export.

A raw LinkedIn job scraper is useful once. A saved job search that tells you only what changed is useful every day. Search for `account executive` in London, set a stable `watchId`, and put the actor on an Apify daily schedule. The first run stores a clean baseline. Each later run emits only `new_job`, `job_changed`, and (optionally) `job_closed` records. That means fewer duplicate rows, simpler Make/Zapier/CRM automations, and a dataset that points directly to action.

No LinkedIn login, cookie, or browser automation is required. The actor reads LinkedIn's server-rendered public guest-job endpoints with inexpensive HTTP requests, so it is substantially lighter than running Playwright for the same public search.

### Who uses this

- **Recruiting and staffing teams:** be the first to contact a hiring manager when a target role appears.
- **B2B sales and sales-ops:** detect headcount investment, GTM expansion, and software-buying triggers at target accounts.
- **Market researchers:** watch a competitor's role mix, locations, and seniority requirements over time.
- **Job-market analysts:** retain a dated, structured record of vacancy activity without repeatedly exporting the same jobs.

### Quick start

Use `once` first to confirm your keywords and location:

```json
{
  "mode": "once",
  "keywords": "account executive",
  "location": "London, England, United Kingdom",
  "datePosted": "pastWeek",
  "maxJobs": 50
}
```

Then create a recurring monitor. Run the same input daily or weekly on an Apify schedule:

```json
{
  "mode": "watch",
  "watchId": "uk-saas-account-executives",
  "keywords": "account executive SaaS",
  "location": "United Kingdom",
  "datePosted": "pastWeek",
  "maxJobs": 100,
  "notifyOnClosed": true
}
```

The first watch run is intentionally a baseline. Later runs push only changes. Each result includes a stable LinkedIn job ID and URL, title, company, location, listing date, detected timestamp, job criteria when publicly available, and `changedFields` for updates. A closed event means a job that was in the saved search is no longer returned by that same search; it can indicate expiry, removal, or a ranking/filter change, so treat it as a signal rather than a claim that the role was filled.

Watch state is stored in a named key-value store, so it survives between runs. Keep `watchId` identical across scheduled runs of the same saved search, and give each distinct search its own ID.

### Input parameters

| Field | Type | Default | What it does |
| --- | --- | --- | --- |
| `mode` | select | `once` | `once` exports the current vacancy list, `watch` emits only changes, `reset` clears a saved watch. |
| `watchId` | string | derived | Stable name for the saved search. Keep it identical across scheduled runs. |
| `keywords` | string | `account executive` | Role or skill to search for. |
| `location` | string | empty | City, region or country. Blank searches all locations. |
| `datePosted` | select | `pastWeek` | Restrict the public search to recently posted roles. |
| `remoteOnly` | boolean | `false` | Ask LinkedIn's public search for remote roles only. |
| `employmentTypes` | array | `[]` | Keep only the selected employment types. |
| `maxJobs` | integer | `50` | Job cards to collect and inspect per run, 10–200. |
| `vertical` | select | `auto` | Keep only roles with evidence for a named market. Auto-detects from your watch ID and keywords. |
| `targetCompanies` | array | `[]` | Exact company names to watch. |
| `includeKeywords` / `excludeKeywords` | array | `[]` | Keep or drop jobs by term across title, company, location and public description. |
| `minFitScore` | integer | `0` | Minimum 0–100 hiring-signal score. |
| `excludeIntermediaries` | boolean | `true` | Drop staffing, recruitment and executive-search postings. |
| `notifyOnClosed` | boolean | `true` | Emit an event when a tracked role leaves the search results. |
| `includeFullDescription` | boolean | `false` | Include the complete public description instead of a 600-character excerpt. |
| `proxy` | object | direct HTTP | Enable an Apify proxy only if your runs are being rate-limited. |

### Output example

```json
{
  "eventType": "new_job",
  "status": "ok",
  "watchId": "saas-sales-london",
  "jobId": "4450637001",
  "title": "Account Executive",
  "companyName": "Klue",
  "location": "London, England, United Kingdom",
  "jobUrl": "https://uk.linkedin.com/jobs/view/account-executive-at-klue-4450637001",
  "postedAt": "2026-08-06",
  "detectedAt": "2026-08-13T09:41:22.104Z",
  "employmentType": "Full-time",
  "seniorityLevel": "Not Applicable",
  "applicantCount": "63 applicants",
  "fitScore": 65,
  "signalTags": ["market_expansion", "software_company", "new_business_motion"],
  "signalSummary": "Klue is hiring Account Executive in London, England, United Kingdom; signals: market expansion, software company, new business motion.",
  "companyOpenRoleCount": 2,
  "descriptionExcerpt": "At Klue, we're building the future of competitive intelligence..."
}
```

Two dataset views are provided: **Hiring changes** for watch events and **Current jobs** for one-time and baseline exports.

### FAQ

**Do I need a LinkedIn account or cookie?** No. The actor reads LinkedIn's public guest job endpoints only.

**Why did my first watch run return every job?** That run is the baseline. Schedule the identical input and later runs emit only `new_job`, `job_changed` and `job_closed`.

**Why did a watch run return nothing?** Nothing changed. An empty dataset in watch mode is the normal healthy result.

**What happens if LinkedIn blocks the run?** The run still succeeds and writes a single record with `status: "blocked"` and `eventType: "search_unavailable"`. Saved watch state is left untouched so you never get a wave of false closed-job events.

**Why do some results look loosely related to my keywords?** LinkedIn's public index widens narrow queries rather than returning an empty page. Use `vertical`, `includeKeywords`, `minFitScore` and `targetCompanies` to tighten the result set.

**Can I change the search and keep the same watch ID?** Yes, but the actor detects the changed search profile and re-baselines instead of reporting the difference as real hiring activity.

### Data quality and responsible use

Job title, company, location, URL, and listing date come directly from public search cards. Description, workplace type, employment type, seniority, and applicant count are extracted only when a public job-detail response provides them; unavailable values are omitted instead of invented. Public sites can rate-limit or alter markup, so enable an Apify proxy only if your scheduled environment is rate-limited. The actor uses bounded sequential HTTP requests, delays, retries, and a maximum of 200 jobs per run to stay economical and polite.

Use the resulting data in accordance with LinkedIn's terms, applicable privacy law, and your team's lawful-basis and outreach policies.

### Turn job rows into client-ready signals

The default output is deliberately compact: it does **not** repeat multi-page job descriptions in every scheduled run. Instead, each role has a 0–100 deterministic **signal score**, tags such as `founding_sales_team`, `market_expansion`, `enterprise_motion`, `software_company`, and `new_business_motion`, plus a short explanation of why the opening matters. Use `minFitScore`, `includeKeywords`, `excludeKeywords`, and `targetCompanies` to make a saved search specific to one client or territory. Set `includeFullDescription` only when a downstream AI workflow genuinely needs the full public text.

# Actor input Schema

## `mode` (type: `string`):

Use one-time export for a current vacancy list. Use watch on a schedule: the first run saves a baseline and later runs output only what changed.

## `watchId` (type: `string`):

A stable name for this saved search, e.g. 'fintech-sales-london'. Watch state is keyed on this value, so keep it identical across scheduled runs. Leave blank and one is derived from your search settings.

## `keywords` (type: `string`):

What role or skill to find, e.g. 'account executive', 'machine learning engineer', or 'SAP consultant'.

## `location` (type: `string`):

City, region or country, e.g. 'London, England, United Kingdom'. Leave blank for all locations.

## `datePosted` (type: `string`):

Limit the public search to recently posted roles.

## `remoteOnly` (type: `boolean`):

Ask LinkedIn's public search for remote roles only.

## `employmentTypes` (type: `array`):

Optional filter applied to extracted job details. Leave empty for all types.

## `maxJobs` (type: `integer`):

Maximum job cards to collect and inspect per run. Higher values make more plain-HTTP requests and take longer.

## `vertical` (type: `string`):

Auto (recommended) detects a named vertical in your watch ID, job keywords, or include-keywords. For example `fintech-sales-london` automatically applies the fintech evidence filter. Select a vertical explicitly to override auto-detection.

## `targetCompanies` (type: `array`):

Optional exact company names to watch. Leave empty to monitor the whole search market.

## `includeKeywords` (type: `array`):

Keep only jobs whose title, company, location, or public description contains at least one term. Examples: SaaS, enterprise, fintech.

## `excludeKeywords` (type: `array`):

Remove jobs containing any term, e.g. internship, recruiter, graduate.

## `minFitScore` (type: `integer`):

Only return jobs with this deterministic 0–100 hiring-signal score. Use 40+ to focus on expansion, enterprise, software, and sales-growth signals.

## `excludeIntermediaries` (type: `boolean`):

Remove likely staffing, recruitment, executive-search, and consultancy intermediaries. Turn off only if agency-posted roles matter to your use case.

## `notifyOnClosed` (type: `boolean`):

In watch mode, output an event when a previously seen role disappears from the configured search results.

## `includeFullDescription` (type: `boolean`):

Off by default to keep datasets compact. Every result includes a 600-character excerpt and a signal summary; enable only when downstream processing needs the complete public description.

## `proxy` (type: `object`):

The actor uses direct HTTP by default, which is the cheapest and works for normal run sizes. Enable an Apify proxy only if your runs are being rate-limited.

## Actor input object example

```json
{
  "mode": "once",
  "watchId": "fintech-sales-london",
  "keywords": "account executive",
  "location": "London, England, United Kingdom",
  "datePosted": "pastWeek",
  "remoteOnly": false,
  "employmentTypes": [],
  "maxJobs": 50,
  "vertical": "auto",
  "targetCompanies": [],
  "includeKeywords": [],
  "excludeKeywords": [],
  "minFitScore": 0,
  "excludeIntermediaries": true,
  "notifyOnClosed": true,
  "includeFullDescription": false,
  "proxy": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `hiringChanges` (type: `string`):

New, changed and closed job events produced by watch runs

## `currentJobs` (type: `string`):

Current job search results from one-time and baseline runs

## `keyValueStore` (type: `string`):

Saved watch snapshots and run metadata

# 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 = {
    "watchId": "fintech-sales-london",
    "keywords": "account executive",
    "location": "London, England, United Kingdom"
};

// Run the Actor and wait for it to finish
const run = await client.actor("marielise.dev/linkedin-job-change-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 = {
    "watchId": "fintech-sales-london",
    "keywords": "account executive",
    "location": "London, England, United Kingdom",
}

# Run the Actor and wait for it to finish
run = client.actor("marielise.dev/linkedin-job-change-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 '{
  "watchId": "fintech-sales-london",
  "keywords": "account executive",
  "location": "London, England, United Kingdom"
}' |
apify call marielise.dev/linkedin-job-change-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,marielise.dev/linkedin-job-change-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/IGBBpIxDlfDrZrE2B/builds/19vtX1D7hY3kD85JC/openapi.json
