Hiring Change Monitor — Track New & Removed Jobs avatar

Hiring Change Monitor — Track New & Removed Jobs

Pricing

from $1.80 / 1,000 hiring-change-results

Go to Apify Store
Hiring Change Monitor — Track New & Removed Jobs

Hiring Change Monitor — Track New & Removed Jobs

Monitor public company career pages and ATS boards (Greenhouse, Lever, Ashby, Workday-style sites) for hiring changes. Detect new, removed, reopened, and changed job posts as clean, CSV-friendly hiring-signal events.

Pricing

from $1.80 / 1,000 hiring-change-results

Rating

0.0

(0)

Developer

Delowar Munna

Delowar Munna

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

a day ago

Last modified

Share

Hiring Change Monitor

Track public company career pages and ATS boards and see what changed between runs — every job tagged new, removed, reopened, changed, or unchanged — from Greenhouse, Lever, Ashby, Workday-style sites, and generic career pages. Clean, flat, CSV-ready rows with a transparent hiring-signal score. Built for sales, recruiting, staffing, investors, and market-intelligence teams.

No login, no cookies, no paid data APIs. The actor uses each platform's free public endpoints over HTTP and compares each run against a stored snapshot.

By default (Output mode = All current jobs) every run returns the full current list of jobs, each tagged with its change_type, plus any jobs that were removed — so output is never empty. Switch to Changes only to return just the deltas (new/removed/reopened/changed) for lean scheduled monitoring.

✨ Why this monitor

  • Knows what changed — every row carries a change_type (new / removed / reopened / changed / unchanged) and reason tags, so you see hiring momentum at a glance.
  • Two output modesAll current jobs (full list each run, tagged) or Changes only (deltas vs the previous run). Pick per use case.
  • Multi-source — Greenhouse, Lever, and Ashby public JSON APIs are first-class; Workday-style sites and generic career pages are supported best-effort, including lightweight companyDomains discovery.
  • Scheduled-run friendly — set a monitorId, schedule the actor, and each run compares against the previous snapshot stored in the Apify key-value store.
  • 38 flat fields — change metadata, company/source, job details, compensation (when public), snapshot timing, and previous values for changed jobs. Drops straight into Sheets/Excel/CRMs.
  • Transparent hiring-signal score — rule-based (no AI), explained below.

🧭 Output modes & first-run behavior

Output mode controls what each run returns:

  • All current jobs (default) — returns every job currently on the board, each tagged with its change_type (new / changed / reopened / unchanged), plus jobs that were removed since the last run. Output is never empty, and you can always see the full picture with the changes highlighted.
  • Changes only — returns just the differences versus the previous run (new / removed / reopened / changed) and suppresses unchanged jobs. Leaner output, ideal for scheduled alerting.

First run: for a given monitorId there is no prior snapshot, so every current job is reported as new and is_first_snapshot is true. The actor stores a snapshot at the end of the run. Reuse the same monitorId across scheduled runs so each run compares against the last.


🚀 Quick start — sample inputs

Example 1 — watch a set of ATS boards (full list each run)

{
"startUrls": [
"https://boards.greenhouse.io/stripe",
"https://jobs.lever.co/spotify",
"https://jobs.ashbyhq.com/notion"
],
"monitorId": "core-saas-watchlist",
"outputMode": "all",
"maxChanges": 250,
"keywordFilter": ["data", "security", "enterprise", "ai"],
"excludeKeywordFilter": ["intern"],
"includeCompensation": true,
"proxyConfiguration": { "useApifyProxy": true }
}

Example 2 — changes-only alerting + domain discovery + your own proxy

{
"companyDomains": ["canva.com"],
"startUrls": ["https://jobs.ashbyhq.com/ramp"],
"monitorId": "fintech-watch",
"outputMode": "changes",
"remoteOnly": true,
"departmentFilter": ["engineering", "product"],
"proxyConfiguration": {
"useApifyProxy": false,
"proxyUrls": ["http://user:pass@proxy.iproyal.com:12321"]
}
}

Provide at least one of startUrls or companyDomains. Leave monitorId empty to derive a stable one from your inputs, or set your own so scheduled runs share a snapshot.

The actor blocks Apify Residential proxy; if you need residential routing, supply your own provider via proxyConfiguration.proxyUrls as shown. See 🚦 Proxy policy below.


📦 Output

The dataset has one view: Hiring change events — a 38-column flat table.

Hiring Change Monitor — Hiring change events table view

Output fields (38)

monitor_id, change_type, change_reason_tags, hiring_signal_score, hiring_signal_label, company_name, company_domain, source_type, source_url, job_id, job_fingerprint, job_title, department, location, remote_status, employment_type, seniority, job_url, apply_url, description_text, compensation_min, compensation_max, compensation_currency, compensation_period, posted_at, first_seen_at, last_seen_at, previous_job_title, previous_location, previous_department, previous_remote_status, previous_compensation_min, previous_compensation_max, snapshot_previous_at, snapshot_current_at, is_first_snapshot, input_value, scraped_at.

