# Hiring Change Radar — Greenhouse, Lever & Ashby (`aatend/hiring-change-radar`) Actor

Export public jobs and track new, changed, reopened and confirmed-removed vacancies across Greenhouse, Lever and Ashby. No ATS API keys required.

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

## Pricing

from $10.00 / 1,000 successful board checks

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?

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

## Hiring Change Radar — Greenhouse, Lever & Ashby

Export public job listings and see what changed since your last check. Built for recruitment research, job board updates, and recurring hiring reports across a known list of companies.

### What you get

- Current jobs from Greenhouse, Lever (US and EU), and Ashby in one consistent format.
- New, updated, reopened, and confirmed-removed job events across scheduled runs.
- Before/after values for changed fields, including a hash to detect description changes.
- JSON reports and separate flat CSV files for jobs and changes.
- Public data only. No ATS credentials, proxy subscription, or AI API key required.

### Quick start

Enter one or more direct public job board URLs and run in **Export current jobs** mode. Example input:

```json
{
  "boards": ["https://jobs.ashbyhq.com/ashby"],
  "mode": "snapshot",
  "includeDescriptions": false
}
```

You can also enter `greenhouse:company`, `lever:company`, `lever-eu:company`, or `ashby:company`. Replace `company` with the board's actual URL identifier. This Actor does not search for companies or discover board URLs.

### Daily monitoring

1. Choose **Monitor changes across runs** and set a monitor name, such as `daily-hiring`.
2. Run once to create your baseline. Existing jobs appear as `baseline` events, rather than new openings.
3. Save the input as an Apify task and attach a daily schedule. Keep the same monitor name and `resetBaseline: false`.
4. Read the next run's changes array or changes CSV. Use Apify integrations/webhooks for your own downstream workflow.

```json
{
  "boards": ["https://jobs.ashbyhq.com/ashby"],
  "mode": "monitor",
  "monitorName": "daily-hiring",
  "removalConfirmations": 2,
  "includeUnchanged": false,
  "includeDescriptions": false,
  "resetBaseline": false
}
```

Baselines live in a named key-value store in the calling user's Apify account. Different monitor names have independent histories. Do not delete that store unless you want to start over. Snapshot mode creates no monitoring history.

### Outputs

The default dataset contains **one report per successfully checked board**, with nested `jobs` and `changes` arrays. Download JSON for complete reports. For a flat spreadsheet, open the run's Storage > Key-value store and download `BOARD-01-JOBS.csv` or `BOARD-01-CHANGES.csv` (then BOARD-02, etc.). The output links include an `EXPORTS` file mapping boards to CSV filenames, and `SUMMARY` with success/failure counts.

Job fields include job ID, title, location, department, team, employment type, workplace type, application URL, dates supplied by the source, compensation JSON when supplied, and a description hash. Optional plain descriptions are capped at 20,000 characters; the hash covers the full source description. Missing source fields are null; salary and remote status are not guessed from titles.

Change types:

| Type | Meaning |
| --- | --- |
| baseline | Job seen during the first successful scan or an explicit reset |
| new | Previously unseen job ID |
| updated | A tracked field changed; before/after contains changed fields |
| removed | Missing for the configured number of successful complete scans |
| reopened | A previously confirmed-removed ID reappeared within retained history |
| unchanged | Optional event for an unchanged job |

`changeCount` excludes baseline and unchanged events. Timestamp-only changes do not generate updates. A removal means the public posting disappeared; it does **not** prove that someone was hired. Removed IDs are retained for 30 days; a later reappearance may be classified as new. CSV cells starting with spreadsheet formula prefixes are escaped for safe viewing.

### Pricing

The price is **$0.01 per successful board check**, plus **$0.00005 per Actor start** at all supported memory settings. Platform usage is included in these prices for customers. The live Pricing tab is authoritative. One check includes all jobs and all changes for that board, including a valid empty board or an unchanged result. Duplicate board inputs are checked once. Source failures do not incur a board-check event charge; the Actor start fee still applies.

