# LinkedIn Hiring Signals Monitor 📡 (new roles only) (`tagadanar/linkedin-hiring-signals-monitor`) Actor

Get alerted only when a genuinely new job appears for your saved LinkedIn search. Remembers what it has already seen and emits just the delta: new roles since the last run, a hiring-signal feed for sales and recruiting. First run is a baseline, not a false 'all new' alert. No API key, no login.

- **URL**: https://apify.com/tagadanar/linkedin-hiring-signals-monitor.md
- **Developed by:** [Tagada Data](https://apify.com/tagadanar) (community)
- **Categories:** Jobs, Lead generation, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.60 / 1,000 new job alerts

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?

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 Hiring Signals Monitor

Save a LinkedIn jobs search once, and this actor tells you only what changed. Every run it re-checks the search and reports the jobs that were not there before: a genuine hiring-signal feed instead of a full re-download you have to diff yourself. Run it on a daily schedule and it remembers what it has already reported, so your feed stays a clean stream of new roles.

No login. No cookies to paste. No API key. This tool watches public job postings and the hiring company only. It does not collect personal profiles, applicants, or the person who posted the job.

### How the monitor works

- Give it one or more **saved searches**: keyword and location (plus optional filters), or a LinkedIn jobs search URL pasted straight from your browser.
- The **first run** for each saved search is a **baseline**: it reports every job currently listed, flagged `monitorStatus: "baseline"`, so you know exactly where the feed starts. This is not treated as "all new" — it is the starting point.
- Every run after that reports **only jobs it has not seen before** for that exact search, flagged `monitorStatus: "new"`.
- Editing your list of searches between runs never disturbs another search's history: each saved search keeps its own memory.
- A **run-summary** row is pushed for every search on every run, even when nothing new was found. Zero new roles is a normal, healthy outcome for a monitor, not an error.

### What you get

One record per new (or baseline) job posting:

| Field | Description |
| --- | --- |
| `monitorStatus` | `baseline` (first run for this search) or `new` (a genuine delta) |
| `searchLabel` | The saved search this job came from |
| `jobId` | LinkedIn job posting ID |
| `title` | Job title |
| `jobUrl` | Direct link to the public job posting |
| `companyName` | Hiring company |
| `companyUrl` | The company's LinkedIn page |
| `companySlug` | Company handle from its URL |
| `location` | Location as shown on the posting |
| `postedAt` / `postedDate` | Posting age (site wording) and ISO date |
| `isNew` | Flagged as newly posted by LinkedIn itself |
| `source`, `scrapedAt` | Provenance |

Plus one `run-summary` row per saved search per run:

| Field | Description |
| --- | --- |
| `isBaseline` | Was this the search's first-ever run |
| `totalCurrentlyListed` | Jobs LinkedIn currently shows for this search |
| `newCount` | How many were genuinely new this run |
| `searchEcho` | The search's own filters, echoed back |

### Who uses this

- **Sales and lead-gen teams:** a company posting new roles is a company with fresh budget. A daily feed of new "account executive" or "customer success" postings is a live outreach trigger, not a stale export.
- **Recruiters and staffing agencies:** watch a role-and-city combination and get pinged the moment a new opening appears, before it gets crowded with applicants.
- **Market and competitive researchers:** watch a specific employer's hiring activity over time without re-downloading and diffing a full list yourself.

### Input examples

**A single saved search, checked daily for new postings:**

```json
{
  "searches": [
    { "keywords": "account executive", "location": "London" }
  ],
  "postedSince": "24h"
}
```

**Several saved searches at once, with a Slack digest of new roles:**

```json
{
  "searches": [
    { "keywords": "product manager", "location": "Berlin", "workType": "remote" },
    { "keywords": "data engineer", "location": "Paris", "experienceLevel": "mid-senior" }
  ],
  "postedSince": "week",
  "slackWebhookUrl": "https://hooks.slack.com/services/T000/B000/XXXX"
}
```

**Paste a LinkedIn search URL straight from your browser (filters included):**

```json
{
  "searches": [
    { "url": "https://www.linkedin.com/jobs/search/?keywords=warehouse%20manager&location=Chicago&f_TPR=r604800" }
  ]
}
```

### Output sample

```json
{
  "recordType": "job",
  "monitorStatus": "new",
  "searchLabel": "software engineer",
  "jobId": "4438655931",
  "title": "Software Engineer",
  "jobUrl": "https://www.linkedin.com/jobs/view/4438655931",
  "companyName": "Biomemory",
  "companyUrl": "https://www.linkedin.com/company/biomemory",
  "location": "Paris, Île-de-France, France",
  "postedAt": "2 days ago",
  "postedDate": "2026-07-09",
  "isNew": true,
  "source": "linkedin.com",
  "scrapedAt": "2026-08-20T09:14:22.317Z"
}
```

### How to run it as a live monitor

Schedule this actor as a Task (not an ad-hoc run) so it keeps its own memory between runs. Once a day is plenty: LinkedIn's guest search is refreshed continuously, and the delta logic only reports what is genuinely new since the last run, no matter how often you check.

### Pricing

You pay per job the monitor reports, with platform usage included in the price: no subscription, no API key. A quiet run (no new jobs) costs only the actor start.

| Event | Price |
| --- | --- |
| Actor start | $0.001 per run |
| Baseline job | $1.00 per 1,000 (first run of a saved search, the "currently listed" dump) |
| New job alert | $8.00 per 1,000 (a job that was not there on the previous run, the hiring signal) |
| Slack digest sent | $0.01 per run, only when a digest was actually posted |

`run-summary` rows are never billed.

### FAQ

**Do I need a LinkedIn account or a cookie?**
No. This reads LinkedIn's public jobs pages, so there is no login, no session cookie and no API key to manage.

**Why did my first run return so many "new" jobs?**
The first run for a saved search is always a baseline: it reports the current state so you have a starting point, and flags every row `monitorStatus: "baseline"`. Only runs after the first one report genuinely new postings.

**What if nothing new is posted?**
That is a normal, successful outcome. The run still finishes cleanly and pushes a `run-summary` row saying zero new jobs were found; it is not treated as a failure.

**Can I change my saved searches without losing history?**
Yes. Each saved search is tracked independently by its own keywords/location/filters, so adding, removing or editing one search does not reset any other search's memory.

**How do I get a Slack alert?**
Set `slackWebhookUrl` to a Slack incoming-webhook URL. You get one digest per run, only when at least one genuinely new job was found (never on a baseline run).

**How is it different from LinkedIn Jobs Scraper?**
[LinkedIn Jobs Scraper](https://apify.com/tagadanar/linkedin-jobs-scraper) returns the full current result set every run, on demand. This actor is built for the opposite job: a recurring, scheduled watch that reports only what changed, so you never have to diff two exports by hand.

### Related actors

- [LinkedIn Jobs Scraper](https://apify.com/tagadanar/linkedin-jobs-scraper): the full, on-demand version of this search, no memory between runs.

***

LinkedIn job alerts, LinkedIn hiring signals, new jobs monitor, LinkedIn jobs delta, hiring signal feed, sales trigger data, job posting alerts, LinkedIn jobs webhook, recruiting lead generation, competitor hiring tracker, saved search monitor, daily jobs digest.

# Actor input Schema

## `searches` (type: `array`):

One saved search per entry. Each entry is either an object with <code>keywords</code>/<code>location</code> and, optionally, <code>workType</code> (<code>any</code>/<code>on-site</code>/<code>remote</code>/<code>hybrid</code>), <code>experienceLevel</code>, <code>jobType</code> and <code>companyIds</code>, or an object with a <code>url</code> you pasted straight from a LinkedIn jobs search (filters included). A run remembers each search's own history, so adding or removing a search here never disturbs another search's baseline.

## `postedSince` (type: `string`):

How far back LinkedIn is asked to look on each search. This only bounds what gets fetched; the actor still reports only jobs it has not already reported. Used as the default for every search, unless a pasted URL already pins its own value.

## `slackWebhookUrl` (type: `string`):

A Slack incoming-webhook URL (or any endpoint accepting a JSON <code>{"text": ...}</code> body) that receives a digest of the new roles found this run. Sent only when at least one genuinely new job was found (never on a baseline run). Pairs with a daily schedule.

## Actor input object example

```json
{
  "searches": [
    {
      "keywords": "software engineer",
      "location": "Paris"
    }
  ],
  "postedSince": "24h"
}
```

# Actor output Schema

## `jobs` (type: `string`):

One item per new (or baseline) job, plus one run-summary row per saved search, in the default dataset.

# 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 = {
    "searches": [
        {
            "keywords": "software engineer",
            "location": "Paris"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("tagadanar/linkedin-hiring-signals-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 = { "searches": [{
            "keywords": "software engineer",
            "location": "Paris",
        }] }

# Run the Actor and wait for it to finish
run = client.actor("tagadanar/linkedin-hiring-signals-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 '{
  "searches": [
    {
      "keywords": "software engineer",
      "location": "Paris"
    }
  ]
}' |
apify call tagadanar/linkedin-hiring-signals-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,tagadanar/linkedin-hiring-signals-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/hchT4yu3r04pkvy1k/builds/vXGphBGQyvziqc61H/openapi.json
