ATS Jobs Monitor avatar

ATS Jobs Monitor

Pricing

from $1.50 / 1,000 new jobs

Go to Apify Store
ATS Jobs Monitor

ATS Jobs Monitor

The ATS Jobs Monitor watches company job boards on a schedule and returns only what changed — roles opened since the last check, and roles closed — with canonical apply URLs, true first-published dates, structured locations, remote and employment type, department and salary.

Pricing

from $1.50 / 1,000 new jobs

Rating

0.0

(0)

Developer

FalconScrape

FalconScrape

Maintained by Community

Actor stats

1

Bookmarked

2

Total users

1

Monthly active users

3 hours ago

Last modified

Share

🚀 ATS Jobs Monitor

Watch any company's job board on a schedule and get back only what changed — the roles that opened since the last check, and, if you want them, the roles that closed.

Every other company-jobs scraper is one-shot: run it today, run it tomorrow, and tomorrow you get the same 400 roles back plus a handful of new ones — and you pay for all 400 again. This monitor remembers every job it has already reported to you and hands you only the difference.

It reads the employer's own applicant-tracking system rather than a job board, so each new role arrives with its canonical apply link, its true first-published date, and the remote / employment-type / salary fields the employer actually published — not a guess inferred from the text.

Point it at a list of companies, put it on a daily schedule, and you have a hiring feed instead of a job dump.

✨ Features

  • Only what changed, ever. Each check is compared against everything the monitor has reported before, keyed on the employer system's own job ID. A role you've already been given is never delivered twice and never charged twice.
  • Closed roles too — a signal nothing else in this category gives you. When a job comes off a board, it was filled, pulled or expired. Switch it on and you get a closed row for it, which is what keeps a job index or a hiring-signal product from going stale. Off by default.
  • A closure is never guessed at. A role is only reported closed after two consecutive checks miss it. A board that answers oddly for one run, or a company whose board is too large to read in full, is left alone rather than reported as a wave of layoffs.
  • Companies in, boards found for you. Give it homepage URLs. The monitor locates each company's job board once and then re-checks that same board on every run, so results stay consistent from day to day instead of drifting between sources.
  • Eight employer systems read at the source: Greenhouse, Lever, Ashby, Workday, Oracle HCM, SmartRecruiters, Workable and Radancy. You can also paste a board URL directly.
  • Filters that genuinely filter. Keyword, location, workplace type and employment type are applied to the fields the employer publishes. A role whose employer doesn't publish a field is kept, not silently dropped — "this company doesn't say" is not the same answer as "no".
  • Nothing is silently dropped. If a check stops at your new-job limit, the rest stay unreported and are picked up next run. Every run writes a RUN_SUMMARY saying what it found per company, what it already knew, which companies it couldn't resolve, and why any closure check was skipped.
  • Memory that survives. The seen-list is stored permanently in your own account, so it works across months of scheduled runs.
  • No proxy, no account, no cookies. Only publicly published job data.

🛠️ How It Works

  1. Enter the companies you want to watch — homepage URLs, or board URLs if you already have them.
  2. Optionally narrow with keywords, a location, workplace type or employment type.
  3. Run it once. The first check has no history, so it reports every open role it finds and becomes your baseline.
  4. Put it on a schedule. Every check after that returns only what's new.

⏰ Setting up the schedule

In the Apify Console, open the actor → SchedulesCreate new schedule, pick a frequency and attach this actor with your input. Nothing else is needed — the monitor picks up its own memory on every run.

Daily is the usual choice. Hiring boards don't change by the hour, and a slower schedule costs you fewer checks for the same set of roles.

🔑 Monitor ID

By default the memory is tied to the exact companies and filters you entered — so adding a company or changing a keyword starts a fresh monitor, and the next check re-reports everything as new.

If you expect to edit the watch over time, set a Monitor ID such as competitor-hiring. Runs sharing an ID share one memory, so you can change the list without being sent — and charged for — your whole feed again.

🔔 Getting notified when something changes

Open the actor → Integrations and connect Slack, email, a webhook, Zapier or Make to the ACTOR.RUN.SUCCEEDED event.

One thing to set up carefully, because it is easy to get wrong: Apify only has run-state events — there is no "only if the dataset has rows" trigger. A check that finds nothing new succeeds with an empty dataset by design, so a plain "notify me on success" hook will ping you every day whether or not there is anything to see, which is exactly the noise this actor exists to remove.

Filter on the run's own numbers instead. Every run writes a RUN_SUMMARY record to its key-value store:

{ "newJobs": 12, "closedJobs": 3, "jobsOnBoards": 486, "alreadyReportedBefore": 474 }
  • Webhook / Zapier / Make — read newJobs (and closedJobs) and drop the event when both are 0.
  • Slack / email integration — send the dataset and add a filter step for a non-empty result.

Set that up once and a quiet week is genuinely silent.

📋 Output Fields

Every row carries changeType, which is either new or closed.

New role