Monitoring 10 boards once a day for 30 days costs $3.0015, including 30 Actor starts. A single 10-board run costs $0.10005; choose a maximum run cost of at least $0.11 for that example. The minimum selectable maximum run cost is $0.02; this is a spending-limit setting, not a minimum billed charge. The Actor stops before starting another board when it cannot afford another event. Event charges may remain for successfully delivered reports even if a later board fails.

### Limits and reliability

- 1–10 distinct boards per run; up to 3,000 public jobs per board.
- Official public ATS endpoints only. Custom careers domains, login-only vacancies, candidate data, and applications are unsupported.
- A source response above 20 MiB or a report/snapshot above 8 MiB fails that board rather than saving a partial baseline. Turn off descriptions for large boards.
- Requests retry transient network/server errors and respect bounded rate-limit delays. Failed, malformed, incomplete, and oversized responses preserve prior monitoring state.
- Runs using the **same monitor name must not overlap**. There is a best-effort lease and revision check, but Apify key-value storage does not provide an atomic lock. Serialize schedules and API calls.
- Delivery and state persistence are separate operations. A crash between them can cause an event to appear again on a later run. Successful committed boards are skipped when the same run is resumed. This is not an exactly-once event stream.
- A partially failed run has FAILED status so your monitoring can notice it; successful reports remain in its dataset. On a fatal export/persistence error, consult `ERROR` and the logs.
- Changes to a source API may require maintenance. No estimate of hiring volume or business outcomes is implied.

### Support

Open an issue on this Actor's Apify Store page with the run ID, affected public board URL, and expected/actual result. Do not include API tokens, private applicant data, or credentials. For a baseline reset, enable `resetBaseline` for one run and turn it off again before scheduling.

### Development

Node.js 22 or newer is required. Install with `npm ci`, then run `npm test` and `npm run check`. `npm run demo` creates clearly labeled synthetic examples. `npm run smoke` accesses live public boards; availability and results change over time. `npm start` runs the Apify SDK entry point using local storage by default.

Data sources: [Greenhouse Job Board API](https://docs.greenhouse.io/job-board.html), [Lever Postings API](https://github.com/lever/postings-api), [Ashby public job posting API](https://developers.ashbyhq.com/docs/public-job-posting-api).

# Actor input Schema

## `boards` (type: `array`):

1–10 direct Greenhouse, Lever (including EU), or Ashby board URLs. You can also use greenhouse:token, lever:token, lever-eu:token, or ashby:token. Duplicate boards are checked only once.

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

Snapshot exports current jobs without storing a baseline. Monitor compares each full scan with previous runs under the same monitor name.

## `monitorName` (type: `string`):

Use the same name on scheduled runs. Different names have independent baselines in your own Apify account. Do not run the same monitor concurrently.

## `includeDescriptions` (type: `boolean`):

Include plain text descriptions, capped at 20,000 characters per job. Full descriptions are always hashed for change detection. Turn off for large boards.

## `includeUnchanged` (type: `boolean`):

Monitor mode only. Add unchanged events to the changes array. The jobs array always contains every current public job.

## `removalConfirmations` (type: `integer`):

Confirm disappearance only after this many successful full scans. Failed or incomplete scans do not advance removal counters. Removal does not imply a position was filled.

## `resetBaseline` (type: `boolean`):

Monitor mode only. Replace existing baselines after successful delivery. Set back to false before scheduling, or every run will be a fresh baseline.

## Actor input object example

```json
{
  "boards": [
    "https://jobs.ashbyhq.com/ashby"
  ],
  "mode": "snapshot",
  "monitorName": "default",
  "includeDescriptions": false,
  "includeUnchanged": false,
  "removalConfirmations": 2,
  "resetBaseline": false
}
```

# Actor output Schema

## `reports` (type: `string`):

No description

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

No description

## `exports` (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 = {
    "boards": [
        "https://jobs.ashbyhq.com/ashby"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("aatend/hiring-change-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 = { "boards": ["https://jobs.ashbyhq.com/ashby"] }

# Run the Actor and wait for it to finish
run = client.actor("aatend/hiring-change-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 '{
  "boards": [
    "https://jobs.ashbyhq.com/ashby"
  ]
}' |
apify call aatend/hiring-change-radar --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,aatend/hiring-change-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/y5W8D7yDel4DWC07J/builds/p73H5AqY19s8AecgK/openapi.json
