# Greenhouse Job Change Monitor — New, Changed & Closed Posts (`jaff-consulting/greenhouse-job-change-monitor`) Actor

Monitor public Greenhouse job boards with persistent snapshots. Detect new, changed and reopened posts, and confirm missing jobs on separate successful runs. Includes source links and change fields.

- **URL**: https://apify.com/jaff-consulting/greenhouse-job-change-monitor.md
- **Developed by:** [JAFF Consulting B.V.](https://apify.com/jaff-consulting) (community)
- **Categories:**
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.50 / 1,000 current job post processeds

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

## Greenhouse Job Change Monitor — New, Changed & Closed Posts

Follow public Greenhouse job boards over time. Receive current job posts with `baseline`, `new`, `changed`, `unchanged` or `reopened` labels, plus confirmed `closed` observations. Each run saves a snapshot for the next comparison.

Built and maintained by **JAFF Consulting B.V.**, Netherlands.

### Quick start

```json
{
  "boards": ["greenhouse"],
  "stateStoreName": "my-greenhouse-monitor",
  "maxJobsPerBoard": 5000
}
```

1. Enter company board tokens such as `greenhouse`, or their Greenhouse board URLs.
2. Choose a snapshot store name. Keep it unchanged for later runs.
3. Run once to establish the baseline, then run again or use an Apify schedule.
4. Export CSV, Excel or JSON, filtering the `change` column as needed.

The Actor reads the [official public Greenhouse Job Board API](https://docs.greenhouse.io/job-board.html). It accesses published posts only; it does not access applicant data, apply for jobs or require a Greenhouse API key.

### Change labels

| Label | Meaning |
|---|---|
| baseline | A job present during the first successful observation of that board. |
| new | A post ID absent from the previous successful snapshot. |
| changed | The title, location, departments, offices, description, URL, language or public metadata changed. `changedFields` identifies the fields. |
| unchanged | A current post with the same material fields. A source timestamp change alone is ignored. |
| reopened | A previously confirmed missing post ID appears again. |
| closed | The post is missing from two complete, successful observations at least one hour apart. |

`closed` means the post disappeared from the public board. It does not establish whether someone was hired or the role was cancelled. A source that serves an incomplete but internally consistent response twice can still cause a false closure; source truth cannot be guaranteed. Pending missing posts are counted in `RUN_SUMMARY` and are not yet labeled closed.

### Reliable comparisons and limits

Failures, malformed responses, duplicate IDs, total-count mismatches, oversized boards and insufficient spending allowance do not replace that board's previous snapshot. Boards are processed independently. A budget or timeout interruption can leave delivered rows without committing the new baseline; rerunning may repeat change labels. Check `RUN_SUMMARY` for successful boards.

**Run each snapshot store sequentially.** Do not overlap runs using the same store name. The Actor checks for a changed snapshot before writing, but storage is not a transactional lock. Use a separate store name for independent monitors. Different stores establish separate baselines. Deleting the named store resets history. Closed posts are retained for 90 days; a post returning after that may be labeled new.

Up to 50 boards and 20,000 posts per board are supported. The Actor compares complete boards; it never truncates a board and treats omitted rows as closures. All current posts are delivered, including unchanged ones. Each output includes the post ID, title, location, description, source URL, first/last observed times and material change fields. A per-run `OBSERVATION-<board>` record makes the fetched snapshot available in the default key-value store.

The Actor stops safely before the run timeout and after at most 30 minutes. Named snapshot storage persists and consumes your Apify storage allowance. Schedule frequency is your choice; daily runs are a practical starting point. At least one hour between missing observations is required for a closure.

### Pricing

**$1.50 per 1,000 current job posts processed** ($0.0015 each), plus the Actor-start fee shown in the Pricing tab. Baseline and unchanged posts are billable because they are fetched and compared. Confirmed closure records and summary reports have no separate result fee. Repeated runs process and bill the current posts again. The Pricing tab is authoritative; there is no monthly rental fee for this Actor.

### Support

Open an Apify issue with the board token and the relevant `RUN_SUMMARY` error. Keep credentials and private applicant information out of issues. Source formats and availability can change; use the results as timestamped observations.

# Actor input Schema

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

Company board tokens or HTTPS boards.greenhouse.io / job-boards.greenhouse.io URLs. Up to 50 boards.

## `stateStoreName` (type: `string`):

Keep this name for repeat monitoring. Use a different name for an independent monitor. Never run the same store concurrently. Named storage persists and uses Apify storage allowance.

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

Larger boards are skipped whole, preserving their previous snapshot. Increase this value to process them.

## Actor input object example

```json
{
  "boards": [
    "greenhouse"
  ],
  "stateStoreName": "jaff-greenhouse-monitor",
  "maxJobsPerBoard": 5000
}
```

# Actor output Schema

## `results` (type: `string`):

Delivered dataset records. Export as JSON, CSV or Excel.

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

Coverage, source errors and stopping limits.

# 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": [
        "greenhouse"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("jaff-consulting/greenhouse-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 = { "boards": ["greenhouse"] }

# Run the Actor and wait for it to finish
run = client.actor("jaff-consulting/greenhouse-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 '{
  "boards": [
    "greenhouse"
  ]
}' |
apify call jaff-consulting/greenhouse-job-change-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,jaff-consulting/greenhouse-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/vzk0E4pl7VpAqbroR/builds/i9dEzyKNeQ9vJ8kva/openapi.json
