ATS Job Change Monitor — Greenhouse, Lever, Ashby avatar

ATS Job Change Monitor — Greenhouse, Lever, Ashby

Pricing

Pay per usage

Go to Apify Store
ATS Job Change Monitor — Greenhouse, Lever, Ashby

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

Marcus Lee

Maintained by Community

Actor 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 preview run emits observation_mode: preview rows without longitudinal state. It still creates the normal default dataset and OUTPUT record under your Apify retention settings. Create a Task for baseline + scheduled delta monitoring; direct baseline and self_test require 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.

FieldTypeDefaultDescription
companiesarrayrequired1–100 {name, career_page_url} records using direct supported ATS board URLs
filters.keywordsarray[]Alert-only title keywords
filters.locationsarray[]Alert-only location filters
filters.departmentsarray[]Alert-only department filters
alerts.webhook_urlstringemptyPublic HTTPS webhook destination
alerts.webhook_secretstringemptyOptional HMAC-SHA256 secret
run_modeenumdeltadelta, baseline, self_test, or preview; direct default/delta is coerced to preview
first_run_alertsbooleanfalseAlert on jobs committed by a baseline run
timeout_secondsinteger300Timeout for each outbound ATS HTTP request
max_companies_per_runinteger100Runtime 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, with active rows, 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 jobs new. Alerts are sent only when first_run_alerts is 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.

FieldDescription
monitor_id16-character hash of an identity-safe normalized returned job URL; identity query parameters such as gh_jid are retained
companyInput display name
ats_sourcegreenhouse, lever, or ashby
ats_job_idATS-provided job identifier
job_id{normalized-company}-{ats_job_id}
title, location, departmentCurrent ATS fields
employment_type, compensationCurrent values when supplied
job_urlReturned public posting URL
description_hashSHA-256 hash; full descriptions are not stored
first_seen_atFirst monitoring observation timestamp, preserved across active/changed runs; null in preview
last_seen_atLatest healthy observation timestamp
statusnew, active, changed, or confirmed removed
change_summaryField-level old/new values for changed rows
run_id, run_timestampRun correlation fields
observation_modepreview 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-Signature is 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

ConditionBehavior
Board 404, timeout, malformed response, rate limitCompany skipped, prior state preserved, sanitized error recorded
Zero jobs or >50% dropRemoval progression suppressed; prior committed state retained
Direct default/delta or previewStateless current preview; no Task state, filters, or webhooks
Direct baseline or self_testRun fails before extraction or side effects
Invalid/private webhook destinationRun fails before state/outbox mutation
Webhook failureDurable retry, then verified DLQ after five attempts
Any self-test platform failureSelf-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_test before 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 companies in 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 .venv
uv pip install --python .venv/bin/python -r requirements-dev.txt
PYTHONPATH=. .venv/bin/pytest
PYTHONPATH=. .venv/bin/pytest tests/test_live_probes.py --run-live
APIFY_CLI_DISABLE_TELEMETRY=1 npx --yes apify-cli@latest validate-schema
docker build -t ats-career-page-monitor .