Sample record — Hiring change event

Real run output (default All current jobs mode, on a scheduled re-run). This row is tagged unchanged with snapshot_previous_at filled in — on the first run the same job would be new with is_first_snapshot: true. The description_text is truncated here for readability.

{
"monitor_id": "core-saas-watchlist",
"change_type": "unchanged",
"change_reason_tags": "unchanged|senior_role|technical_role|public_compensation|remote_role",
"hiring_signal_score": 60,
"hiring_signal_label": "high",
"company_name": "Databricks",
"company_domain": "databricks.com",
"source_type": "greenhouse",
"source_url": "https://boards.greenhouse.io/databricks",
"job_id": "8367021002",
"job_fingerprint": "greenhouse:8367021002",
"job_title": "Staff Backend Software Engineer- (AI Platform)",
"department": "Engineering",
"location": "San Francisco, California",
"remote_status": "remote",
"employment_type": "",
"seniority": "lead",
"job_url": "https://databricks.com/company/careers/open-positions/job?gh_jid=8367021002",
"apply_url": "https://databricks.com/company/careers/open-positions/job?gh_jid=8367021002",
"description_text": "At Databricks, we are passionate about enabling data teams to solve the world's toughest problems — from making the next mode of transportation a reality to...",
"compensation_min": 192000,
"compensation_max": 260000,
"compensation_currency": "USD",
"compensation_period": "year",
"posted_at": "2026-04-09T20:51:23.000Z",
"first_seen_at": "2026-06-05T01:34:38.356Z",
"last_seen_at": "2026-06-05T04:19:54.506Z",
"previous_job_title": "",
"previous_location": "",
"previous_department": "",
"previous_remote_status": "",
"previous_compensation_min": null,
"previous_compensation_max": null,
"snapshot_previous_at": "2026-06-05T04:18:32.768Z",
"snapshot_current_at": "2026-06-05T04:19:54.506Z",
"is_first_snapshot": false,
"input_value": "https://boards.greenhouse.io/databricks",
"scraped_at": "2026-06-05T04:19:54.506Z"
}

For a new job, change_type is new and change_reason_tags starts with new_job. For a changed job, the relevant previous_* fields are populated (e.g. previous_job_title) and change_reason_tags includes tags like title_changed, location_changed, department_changed, remote_status_changed, description_changed, or compensation_changed.


🎯 Hiring-signal score

Transparent rule-based score (0–100) computed from visible fields — no AI, no external enrichment.

Base score by change type: reopened 65 · new 55 · removed 40 · changed 35 · unchanged 5.

ModifierPoints
Senior / lead / manager / director / executive role+15
Revenue/growth department (sales, marketing, partnerships, CS)+10
Technical department (engineering, data, product, security, AI)+10
Public compensation present+10
Remote / hybrid+10
Multiple jobs newly opened at the same company this run+10
Expansion-oriented title (founding, launch, growth, enterprise, …)+10
Internship / temporary role−10

Score is floored at 0 and capped at 100.

Labels: very_high (80–100) · high (60–79) · medium (30–59) · low (0–29).

change_reason_tags is a pipe-separated list combining the change reasons and the signal reasons — e.g. new_job|senior_role|technical_role|remote_role|public_compensation.


💰 Pricing

Pay-Per-Event. One flat event per saved record (final per-event price is configured on the Apify console):

EventCharged when
hiring-change-resultOnce per row that passed all filters and was successfully written to the dataset.

So your bill is simply results_saved × price_per_event. Choose your mode to control cost:

  • All current jobs (default) — you're charged for every job each run (including unchanged ones), because the full current list is saved. Best when you always want the complete, change-tagged picture.
  • Changes only — you're charged only for the deltas (new/removed/reopened/changed). A run with no changes saves and charges nothing. Best for lean scheduled monitoring.

The actor honors the user-configured per-run spending cap (Apify eventChargeLimitReached): it caps how many rows it will save up-front to what the limit can pay for, and stops cleanly the moment the cap is reached.

Not charged in either mode:

  • Duplicates (deduplicated by source+job_id, canonical URL, then title/company/location keys).
  • Rows filtered out by keyword / department / location / remote / change-type filters.
  • unchanged jobs when Changes only mode is selected.
  • Failed source fetches, invalid inputs, and snapshot storage.

🚦 Proxy policy

Use Apify Datacenter proxy or no proxy for normal runs — both work reliably for public ATS APIs and career pages at this actor's conservative concurrency.

Apify Residential proxy is not supported. The actor will fail at startup if proxyConfiguration.apifyProxyGroups includes RESIDENTIAL. Reason: in pay-per-event actors, residential bandwidth (~/GB) is billed to the developer, not the run user, so a single bandwidth-heavy run could exceed the per-result event revenue.

