ATS Jobs Monitor
Pricing
from $1.50 / 1,000 new jobs
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
Maintained by CommunityActor stats
1
Bookmarked
2
Total users
1
Monthly active users
3 hours ago
Last modified
Categories
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
closedrow 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_SUMMARYsaying 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
- Enter the companies you want to watch — homepage URLs, or board URLs if you already have them.
- Optionally narrow with keywords, a location, workplace type or employment type.
- Run it once. The first check has no history, so it reports every open role it finds and becomes your baseline.
- 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 → Schedules → Create 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(andclosedJobs) and drop the event when both are0. - 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
| Field | Description |
|---|---|
changeType | new |
jobId | The employer system's own job ID — the key the monitor remembers |
title | Job title |
companyName / companyDomain | Hiring company, and its domain as a join key |
source | Which employer system the role was read from |
atsBoardToken | The company's board identifier on that system |
jobUrl | Canonical job posting URL |
applyUrl | Canonical application link — the real one, not a redirect |
location | Location as published |
locationCity / locationRegion / locationCountry | Structured location |
remote | yes, no, hybrid or unknown, from the employer's own field |
employmentType | Full-time, Part-time, Contract, Internship, ... |
department / team | Org placement, where the employer publishes it |
seniority | Seniority read from the title |
requisitionId | The employer's internal requisition number |
postedAt | True first-published date, not "2 weeks ago" |
updatedAt | When the employer last edited the posting |
salary | Salary as published |
salaryMin / salaryMax / salaryCurrency / salaryPeriod | Parsed salary |
descriptionText / descriptionHtml | Full job description |
firstSeenAt / lastSeenAt | When this monitor first and last saw the role |
careersUrl | The board the role was read from |
scrapedAt | When the role was read |
skills / requirements / responsibilities / benefits | Populated when AI enrichment is switched on |
Closed role
| Field | Description |
|---|---|
changeType | closed |
jobId / title / jobUrl | The role that came off the board |
companyName / companyDomain / source | Which company and system |
firstSeenAt | When the monitor first reported the role |
lastSeenAt | The last check that still found it on the board |
closedAt | When 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
| Event | Free plan | Gold 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.