FieldDescription
changeTypenew
jobIdThe employer system's own job ID — the key the monitor remembers
titleJob title
companyName / companyDomainHiring company, and its domain as a join key
sourceWhich employer system the role was read from
atsBoardTokenThe company's board identifier on that system
jobUrlCanonical job posting URL
applyUrlCanonical application link — the real one, not a redirect
locationLocation as published
locationCity / locationRegion / locationCountryStructured location
remoteyes, no, hybrid or unknown, from the employer's own field
employmentTypeFull-time, Part-time, Contract, Internship, ...
department / teamOrg placement, where the employer publishes it
senioritySeniority read from the title
requisitionIdThe employer's internal requisition number
postedAtTrue first-published date, not "2 weeks ago"
updatedAtWhen the employer last edited the posting
salarySalary as published
salaryMin / salaryMax / salaryCurrency / salaryPeriodParsed salary
descriptionText / descriptionHtmlFull job description
firstSeenAt / lastSeenAtWhen this monitor first and last saw the role
careersUrlThe board the role was read from
scrapedAtWhen the role was read
skills / requirements / responsibilities / benefitsPopulated when AI enrichment is switched on

Closed role

FieldDescription
changeTypeclosed
jobId / title / jobUrlThe role that came off the board
companyName / companyDomain / sourceWhich company and system
firstSeenAtWhen the monitor first reported the role
lastSeenAtThe last check that still found it on the board
closedAtWhen the monitor confirmed it was gone

Fields the employer does not publish are null or empty — never invented.

📊 Sample Output Data

A newly opened role:

{
"changeType": "new",
"companyDomain": "figma.com",
"companyName": "Figma",
"source": "greenhouse",
"atsBoardToken": "figma",
"jobId": "gh-5364702004",
"title": "Account Executive, Emerging Enterprise (Berlin, Germany)",
"location": "Berlin, DE",
"locationCity": "Berlin",
"locationCountry": "Germany",
"remote": "unknown",
"department": "Sales",
"requisitionId": "1334",
"postedAt": "2024-11-01T06:05:10-04:00",
"updatedAt": "2026-07-22T05:37:08-04:00",
"jobUrl": "https://boards.greenhouse.io/figma/jobs/5364702004",
"applyUrl": "https://boards.greenhouse.io/figma/jobs/5364702004?gh_jid=5364702004",
"firstSeenAt": "2026-08-09T06:30:42.000Z",
"lastSeenAt": "2026-08-09T06:30:42.000Z",
"closedAt": null
}

A role that has come off the board:

{
"changeType": "closed",
"companyDomain": "notion.so",
"companyName": "Notion",
"source": "ashby",
"jobId": "ashby-4f2c1a90-1b7e-4a55-9a3e-2c6d8b0f7e11",
"title": "Staff Software Engineer, Platform",
"jobUrl": "https://jobs.ashbyhq.com/notion/4f2c1a90-1b7e-4a55-9a3e-2c6d8b0f7e11",
"firstSeenAt": "2026-08-06T06:43:40.000Z",
"lastSeenAt": "2026-08-08T06:43:40.000Z",
"closedAt": "2026-08-09T06:43:43.368Z"
}

Each run also writes a RUN_SUMMARY to the key-value store, with a per-company breakdown:

{
"firstRun": false,
"newJobs": 12,
"closedJobs": 3,
"jobsOnBoards": 486,
"alreadyReportedBefore": 474,
"companiesWatched": 5,
"companiesResolved": 5,
"companiesUnresolved": 0,
"remembered": 495,
"truncated": false,
"truncationReason": null
}

💰 Pricing

EventFree planGold plan
Monitor check (per run)$0.008$0.003
New job found$0.004$0.0015
Job closed (optional)$0.002$0.001

You pay for a check and for changes, not for the board. Reading a company's full board is what makes it possible to tell a genuinely new role from one you already have — and to tell a closed role from a board that simply failed to answer — so that work is covered by the flat per-check fee, however many roles the board holds.

A daily monitor over five companies turning up about 15 new roles a day costs roughly $2.00/month on the Free plan and $0.80/month on Gold. A quiet day costs the check fee alone — under a cent.

Closed-job reporting is off unless you switch it on, and adds nothing when it is off.

❓ FAQ

What happens on the very first run? It has no history, so every open role it finds is new and gets reported — that's your baseline, and it is charged. If you're watching large employers, keep Maximum new jobs per run low to spread that first intake over several runs; nothing is lost, the next check picks up where the last one stopped.

I added a company and got everything again. Why? By default the memory is tied to the exact watch. Set a Monitor ID and reuse it, and you can edit the list and the filters freely without losing history.

One of my companies came back unresolved. Some employers — mostly large enterprises whose careers site is built entirely in the browser — publish no board that can be found from their homepage. The monitor names those companies in the log and in RUN_SUMMARY with every route it tried, rather than quietly returning nothing for them. If you know the board URL, paste it in directly and it will be used as-is.

Why didn't it report any closed jobs for one of my companies? Closure detection is deliberately skipped whenever the monitor cannot prove it read the whole board: the read failed, the board returned nothing, the run stopped early at its new-job limit, or the board is larger than Maximum jobs read per company. The reason is recorded per company in RUN_SUMMARY. Raising that limit fixes the last case.

Will it ever send me the same role twice? Only if a run is interrupted between delivering a role and saving its memory — the monitor deliberately errs toward a visible duplicate rather than silently dropping a role you paid to be told about.

Does zero results mean it's broken? No. Zero changes is the normal outcome on a quiet day and the run succeeds. A run only fails when no company could be resolved at all, or when every board came back empty — which is what a genuine breakage looks like from here.

I want every open role, not just what changed. Use the Company Career Page Scraper, which returns a company's full board on every run.