If you genuinely need residential routing, supply your own residential provider via the proxy editor's Custom proxy URLs field — that traffic goes through your provider, not Apify, and is unaffected:

http://user:pass@proxy.iproyal.com:12321
http://user:pass@proxy.brightdata.com:22225
http://user:pass@proxy.oxylabs.io:7777

📊 Run summary

After each run, a RUN_SUMMARY entry is written to the default key-value store:

{
"monitor_id": "core-saas-watchlist",
"inputs_total": 3,
"successful_inputs": 3,
"failed_inputs": 0,
"sources_detected": { "greenhouse": 1, "lever": 1, "ashby": 1 },
"raw_results_found": 1260,
"current_jobs_processed": 1000,
"changes_found": 118,
"results_saved": 100,
"new_jobs": 72,
"removed_jobs": 18,
"reopened_jobs": 4,
"changed_jobs": 24,
"unchanged_jobs": 882,
"duplicates_removed": 37,
"filtered_out": 18,
"charged_events": 100,
"blocked_requests": 0,
"retry_count": 9,
"snapshot_previous_at": "2026-06-01T10:00:00.000Z",
"snapshot_current_at": "2026-06-02T10:00:00.000Z",
"is_first_snapshot": false,
"runtime_seconds": 184,
"scraped_at": "2026-06-02T10:03:04.000Z"
}

charged_events equals the number of successfully saved records. Snapshots and the cross-run history index are stored in a named key-value store (hiring-change-monitor-state) so they survive between scheduled runs.


⚙️ Filters

FilterEffect
outputModeall (every current job, tagged — default) or changes (deltas only, unchanged suppressed).
changeTypesOptional. Restrict output to specific change types. Empty = use the Output mode default.
keywordFilter / excludeKeywordFilterCase-insensitive match across title, department, location, description, company, tags. Exclusion wins.
departmentFilterKeep only matching departments/teams. Missing department fails when set.
locationFilterKeep only matching locations. Missing location fails when set.
remoteOnlyKeep only remote/hybrid postings.
deduplicateDrop duplicate jobs across inputs (recommended ON).

Filters are applied after change detection but before any dataset push or event charge.


🚧 Limitations (V1)

  • Public data only: no login, cookies, or member-only content; no candidate/applicant data.
  • Core ATS are first-class: Greenhouse, Lever, and Ashby use stable public JSON APIs. Workday-style sites and generic career pages are best-effort — when a public listing cannot be reached, the input is marked failed/unsupported and the run continues with the others.
  • companyDomains does lightweight discovery only (homepage + common careers paths, following links to supported ATS boards). It does not crawl the whole site.
  • Compensation is included only when the public source exposes it (e.g. Lever salary ranges, Ashby compensation, or amounts visible in the description). No paid enrichment.
  • Reopened detection relies on a compact history index kept per monitorId; a job that returns after being absent is reopened, otherwise new.
  • maxResults caps raw current jobs processed; maxChanges caps saved records per run (changes in Changes only mode, all rows in All current jobs mode).
  • No webhooks/alerts in V1 — schedule the actor and consume the dataset/API.

❓ FAQ

Do I need an account, cookies, or API keys? No. The actor only uses each platform's free public endpoints.

Why is everything new on my first run? There is no prior snapshot yet, so every current job is reported as new and is_first_snapshot is true. Run again later with the same monitorId to see real changes.

My second run returned nothing / only a few rows — is it broken? No. In Changes only mode a run with no changes correctly returns nothing (there were no new/removed/reopened/changed jobs since the last run). If you'd rather always get the full current list with each job's status, use the default All current jobs mode — every run returns every job tagged new / changed / unchanged / etc.

Will I be charged for unchanged jobs? Only in All current jobs mode, where the full list (including unchanged) is saved and charged each run. In Changes only mode, unchanged jobs are suppressed and never charged.

How do I track the same companies over time? Set a stable monitorId and schedule the actor. Each run compares against the snapshot from the previous run under that ID.

What counts as a changed job? A material change in a tracked visible field — title, location, department, remote status, compensation, or description — for a job that exists in both snapshots. The previous_* fields show the old values.

Can I export to CSV? Yes — every field is flat (no nested objects). Use Apify's CSV / Excel export, or call the dataset API with format=csv.


🛠️ Technical notes

  • Stack: Node.js 22 · Apify SDK 3 · Crawlee HttpCrawler · Cheerio (career-page HTML only). No browser.
  • Endpoints: Greenhouse Job Board API, Lever Postings API, Ashby Job Posting API (all free/public); Workday CxS and career-page HTML best-effort.
  • State: snapshot + history diff in a named Apify key-value store, namespaced by monitorId.
  • Concurrency: min=1, max=5 (conservative; tune after real runs).
  • Memory: 1 GB min · 2 GB default · 4 GB max.
  • Proxy: Apify Proxy enabled by default; custom configs accepted; Apify Residential rejected at startup.