ATS Job Change Monitor — Greenhouse, Lever, Ashby
Pricing
Pay per usage
ATS Job Change Monitor — Greenhouse, Lever, Ashby
Preview current public Greenhouse, Lever, and Ashby job boards directly, then monitor scheduled Task runs for normalized new, changed, active, and confirmed-removed jobs with full datasets and optional signed webhook alerts.
Pricing
Pay per usage
Rating
0.0
(0)
Developer
Marcus Lee
Maintained by CommunityActor stats
0
Bookmarked
1
Total users
0
Monthly active users
19 hours ago
Last modified
Categories
Share
Reliable ATS job changes for downstream data pipelines
Monitor up to 100 direct public Greenhouse, Lever, and Ashby boards from one scheduled Apify Task. Each healthy run returns normalized job data with explicit new, active, changed, and conservatively confirmed removed statuses.
- Stop rebuilding snapshot-diff logic: receive explicit lifecycle statuses and field-level changes.
- Reduce false closure signals: source errors, empty responses, and drops above 50% do not advance removals.
- Use the data your way: keep the complete dataset while filtering webhook alerts by keyword, location, or department.
- Integrate with datasets or webhooks: optional HMAC-signed webhook delivery includes retry and dead-letter handling; cloud delivery validation is still pending.
Start with one company: run a direct current preview first, then create an Apify Task, run a baseline, and schedule delta runs for monitoring.
Not affiliated with, endorsed by, or sponsored by Greenhouse, Lever, or Ashby. Uses their documented public job-posting endpoints only.
Does not support: Workday, LinkedIn, Indeed, Glassdoor, generic career-site discovery, applicant data, browser/proxy bypass, email/Slack/Sheets delivery, or SLA. See Excluded below.
Direct run = safe current preview: a direct default/delta or explicit
previewrun emitsobservation_mode: previewrows without longitudinal state. It still creates the normal default dataset andOUTPUTrecord under your Apify retention settings. Create a Task for baseline + scheduled delta monitoring; directbaselineandself_testrequire a Task.
Preview never sends alerts: filters and alert configuration are ignored in preview, no webhook is validated or delivered, and no Task snapshots, locks, history, caches, request queues, outbox, or DLQ are opened.
One-company direct preview
{"companies": [{"name": "Stripe", "career_page_url": "https://boards.greenhouse.io/stripe"}]}
The schema default is delta, but without a Task this is reported as requested_run_mode: "delta", run_mode: "preview", and mode_coerced: true. Use "run_mode": "preview" to make that choice explicit.
Use cases
- Job-data vendors: maintain an incremental feed without rebuilding snapshot comparison and removal confirmation.
- Job boards and recruiting products: synchronize selected companies' openings and identify field-level changes.
- Talent and market-intelligence teams: track deterministic job-level changes for downstream analysis without receiving opinionated company-level interpretations.
Why choose this Actor?
Broad ATS scrapers are better when you need maximum platform coverage or a one-time snapshot. Hiring-signal tools are better when you want ready-made commercial interpretation. Choose this Actor when reliable job-level change detection matters: explicit lifecycle states, conservative removals, source-anomaly protection, complete datasets, and optional filtered alerts.
Who it is for
This is an integration component for data vendors, job-board operators, and talent-intelligence teams that can consume Apify datasets or webhooks. It is not a consumer job-alert app and does not discover generic career sites.
Supported behavior
- New: first appearance in committed Task state
- Active: unchanged since the prior healthy run
- Changed: normalized title, location, department, employment type, compensation, job URL, or description hash changed
- Removed: absent on two consecutive healthy runs
- Errors, zero-result anomalies, and drops greater than 50% preserve committed state and do not advance removal candidates
Input
Create an Apify Task with this input. Because the current Apify input schema applies required fields unconditionally, companies remains required in self_test mode even though the self-test uses fixed public probes. The alerts object is secret in the Apify input schema, so Apify encrypts its webhook URL and HMAC secret at rest.
| Field | Type | Default | Description |
|---|---|---|---|
companies | array | required | 1–100 {name, career_page_url} records using direct supported ATS board URLs |
filters.keywords | array | [] | Alert-only title keywords |
filters.locations | array | [] | Alert-only location filters |
filters.departments | array | [] | Alert-only department filters |
alerts.webhook_url | string | empty | Public HTTPS webhook destination |
alerts.webhook_secret | string | empty | Optional HMAC-SHA256 secret |
run_mode | enum | delta | delta, baseline, self_test, or preview; direct default/delta is coerced to preview |
first_run_alerts | boolean | false | Alert on jobs committed by a baseline run |
timeout_seconds | integer | 300 | Timeout for each outbound ATS HTTP request |
max_companies_per_run | integer | 100 | Runtime company cap, maximum 100 |
{"companies": [{"name": "Stripe", "career_page_url": "https://boards.greenhouse.io/stripe"},{"name": "Lever Demo", "career_page_url": "https://jobs.lever.co/leverdemo"},{"name": "Linear", "career_page_url": "https://jobs.ashbyhq.com/linear"}],"run_mode": "delta","first_run_alerts": false}
To enable webhooks, add:
{"alerts": {"webhook_url": "https://hooks.example.com/ats-monitor","webhook_secret": "replace-with-a-secret"}}
Run modes
preview: current stateless snapshot, withactiverows,first_seen_at: null, no lifecycle deltas, filters, state, Actor event charges, or webhook processing. It works with or without a Task; ordinary Apify compute and storage still incur platform usage costs.baseline: Task-only; replaces committed jobs for each successfully extracted company, clears inherited removal candidates, and marks current jobsnew. Alerts are sent only whenfirst_run_alertsis true.delta: Task-only monitoring; compares current extraction with the Task's committed snapshot. A direct default/delta run is a preview instead.self_test: Task-only; probes the fixed public Greenhouse (stripe), Lever (leverdemo), and Ashby (linear) boards. It writes a run summary but no job rows. Any declared-platform failure fails the Actor run.
Output
Default dataset
Preview, baseline, and delta runs emit one flat row per returned current or confirmed-removed job. Self-test emits no dataset rows.
| Field | Description |
|---|---|
monitor_id | 16-character hash of an identity-safe normalized returned job URL; identity query parameters such as gh_jid are retained |
company | Input display name |
ats_source | greenhouse, lever, or ashby |
ats_job_id | ATS-provided job identifier |
job_id | {normalized-company}-{ats_job_id} |
title, location, department | Current ATS fields |
employment_type, compensation | Current values when supplied |
job_url | Returned public posting URL |
description_hash | SHA-256 hash; full descriptions are not stored |
first_seen_at | First monitoring observation timestamp, preserved across active/changed runs; null in preview |
last_seen_at | Latest healthy observation timestamp |
status | new, active, changed, or confirmed removed |
change_summary | Field-level old/new values for changed rows |
run_id, run_timestamp | Run correlation fields |
observation_mode | preview for a stateless direct snapshot or monitoring for Task lifecycle rows |
Run summary
A sanitized OUTPUT record is written to the run's default key-value store and linked from the Apify Output tab. It contains counts, requested/effective mode, coercion, monitoring/filter flags, timestamp, and sanitized company errors. Preview adds active_jobs and guarantees zero new_jobs, changed_jobs, and removed_jobs. Durable snapshots, webhook destinations, pending payloads, and DLQ records are not exposed there.
Webhooks
Webhook delivery, signing, retry, and DLQ behavior is locally tested but has not been cloud-validated. Cloud acceptance remains pending.
Delivery is at least once, not exactly once. Receivers must deduplicate X-Webhook-Event-Id.
- Filters apply consistently to new, changed, and removed alert jobs; dataset output remains unfiltered.
- No event is staged when no webhook is configured or all filtered groups are empty.
- Configured destinations must be public HTTPS endpoints. Literal and DNS-resolved loopback, private, link-local, reserved, multicast, unspecified, and cloud-metadata addresses are rejected.
- Delivery pins the validated public IP while retaining the original HTTPS Host/TLS SNI and does not follow redirects.
- Pending events retain their staging destination. A destination mismatch, including a legacy empty destination, is verified into the DLQ before the pending record is tombstoned.
- Failed events retry across runs and move to the DLQ after five total attempts. The DLQ write is read back before the outbox record is removed.
X-Webhook-Signatureis HMAC-SHA256 when a secret is configured.
Example payload:
{"run_id": "run-f654a7aacd38","run_timestamp": "2026-07-10T08:00:00Z","company": "Stripe","new_jobs": 1,"changed_jobs": 0,"removed_jobs": 0,"jobs": [{"status": "new", "title": "Account Executive", "location": "Remote", "department": "Sales", "url": "https://example.com/jobs/123"}]}
Pricing
This Actor runs under Apify's pay-per-usage model with no Actor event charges during an initial validation period (suggested 30 days). The code has no Actor.charge() calls, so there is no enforcement mechanism for the validation period — it is a manual policy. Ordinary Apify compute and storage still incur platform usage costs, including dataset and KV store reads/writes. Per-row billing is intentionally not used because unchanged active rows would bill noise. A separate price decision may follow after measured cloud costs and repeat usage.
State and concurrency
The Actor's normal execution path derives one named KVS per Task: ats-monitor-state-{actor_task_id}. It stores flat snapshot--…, outbox--…, dlq--…, and history--… keys and does not enumerate other Task stores. This is namespace separation in the Actor's code path, not a claim that the named store is a separate platform ACL or tenant security boundary.
There is no atomic lock. Overlapping baseline/delta runs can corrupt logical state; configure schedules so runs cannot overlap.
Failure behavior
| Condition | Behavior |
|---|---|
| Board 404, timeout, malformed response, rate limit | Company skipped, prior state preserved, sanitized error recorded |
| Zero jobs or >50% drop | Removal progression suppressed; prior committed state retained |
Direct default/delta or preview | Stateless current preview; no Task state, filters, or webhooks |
Direct baseline or self_test | Run fails before extraction or side effects |
| Invalid/private webhook destination | Run fails before state/outbox mutation |
| Webhook failure | Durable retry, then verified DLQ after five attempts |
| Any self-test platform failure | Self-test run fails visibly |
Platform and compliance boundary
The Actor uses public job-posting endpoints documented by Greenhouse, Lever, and Ashby. It uses no login, applicant/account data, private endpoints, browser automation, proxy rotation, or anti-bot bypass. It is not affiliated with, endorsed by, or sponsored by those companies; their names and trademarks belong to their owners.
Users are responsible for lawful use and for compliance with Apify's terms, each source site's terms, robots/content signals, and applicable privacy/data rules. “Public” does not grant unrestricted reuse rights. Do not configure boards you are not authorized to monitor. Some sites' terms may prohibit automated access even to public endpoints; Apify may receive takedown requests and the user bears that risk.
The Actor stores only selected public posting fields plus a description hash; it does not store full descriptions or applicant data. Dataset items follow the retention policy of the user's Apify account. Task-local snapshots/outbox/DLQ have no Actor-enforced TTL and remain until overwritten or the associated store/Task data is deleted. Webhook receivers control their own downstream retention.
Excluded
Workday, LinkedIn, Indeed, Glassdoor, generic career-site discovery, applicant data, full description storage, browser fallback, email/Slack/Sheets delivery, and any SLA.
Known limitations
- Maximum 100 companies per run; runtime/cost vary with board size and pagination.
- Public ATS contracts may change; run
self_testbefore relying on a new build. - No automatic retry of rate-limited ATS extraction inside the same run.
- At-least-once webhooks can duplicate deliveries — receivers must deduplicate by
X-Webhook-Event-Id. - No overlapping-run protection. Schedule intervals must exceed the Actor's typical runtime.
- Direct-preview private-cloud acceptance passed for one Greenhouse board and two direct runs. Behavior under rate-limiting, large boards (100+ pages), concurrent users, or sustained scheduled operation has not been cloud-tested.
- Webhook delivery, signing, retry, and DLQ are locally tested, not cloud-validated.
- Self-test mode requires
companiesin the input due to Apify schema constraints, but self-test does not scrape them — it uses fixed public probes.
Development
Cloud acceptance must use a private duplicate Actor so the public Store metadata and README cannot change before the separate publication gate.
uv venv --python 3.14 .venvuv pip install --python .venv/bin/python -r requirements-dev.txtPYTHONPATH=. .venv/bin/pytestPYTHONPATH=. .venv/bin/pytest tests/test_live_probes.py --run-liveAPIFY_CLI_DISABLE_TELEMETRY=1 npx --yes apify-cli@latest validate-schemadocker build -t ats-career-page-monitor .