# LinkedIn Jobs Scraper & Hiring Signals (`ntriqpro/linkedin-jobs-hiring-intel`) Actor

LinkedIn jobs scraper for public job listings and hiring signals. Compare prior snapshots for new listings, repost candidates and evidence-based closure signals, with scoped company hiring counts.

- **URL**: https://apify.com/ntriqpro/linkedin-jobs-hiring-intel.md
- **Developed by:** [daehwan kim](https://apify.com/ntriqpro) (community)
- **Categories:** Jobs, Lead generation
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $15.60 / 1,000 job 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 Jobs Scraper & Hiring Signals

Collect public LinkedIn job listings for a bounded set of keywords and locations. Compare an earlier dataset to see newly observed listings, repost candidates and evidence-backed closure suspicions. Each job includes company counts for the observed search scope; these are not company-wide hiring totals.

### Quick start

All inputs are arrays, including numbers and switches. Explicit acknowledgement is required; the UI does not pre-accept it for you. The included `prefill.json` is an acknowledged validation example with one keyword, one location and ten results:

```json
{
  "keywords": ["software engineer"],
  "locations": ["New York, United States"],
  "maxResults": [10],
  "datePostedDays": [7],
  "proxyModes": ["RESIDENTIAL"],
  "comparisonModes": ["snapshot"],
  "disclaimerAck": ["accepted"]
}
```

Use up to five keywords and two locations, with maxResults between 1 and 300. Search combinations are visited in rounds; a reached result cap means the search is incomplete. `jobUrls` accepts up to 100 public LinkedIn job URLs; set `keywords` and `locations` to empty arrays for direct jobs only.

`previousDatasetIds: ["YOUR_DATASET_ID"]` supplies one accessible dataset produced by this Actor. Set `comparisonModes: ["compare"]` to request comparison. A missing baseline produces `no_baseline`; access/schema problems produce `baseline_unavailable`; different search scopes produce `scope_mismatch`. Baselines are limited to 2,000 observations and are read in pages. The originating run must have succeeded and its final coverage manifest is checked, so interrupted or partially delivered datasets cannot silently become complete baselines. No arbitrary HR or applicant datasets are supported.

### Results and uncertainty

Every result exposes all 83 documented keys. `id`, `jobId` and `canonicalJobId` share a job ID. `url`, `link` and `jobUrl` share its canonical URL; `title` and `jobTitle` share its title. `fieldAvailability` distinguishes observed data from unmeasured or policy-excluded fields. The exact field contract is in `src/row-schema.json`.

`comparisonStatus` is `comparable` only when both matching scopes were completed. `isNew` means absent in that comparable baseline, not a confirmed new publication. `isReposted` is a candidate with the same company ID, normalized title and location, a different job ID and a later observed publication date. A changed date on the same job ID is recorded separately. Missing dates remain unknown.

A listing disappearing from search is not enough to mark it closed. `closure_suspected` requires a matching completed scope plus an explicit closure message or HTTP 404/410 on the public detail page. Closure is never asserted from login, captcha, throttling, timeout or a changed parser. Closure records are uncharged status records. `companyDelta` is withheld on incomplete coverage. No company summary rows are added.

For a first capped run, a typical observation has `recordType: "active_job"`, `comparisonStatus: "no_baseline"`, `coverageComplete: false`, and null change flags. On a capped comparison, the status is `incomplete`. `OUTPUT` in the key-value store reports request counts, blocking rate, reasons, runtime and billing mode even when no job row is produced.

### Billing

The platform-managed `apify-actor-start` costs $0.001 once per run in every tier, including input rejection, zero results, blocked requests and failed runs. The code never charges a second startup event. `job-result` applies to each validated, unique active-job row delivered. Comparison signals and scoped company aggregation are included.

| Plan | Startup per run | Job result per row |
|---|---:|---:|
| FREE | $0.001 | $0.0300 |
| BRONZE | $0.001 | $0.0240 |
| SILVER | $0.001 | $0.0195 |
| GOLD | $0.001 | $0.0156 |
| PLATINUM | $0.001 | $0.0156 |
| DIAMOND | $0.001 | $0.0156 |

At the FREE base price, 100 delivered jobs cost $3.001; zero jobs cost the $0.001 startup fee. Registered event pricing includes platform usage; no separate usage surcharge is configured. Owner validation is not evidence of a paying customer's invoice.

Input rejection, blocked requests, internal failures, duplicate rows and closure-status records do not trigger `job-result`. Internal failures remain FAILED. Notices belong in `OUTPUT`, not billable job rows. A spending limit stops additional unfunded work and retains delivered results. No FREE-account-only cap is applied. Pricing comes from platform metadata, not account-tier guesses.

A durable ledger keyed by run ID and job ID serializes charges. A saved charge is restored to the dataset after interruption. Ambiguous charge responses stop without an automatic retry; resurrection reconciles platform counts before delivery. If the outcome cannot be established, the run fails for operator reconciliation. Cross-run deduplication is not promised: a new run is a new observation.

### Collection boundaries and responsibilities

This is an unofficial tool, not affiliated with or endorsed by LinkedIn. The operator must have authority to process inputs, comply with applicable target terms and laws, and verify and lawfully use outputs. Where applicable, the operator is the controller of personal data and Ntriq acts as processor under the governing contract. This notice does not remove Ntriq's own legal or contractual obligations.

The Actor uses public HTTP pages and identifies itself as a bot. It does not log in, use cookies, solve captchas, rotate accounts, or bypass access controls. RESIDENTIAL is the default proxy group; `proxyModes` can select DATACENTER. Temporary HTTP 429, timeout, network, 5xx and empty-body failures receive up to three retries, waiting 2, 6 and 18 seconds with 20% jitter and a fresh proxy session for each retry. Login, captcha and other access-denied requests are recorded without retry. Five consecutive blocked responses stop collection and mark it incomplete. All requests, including retries, start at least one second apart, with at most three in flight. Details are collected page by page, so a later search failure retains earlier job results. `OUTPUT.source` reports attempts, completed and failed requests, retries, blocks, and classifications by request stage. Failed requests never trigger `job-result`; the registered platform startup event still applies.

Recruiter names, contact/profile information, job-description text/HTML and company posts are excluded. Their compatibility keys are null and labeled `excluded_by_policy`; their contents are not stored. Pages are parsed transiently under a 1 MB response limit. Keyword and experience-number extraction retains only structured facts, not description prose. Use this tool for bounded job research, not systematic replication of LinkedIn's database.

### Operations

The default allocation is 256 MB with a 1,800-second timeout. The supported result cap is 300, validated in two private runs; larger batches are not enabled.

`npm test` validates input, privacy fields, comparison and billing recovery. `npm start` runs the Actor. `test/heartbeat.js` defines three public heartbeat scenarios (search, detail, baseline). Scheduling these on the deployed private Actor remains an operator task before publication; no external scheduler is created by this package. Parser failures set incomplete coverage rather than fabricating an empty complete search.

# Actor input Schema

## `keywords` (type: `array`):

Array input. keywords

## `locations` (type: `array`):

Array input. locations

## `jobUrls` (type: `array`):

Array input. jobUrls

## `previousDatasetIds` (type: `array`):

Array input. One authorized previous dataset from this Actor.

## `maxResults` (type: `array`):

Array input. 1 to 300 unique active jobs. Reaching the limit makes coverage incomplete.

## `datePostedDays` (type: `array`):

Array input. datePostedDays

## `proxyModes` (type: `array`):

Array input. RESIDENTIAL by default; choose DATACENTER if needed. Temporary failures retry up to three times with a fresh proxy session. Login and captcha requests never retry.

## `comparisonModes` (type: `array`):

Array input. comparisonModes

## `disclaimerAck` (type: `array`):

Array input. Explicitly enter \["accepted"] to acknowledge input rights, target terms, and responsibility for lawful output use.

## Actor input object example

```json
{
  "keywords": [
    "software engineer"
  ],
  "locations": [
    "New York, United States"
  ],
  "jobUrls": [],
  "previousDatasetIds": [],
  "maxResults": [
    10
  ],
  "datePostedDays": [
    7
  ],
  "proxyModes": [
    "RESIDENTIAL"
  ],
  "comparisonModes": [
    "snapshot"
  ]
}
```

# Actor output Schema

## `dataset` (type: `string`):

No description

## `summary` (type: `string`):

No description

# 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 = {
    "keywords": [
        "software engineer"
    ],
    "locations": [
        "New York, United States"
    ],
    "jobUrls": [],
    "previousDatasetIds": [],
    "maxResults": [
        10
    ],
    "datePostedDays": [
        7
    ],
    "proxyModes": [
        "RESIDENTIAL"
    ],
    "comparisonModes": [
        "snapshot"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("ntriqpro/linkedin-jobs-hiring-intel").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 = {
    "keywords": ["software engineer"],
    "locations": ["New York, United States"],
    "jobUrls": [],
    "previousDatasetIds": [],
    "maxResults": [10],
    "datePostedDays": [7],
    "proxyModes": ["RESIDENTIAL"],
    "comparisonModes": ["snapshot"],
}

# Run the Actor and wait for it to finish
run = client.actor("ntriqpro/linkedin-jobs-hiring-intel").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 '{
  "keywords": [
    "software engineer"
  ],
  "locations": [
    "New York, United States"
  ],
  "jobUrls": [],
  "previousDatasetIds": [],
  "maxResults": [
    10
  ],
  "datePostedDays": [
    7
  ],
  "proxyModes": [
    "RESIDENTIAL"
  ],
  "comparisonModes": [
    "snapshot"
  ]
}' |
apify call ntriqpro/linkedin-jobs-hiring-intel --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,ntriqpro/linkedin-jobs-hiring-intel"
        }
    }
}
```

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/X9Rp57gXIfd9XBV36/builds/0FSdPBT6qVvvbAr1r/openapi.json
