# Hiring Changes Radar — Greenhouse & Lever (`nexascout/hiring-changes-radar`) Actor

Monitor Greenhouse and Lever job boards for new, updated, removed and reopened jobs. Save history, filter keywords, and export changes with source links.

- **URL**: https://apify.com/nexascout/hiring-changes-radar.md
- **Developed by:** [NexaScout](https://apify.com/nexascout) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 job or change records

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/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

## Hiring Changes Radar — Greenhouse & Lever

Monitor public company job boards and export jobs plus changes between successful observations. Built for recruiting research, competitor hiring watchlists, and evidence-based sales research.

### What you get

- One normalized dataset across Greenhouse and Lever (global and EU).
- Events: BASELINE, NEW, UPDATED, UNCHANGED, MISSING\_UNCONFIRMED, REMOVED, REOPENED.
- Changed field names, previous values, source URLs, observation dates, and literal keyword evidence.
- Persistent per-board history, a self-contained HTML dashboard, and JSON exports.
- Direct public API requests: no browser, residential proxy, paid Actor dependency, or LLM API.

You supply career board URLs. This version does not discover all companies or search the whole job market. It does not access LinkedIn, send outreach, submit applications, or collect candidate profiles.

### Quick start

Use `INPUT.example.json`, 512 MB memory and a 300-second platform timeout. The prefilled Greenhouse board is a small real-data smoke test, not a sales prospect recommendation.

Example:

```json
{
  "boardUrls": ["https://job-boards.greenhouse.io/greenhouse", "https://jobs.lever.co/palantir"],
  "keywords": [],
  "saveHistory": true,
  "historyProfile": "default",
  "changesOnly": false,
  "maxJobsPerBoard": 2000,
  "maxRunSeconds": 180
}
```

Supported hosts: `boards.greenhouse.io`, `job-boards.greenhouse.io`, `jobs.lever.co`, `jobs.eu.lever.co`. Custom company career domains and Greenhouse embedded URLs are not supported; use the provider-hosted board link.

### Change semantics

First successful observation establishes BASELINE: existing jobs are not labelled NEW. NEW means first observed after that baseline, not necessarily published today. UPDATED compares title, primary location, department, employment/workplace type, URL, normalized description and structured salary. A provider timestamp alone is not a content change. Description history exposes its prior SHA-256 hash instead of duplicating old text.

A missing job first becomes MISSING\_UNCONFIRMED. It becomes REMOVED only after another complete observation at least six hours after first absence. This means removed from the public board, **not proof of a hire, cancelled role, or layoffs**. REMOVED emits once; reappearance within the 30-day tombstone retention becomes REOPENED. After retention expires it can be NEW again. A reappearance before confirmation clears the missing state.

The APIs are live feeds, not transactional snapshots: jobs can change during pagination. Duplicate IDs cause the board to fail conservatively. Errors, malformed responses and capped/truncated boards never advance that board's baseline or generate disappearance events. Lever reaching the configured cap is conservatively PARTIAL even if the board happens to have exactly that many jobs.

Partial-board jobs can still appear in output with `observationComplete:false`; the next full run may repeat their change events. `SUMMARY` is authoritative for coverage. If all boards fail, the Actor fails. A successful collection with zero jobs is allowed. Mixed failures return PARTIAL in SUMMARY, even though the platform run can finish successfully.

### History and schedules

Use Limited permissions from the first run. History lives in named Actor-created storage scoped to Actor ID and historyProfile; board IDs are independently keyed. Keep one concurrent run per profile. There is no distributed lock. Do not overlap runs or resurrect an older run after a newer one has committed history.

Schedule a saved task daily, with the same profile. Keywords and changesOnly filter output after full-board comparison; they do not reset history. Different watchlists that include the same board and profile share its baseline. Use separate profiles for independent workflows. `saveHistory:false` is a stateless snapshot and produces BASELINE events only.

If changesOnly is true, the first run normally has no dataset rows but still creates history. Changes are delivered in each run's dataset; no email, Slack or webhook delivery is built into this version. Configure Apify integrations separately if needed.

### Fields and limits

Up to 20 boards and 5,000 jobs per board; default cap 2,000. Requests are sequential with bounded retries and a collection deadline. Dataset writes happen after each collected board, before its history advances. Outputs include a job description, URL, company label, source ID, primary location, department, event, changed fields and observation timestamps.

Lever company labels use the board token unless the source supplies a company name. Lever salary fields are included only when structured salaryRange is present. Greenhouse salary is null in this version: no guessed salary extraction or paid enrichment. Missing workplace type stays null. Cross-board duplicate titles and multiple-location posts are not merged; identity is provider + region + board + source job ID. This avoids incorrectly merging distinct openings.

Literal keyword matches do not prove procurement intent, budget or company growth. Historical events are observations, not financial advice or forecasts. Source outages, changed API behavior, retired boards and unusual descriptions can still affect results.

### Pricing

Check the Pricing tab for the current active rates. Launch pricing is $0.01 per start event plus $1 per 1,000 dataset records ($0.001 per record). At the recommended 512 MB, one run uses one start event; at 2 GB it uses two.

Every dataset record counts, including BASELINE, UNCHANGED, missing-job observations and changes. Downloading the same run's JSON or viewing its dashboard does not create another result event. Filters reduce emitted records, while the start fee still applies when there are no matching changes, no jobs, or a source error. With `changesOnly:true`, BASELINE and UNCHANGED are excluded; the first run can therefore have zero output records while establishing history.

At 512 MB, 334 emitted records cost $0.344 before any applicable discounts; a run with zero emitted records costs $0.01. These are event-price examples, not guaranteed resource costs or a promise of a particular job count. Platform usage is intended to be included in this launch pricing. Check the active Pricing tab before running.

Set a run spending limit sufficient for your watchlist. A spending-limit stop can interrupt output and history; inspect SUMMARY and the dataset before using an interrupted run. Do not treat an interrupted run as a complete market observation.

### Tested examples

On September 20, 2026, two cloud observations of the Greenhouse company board and Palantir's Lever board each returned 334 records: 21 Greenhouse and 313 Lever. The first established BASELINE; the second returned 334 UNCHANGED records and saved history for both boards. These are dated observations, not a guaranteed yield. Removal and reopening logic was tested with controlled fixtures.

### Frequently asked questions

**Do I need API keys for Greenhouse or Lever?** No. The Actor reads their public job-board endpoints. Apify API access itself uses your normal Apify authentication.

**Does it find every company using these ATS platforms?** No. Supply the company board URLs you want to monitor.

**Can I export CSV or Excel?** Use Apify's dataset export controls. A JSON copy and an HTML dashboard are also included in run output.

**Why is the first run BASELINE?** It establishes what existed when monitoring began. Only later observations can identify newly seen postings.

**Why are salary or workplace fields empty?** Fields depend on the source. This version does not extract Greenhouse salary. Lever salary is included only when a structured range is supplied.

**Does REMOVED mean the vacancy was filled?** No. It means the posting was absent from repeated complete observations. It is not proof of a hire.

**Why do I see no changes?** The board may be unchanged, filters may exclude the rows, or this may be the first observation with changesOnly enabled. Check SUMMARY for board coverage and errors.

**Can I use it through an AI assistant?** Yes, through Apify's Actor tools/MCP using the same input schema. The Actor itself does not require an LLM key.

**How do I report an issue?** Use the Actor's Issues tab and include the run URL, affected public board URL and expected behavior. Do not include access tokens.

### Development

Node.js 22+. `npm ci`, `npm test`, `npm run check`. `npm run sample` checks two public boards and writes sample files without cloud execution. `npm start` uses the Apify SDK.

For local testing, leave APIFY\_IS\_AT\_HOME unset (the string "false" is truthy in the SDK). Preserve local storage for comparison tests. The Docker build uses Node 22. `bash deploy.sh` validates and updates Actor Nbw85R8YCjl5DdtGm using your existing CLI login. Publication and pricing are configured separately in Console.

Source documentation:

- https://docs.greenhouse.io/job-board.html
- https://github.com/lever/postings-api

# Actor input Schema

## `boardUrls` (type: `array`):

1–20 public Greenhouse or Lever board URLs. Global and EU Lever supported. Custom domains not supported.

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

Literal OR match in title or description. Filtering does not change stored history. Up to 20 keywords.

## `maxJobsPerBoard` (type: `integer`):

Truncated boards do not advance history or generate disappearance events.

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

Set platform timeout to at least 300 seconds.

## `saveHistory` (type: `boolean`):

Named storage created by this Actor. Use Limited permissions from the first run.

## `historyProfile` (type: `string`):

Letters, digits, hyphens; 1–32 characters. Separate profiles isolate monitoring histories.

## `changesOnly` (type: `boolean`):

Excludes BASELINE and UNCHANGED. First run can have zero output rows while creating a baseline.

## Actor input object example

```json
{
  "boardUrls": [
    "https://job-boards.greenhouse.io/greenhouse"
  ],
  "keywords": [],
  "maxJobsPerBoard": 2000,
  "maxRunSeconds": 180,
  "saveHistory": true,
  "historyProfile": "default",
  "changesOnly": false
}
```

# Actor output Schema

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

No description

## `dashboard` (type: `string`):

No description

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

No description

## `json` (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 = {
    "boardUrls": [
        "https://job-boards.greenhouse.io/greenhouse"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("nexascout/hiring-changes-radar").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 = { "boardUrls": ["https://job-boards.greenhouse.io/greenhouse"] }

# Run the Actor and wait for it to finish
run = client.actor("nexascout/hiring-changes-radar").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 '{
  "boardUrls": [
    "https://job-boards.greenhouse.io/greenhouse"
  ]
}' |
apify call nexascout/hiring-changes-radar --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,nexascout/hiring-changes-radar"
        }
    }
}
```

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/Nbw85R8YCjl5DdtGm/builds/yZydm9ehs43QONNWJ/openapi.json
