Job Postings API & Change Tracker: Greenhouse, Lever, Ashby +4 avatar

Job Postings API & Change Tracker: Greenhouse, Lever, Ashby +4

Pricing

from $1.00 / 1,000 job posting or company checks

Go to Apify Store
Job Postings API & Change Tracker: Greenhouse, Lever, Ashby +4

Job Postings API & Change Tracker: Greenhouse, Lever, Ashby +4

Hiring signals and job postings API for Greenhouse, Lever, Ashby, SmartRecruiters, Workable, Recruitee and Personio. Paste company domains, get every open job, then only new, closed and edited ones each run, plus a surge or freeze signal per company. Slack alerts. No API key.

Pricing

from $1.00 / 1,000 job posting or company checks

Rating

0.0

(0)

Developer

Fulox

Fulox

Maintained by Community

Actor stats

1

Bookmarked

2

Total users

1

Monthly active users

2 days ago

Last modified

Share

Job Postings API & Change Tracker: Greenhouse, Lever, Ashby, SmartRecruiters, Workable, Recruitee, Personio

What you get: paste company domains (acme.com) and get every open job posting from Greenhouse, Lever, Ashby, SmartRecruiters, Workable, Recruitee and Personio in one format: title, department, location, remote, posted salary range, link. On every later run you get only what changed: new, closed and edited postings, plus one hiring-signal row per company (surge, freeze, leadership hires, tools mentioned).

Who it is for: sales and RevOps teams that want lead signals from job postings, recruiters and agencies watching client and competitor boards, investors tracking portfolio companies, and researchers who count roles and posted salaries. It is a job change monitor and hiring signals tracker, not a one-off scraper.

No login, no API key, no personal data.

Next run, only what changed: new, closed and edited job postings across companies

▶️ Watch the 18-second demo: paste domains, the job board is found, every open job in one table, then only what changed, hiring signals and a Slack alert.

⚡ Try it in 30 seconds

Click Start with the default input. It reads three public boards (ashby:linear, greenhouse:vercel, lever:spotify) and lists the postings of the last 7 days (about 10 in October 2026) plus a row per company, for about $0.02. For every open posting (about 200, roughly $0.30 on the Free or Starter plan, less on higher plans, see Pricing), clear postedWithinDays before your first run; if you already ran the default input, also give a new watchlistName, because later runs of a watchlist list only changes. Then replace the boards with your own domains.

Run it again later, and you get only the changes.

✨ Key features

  • 🌐 Domains in, board found for you. Paste acme.com or https://acme.com/careers. The job board is detected for free.
  • 🧩 7 applicant tracking systems in one output format: Greenhouse, Lever, Ashby, SmartRecruiters, Workable, Recruitee, Personio.
  • 🔄 Change tracking. First run lists everything. Later runs deliver only new, closed and changed postings.
  • ✏️ Edits with old values. A changed posting lists which fields changed (changedFields) and what they were before (previous): title, department, team, location, workplace type, employment type, salary.
  • 📈 Hiring signals per company. surge, freeze, expanding, contracting or steady, with reasons. Plus open roles now vs 7 and 30 days ago, leadership hires (titles only), seniority mix, roles per function, functions hiring for the first time, reposted roles, remote share and tools mentioned (Kubernetes, Snowflake, dbt and more).
  • 💰 Salary ranges, also from the posting text. Ashby and other feeds give the pay range directly (salarySource: "structured"). Greenhouse does not, so the range is read from the posting text by fixed rules, no AI (salarySource: "description"). On the 80 preset companies, 55% of the postings now carry a range (it was 18% from feed fields alone). These are posted ranges, not actual pay.
  • 🆚 Competitor hiring report. With two or more companies you also get one free report row: open roles, 7 and 30 day change, function and seniority mix, remote share, median posted salary by function and top tools per company, each marked above or below your list's average.
  • 🧰 Skills and tools ranking. The same row lists the 25 most mentioned tools across all postings in the run, with the functions asking for them, and which tools are rising since your last report.
  • 🏢 Ready-made company lists. Add presets such as ai-labs, dev-tools, fintech or b2b-saas: 20 well-known companies each, boards found for you.
  • 🗓️ Fresh postings only. postedWithinDays keeps postings from the last N days, so a first run lists and charges only what is recent.
  • 📰 Pick the rows you pay for. statuses delivers only new, only closed (an expired-jobs feed) or any mix.
  • 📝 Description text, optional. includeDescription adds a plain-text description; descriptionIncludes keeps postings that mention your keywords.
  • 💶 Volume discounts. $1.50 per 1,000 results, down to $1.00 on Business and Enterprise plans.
  • 🔔 Alerts. Optional Slack or webhook digest, sent only when something changed. Free.
  • 🎯 Filters and watchlists. Filter by title, department, location or remote. Keep separate watchlists per list or per client.
  • 🔑 No login, no API key. Reads public job board feeds.

👥 Who it is for

  • 💼 Sales and RevOps. Watch your target accounts. A company with a surge signal, a first-ever legal or security posting, or a new "Head of" hire is a reason to reach out this week. Push signal and summary into your CRM or Clay.
  • 🧑‍💼 Recruiters and agencies. Track client and competitor boards. Get new postings matching titleIncludes: ["Data Engineer"] in your locations every morning, without opening 40 career pages.
  • 📊 Investors and VCs. Monitor portfolio and pipeline companies. A freeze (nothing new, many roles closed) or a contracting signal shows up in the data before it shows up in the news.
  • 🔍 Market researchers. Count roles per function and tools mentioned across a list of companies, and track how that changes week by week.

🎯 Use cases

  • Sales hiring signals. Put your target accounts in boards with "emitBaseline": false and schedule it weekly. Send company rows whose signal is surge, or that list newFunctions or leadershipHires, to Slack, your CRM or Clay.
  • Recruiting lead signals. A company that opens its first legal, security or sales posting is hiring in a new area. Use titleIncludes and locations to get only the roles you place.
  • Job change monitor for competitors. Track a rival's new, closed and edited postings (a changed title or location comes with the old value in previous) and the open-role trend in the report row.
  • Salary from job postings. Run a presets list with postedWithinDays: 30 and read salaryMin, salaryMax, salaryCurrency and salarySource. These are posted ranges, not actual pay.
  • Expired jobs feed. Set "statuses": ["closed"] to get only postings that disappeared (see the job board feed recipe).

🚀 How to use it

  1. Paste company domains (acme.com), career pages, or boards such as greenhouse:vercel, ashby:linear, lever:spotify, smartrecruiters:Ubisoft2, workable:mindvalley, recruitee:bunq, personio:acme.
  2. Optional: add filters, a watchlistName, and a Slack webhook.
  3. Click Start. The first run lists every open posting (turn emitBaseline off to only record the starting point).
  4. Open Schedules and create a schedule, for example weekly on Monday at 07:00, with your saved input. Each scheduled run delivers only the changes.
  5. For Slack alerts, paste an incoming webhook URL into slackWebhookUrl. A short message lists the companies that changed and up to 5 new titles each.
  6. Download as JSON, CSV or Excel, or send the data to Google Sheets, Make, Zapier, n8n or Clay.

📥 Input

Input form of the Job Postings API and Change Tracker with company domains and filters

Example input:

{
"boards": ["acme.com", "https://globex.com/careers", "greenhouse:vercel"],
"titleIncludes": ["engineer", "data"],
"remoteOnly": false,
"watchlistName": "target-accounts",
"emitBaseline": true,
"presets": ["dev-tools"],
"postedWithinDays": 30,
"statuses": ["baseline", "new", "closed"],
"slackWebhookUrl": "https://hooks.slack.com/services/XXX/YYY/ZZZ"
}
FieldWhat it doesDefault
boardsUp to 100 entries. A company domain or career page (the board is detected, free), or <ats>:<slug> / a board link. Invalid entries are skipped free, duplicates removed.ashby:linear, greenhouse:vercel, lever:spotify
presetsAdd ready-made lists of 20 companies each: ai-labs, dev-tools, fintech, b2b-saas (checked 2026-10-14). Added after your own entries, deduplicated, and counted in the 100-company limit. A run finds up to 20 new company domains, so use one or two presets per first run.none
emitBaselineOn a company's first run, output every open posting (after your filters) as a baseline row; each row is charged. Off = record the starting point only.on
titleIncludes, titleExcludesKeep postings whose title contains any of these words / drop those that do (not case sensitive, up to 50 words).none
departmentsKeep postings whose department, team or function (engineering, product, design, data, sales, marketing, customer success, finance, legal, people, operations, security) contains any of these.none
locations, remoteOnlyKeep postings in these places / marked remote.none, off
postedWithinDays1 to 365. Keep postings first published (or, if the board gives no date, first seen) within this many days. Older postings are not listed or charged; company counts stay whole.no limit
statusesWhich posting rows to deliver: baseline, new, closed, changed. ["closed"] is an expired-jobs feed. The company read is still one result.all
includeDescriptionAdd a plain-text description (HTML removed, up to 5,000 characters) to posting rows. Same price.off
descriptionIncludesKeep postings whose description contains any of these words (not case sensitive). Closed rows carry no text, so they are not listed while this is set.none
reportAdd the free competitor hiring report row (see Output). Default: on when two or more companies were read; false turns it off; true also works with one company.automatic
watchlistNameLetters, digits, -, _. Each name keeps its own snapshots: several lists or several clients, without mixing their changes.default
slackWebhookUrlSlack incoming webhook. A digest is posted only when something changed. Free.none
webhookUrlAn https URL that gets the same digest as JSON. Private and local addresses are refused. Free.none
resetStateForget the saved snapshot and saved board lookup of the listed companies; this run is their first run again.off

Filters, postedWithinDays, statuses and descriptionIncludes decide which posting rows you get (and pay for). The company row always counts the whole board (openCount); matchingOpenCount counts the postings that pass your filters. Changing a filter later never creates fake new or closed postings.

📤 Output

Dataset table of job postings with status, title, department, location and link

A posting row (type: "posting", status baseline, new, closed or changed):

{
"type": "posting", "status": "new", "ats": "lever", "board": "acme", "domain": "acme.com", "company": "Acme",
"jobId": "223f9722-3579-4ff2-b123-e4cc7b0c1a64", "title": "Head of Legal",
"department": "Legal", "team": null, "location": "Berlin", "workplaceType": "hybrid", "employmentType": "Permanent",
"salaryMin": 150000, "salaryMax": 190000, "salaryCurrency": "EUR", "salaryInterval": "1 YEAR", "salarySource": "description", "salaryOte": false,
"function": "legal", "seniority": "director", "remote": false, "techMentions": ["Snowflake"], "reposted": false,
"postedAt": "2026-10-13T17:15:08.174Z", "url": "https://jobs.lever.co/acme/223f9722-3579-4ff2-b123-e4cc7b0c1a64",
"applyUrl": "https://jobs.lever.co/acme/223f9722-3579-4ff2-b123-e4cc7b0c1a64/apply",
"firstSeenAt": "2026-10-14T08:00:00.000Z", "closedAt": null, "daysOpen": null,
"changedFields": [], "previous": null, "runAt": "2026-10-14T08:00:00.000Z"
}

With includeDescription on, posting rows also carry "description": "..." (plain text, at most 5,000 characters; null for closed rows and for Workable and SmartRecruiters, which publish no text).

For an edited posting (status: "changed"), changedFields and previous carry the edit, for example "changedFields": ["location"] and "previous": {"location": "Berlin"}.

Hiring signals per company: surge, freeze, leadership hires and open roles in 7 and 30 days

A company / signals row (type: "board"):

{
"type": "board", "ats": "lever", "board": "acme", "domain": "acme.com", "company": "Acme", "status": "ok", "baseline": false,
"openCount": 31, "previousOpenCount": 28, "newCount": 3, "closedCount": 0, "changedCount": 1, "netChange": 3,
"openCount7dAgo": 28, "openCount30dAgo": 29, "matchingOpenCount": 31,
"byFunction": {"engineering": 14, "sales": 9, "legal": 1, "other": 7}, "newFunctions": ["legal"],
"seniorityMix": {"intern": 0, "junior": 1, "mid": 2, "senior": 9, "lead": 3, "manager": 4, "director": 1, "exec": 0, "unknown": 11},
"leadershipHires": {"count": 1, "titles": ["Head of Legal"]}, "remoteShare": 0.26,
"signal": "surge", "signalReasons": ["3 new of 28 (11%)", "first legal posting"],
"repostedCount": 0, "techStack": {"Kubernetes": 4, "Snowflake": 1}, "newTech": ["Snowflake"],
"summary": "Acme: +3 new (2 engineering, 1 legal), first legal hire, 1 leadership hire, 31 open, +7% in 30 days",
"fetchedAt": "2026-10-14T08:00:00.000Z", "runAt": "2026-10-14T08:00:00.000Z"
}

Salary: where the numbers come from

salarySource says it. structured: the feed publishes the range (Ashby, Lever, Recruitee when filled in). description: the feed has none, so the range is read from the posting text by fixed rules (no AI): $150,000 - $200,000 USD per year, £60k–£75k, €55.000 - €70.000 brutto/Jahr, $45–$60/hr, OTE $200K. The feed's own range always wins. salaryInterval is 1 YEAR, 1 HOUR, 1 MONTH, 1 WEEK or 1 DAY; salaryOte: true marks on-target earnings (base plus variable pay).

Figures that are revenue, funding, bonus, equity, perks, phone numbers or years are ignored, and so are ranges with no currency or no sign that they are pay. A bare $ is read as USD unless the words next to the figures name Canada, Australia, New Zealand or Singapore.

Coverage, measured live on 2026-10-14 over the 80 preset companies (11,883 postings): 54.7% carry a range (Greenhouse 58.9%, Ashby 60.4%; SmartRecruiters and Recruitee 0%, their feeds carry no pay or no text). Before, only the feed fields counted: 17.8%. On 150 hand-checked snippets from real postings the rules made no wrong reading (about 97.6% of the pay ranges in the text are found; typos such as $140,00 and hourly figures with no unit are missed). These are posted ranges, not actual pay: they skew to US tech and to roles that must publish a range by law.

The competitor hiring report row

With two or more companies, every run adds one free report row (never charged; turn it off with "report": false). It compares the companies of the run:

{
"type": "report", "watchlist": "default", "runAt": "2026-10-14T08:00:00.000Z",
"basis": "posted pay ranges in open postings (read from the feed or the posting text), not actual pay",
"companiesCompared": 18, "totalOpenPostings": 2225, "postingsWithToolData": 2225, "partial": false,
"watchlistAverage": {"openRoles": 123.61, "growth30dPct": 4.2, "remoteShare": 0.58, "postedPayShare": 0.56},
"companies": [{
"company": "Vercel", "ats": "greenhouse", "board": "vercel", "openRoles": 90,
"netChange7d": 3, "netChange30d": 8, "growth30dPct": 9.8,
"functionMix": {"sales": 0.26, "engineering": 0.24, "finance": 0.08},
"seniorityMix": {"unknown": 0.53, "manager": 0.24, "senior": 0.1, "director": 0.06},
"remoteShare": 0.09, "postedPayShare": 0.66, "postingsWithPay": 59,
"medianPostedSalaryByFunction": [{"function": "engineering", "currency": "USD", "interval": "1 YEAR", "n": 18, "median": 245000}],
"topTools": [{"tool": "Next.js", "postings": 28}, {"tool": "TypeScript", "postings": 21}],
"vsAverage": {"openRoles": "below", "growth30dPct": "above", "remoteShare": "below", "postedPayShare": "above"}
}],
"tools": [{"tool": "AWS", "postings": 660, "share": 0.297, "topFunctions": [{"function": "engineering", "postings": 257}, {"function": "sales", "postings": 91}],
"previousShare": 0.281, "shareChange": 0.016, "rising": true}],
"risingTools": [{"tool": "AWS", "shareChange": 0.016}],
"previousReport": {"runAt": "2026-10-07T08:00:00.000Z", "companiesInCommon": 18}
}
  • Per company: the numbers count the whole board (filters do not change them). netChange7d and netChange30d are null until your saved history is long enough. functionMix and seniorityMix are shares of open roles. medianPostedSalaryByFunction is the median of the yearly midpoint of posted ranges per function and currency, only where at least 3 ranges exist; on-target-earnings and one-sided ranges are left out and one role posted in several places counts once. vsAverage says above, below or average against the average of the companies in the run.
  • Tools: the 25 most mentioned tools of a fixed list of about 60 (AWS, Kubernetes, Snowflake, dbt, React and so on) across all postings, with the share of postings and the top 3 functions. The share counts postings whose feed carries text (not Workable or SmartRecruiters; postingsWithToolData says how many).
  • Rising: each run keeps a small summary per watchlist (watchlistName) and compares the share of postings with the previous report, over the companies in both. A tool is rising when its share grew by at least one point and it appears in at least 3 postings. The first run has nothing to compare with.
  • A run that was restarted or stopped early marks the report partial: true and does not replace the saved summary.

Other free rows: report (see above), input (invalid entry, detected, ats_not_found, detection_deferred, duplicate), digest (one per run), and run (a run that delivered nothing, with the reason). The dataset has five views (postings, companies, report, issues, digest). Views choose columns, not rows, so filter the type column to see one kind of row.

💸 Pricing

Pay per result, cheaper on higher Apify plans. A result is one posting row delivered (listed on the first run, new, closed or edited) or one company read successfully.

Your Apify planPer 1,000 resultsPer result
Free and Starter$1.50$0.0015
Scale$1.25$0.00125
Business$1.00$0.0010
Enterprise$1.00$0.0010

Apify's standard Store tiers apply: Free and Starter are the Free and Bronze tiers, Scale is Silver, Business is Gold, Enterprise is Platinum and Diamond. The run's cost line shows what you were charged.

Free: domain detection, boards that cannot be read (not found, error, empty feed), invalid entries, the digest row, the competitor report row, and Slack and webhook alerts.

Worked examples:

ResultsFree / StarterScaleBusinessEnterprise
🆕 First run, 3 companies, about 200 open jobs (the default boards, postedWithinDays cleared)about 203$0.30$0.25$0.20$0.18 to $0.16
📅 Weekly check of 50 companies, about 50 postings changedabout 100$0.15$0.125$0.10$0.09 to $0.08
🏢 presets: ["fintech"] (20 companies), emitBaseline off20$0.03$0.025$0.02$0.018 to $0.016
😴 A quiet company, per run1$0.0015$0.00125$0.0010$0.0010
  • 🔕 Want to skip the first-run listing? Turn emitBaseline off, set postedWithinDays, or filter by title, department, location, remote or description, and you pay only for matching rows. A first run of the ai-labs preset lists about 4,300 postings (October 2026); with emitBaseline off it is 20 results.
  • 📰 Only closed or only new postings? statuses limits the rows (and the charge) to those. The company read still counts as one result.

A company that just posted 100 roles costs more, because each delivered posting is one result. Set a maximum cost per run to stay in control.

🔌 Alerts and integrations

Slack digest listing companies that changed and their new job titles

  • 💬 Slack, simplest: paste an incoming-webhook URL into slackWebhookUrl. Sent only when something changed, free.
  • 🔗 Apify Slack integration: Actor > Integrations > Slack, trigger "Run succeeded", to get a message after every run.
  • 📧 Gmail / email: Integrations > Gmail (or Make) on "Run succeeded", attach the dataset as CSV.
  • 📑 Google Sheets: Integrations > Google Sheets appends the dataset after every run. Filter the type column to posting.
  • ⏰ Schedule: Schedules > create, daily or weekly with your saved input. Quiet days cost one result per company.
  • 🔁 n8n / Make / Zapier: run the Actor, send posting rows to a sheet or CRM, and board rows with signal other than steady to Slack. A webhookUrl delivers the digest as JSON without polling.
  • 🧱 Clay: add an HTTP API column calling this Actor's run-sync-get-dataset-items endpoint with your Apify token and body {"boards":["{{domain}}"],"emitBaseline":false}. Read signal, summary and leadershipHires from the board row.
  • 📰 Job board feed: see the recipe below.
  • 🛠️ API: call it from any language and read the dataset.
  • 🤖 MCP and AI agents: call with {"boards": ["acme.com"], "emitBaseline": false} and read the dataset. Row types: posting (status baseline|new|closed|changed), board (status ok or a failure), input, digest, report (free comparison of the companies in the run, with tool ranking), run. To answer "what changed since yesterday", run the same input again under the same watchlistName and read the new, closed and changed rows plus each board row's signal and summary. Failed-board, input and run rows are free.

📰 Recipe: a job board feed

Keep a job board, newsletter or Sheet fed with fresh postings, and take down the ones that closed.

  1. Create a Task with your companies (or presets) and {"postedWithinDays": 7, "statuses": ["new"]}. The first run records the starting point and delivers only company rows; every later run delivers just the postings that appeared since the last run.
  2. Add a second Task with {"statuses": ["closed"]} on the same companies if you also need to remove listings. This is an expired-jobs feed: each closed row has jobId, url and daysOpen.
  3. Schedule each Task daily (Schedules > create).
  4. Read the result from the Task's last run: https://api.apify.com/v2/actor-tasks/TASK_ID/runs/last/dataset/items?format=json (add &token=YOUR_TOKEN for a private account), or use the Google Sheets integration to append every run. format=xml and format=csv work too.
  5. Rows of other types (board, digest) share the dataset; keep the rows where type is posting.

Give each Task its own watchlistName, so each keeps its own snapshots.

🆚 How it compares (October 2026)

Facts from each Actor's Apify Store page and README, read 2026-10-14. Prices per 1,000 results are not like-for-like: some include enrichment.

This Actorfantastic-jobs/career-site-job-listing-apibovi/greenhouse-lever-ashby-job-scraper
Coverage7 ATSs: Greenhouse, Lever, Ashby, SmartRecruiters, Workable, Recruitee, Personio54 to 58 ATS platforms and 200,000+ career sites (its README and Store text differ)6 ATSs: Greenhouse, Lever, Ashby, Recruitee, SmartRecruiters, Personio
InputCompany domains, careers URLs or <ats>:<slug>, up to 100; presets; filters; watchlistsSearch by title, location, organization, domain, ATS and more; time window 1h to 6 months; up to 5,000 jobsCompany list by ATS and name, or name auto-detect; preset lists; keyword filters
Change trackingYes: new, closed and edited postings against a saved snapshot per watchlistIncremental time windows of new and indexed jobsYes: only new since last run, plus closed
Closed jobs includedYes, same run and priceSeparate Actor (expired jobs, $0.75 per daily run)Yes, in diff mode
Edits with old valuesYes (changedFields, previous)Not on Apify (README: its own API only)Not documented
EnrichmentRule-based seniority, function, remote, tools mentioned; optional description text; no AIAI enrichment (salary, skills, experience level), company data, derived company domainSeniority and remote type
AlertsSlack and webhook digest, freeNot documentedNot documented
Price per 1,000$1.50 Free/Starter, $1.25 Scale, $1.00 Business and Enterprise$12 Free/Starter, $6 Scale, $4 Business and up, plus a small start fee$1.50 Free, falling to $1.425 on Enterprise

Where fantastic-jobs does better: it searches a database of 54+ ATS platforms and about 200,000 career sites, adds AI enrichment and company data, and finds jobs at companies you are not watching yet. This Actor reads only the seven ATSs above, from the companies you give it, and is built to tell you what changed there.

🏆 Why this one

  • 🌐 Start from a domain, not from a board name you have to look up.
  • 🧩 Seven applicant tracking systems in one run and one output format.
  • 🏢 Presets, a time window and a status filter: start from a ready-made list and pay only for the rows you want.
  • 💶 Volume discounts down to $1.00 per 1,000.
  • 🧾 Edits with old values, not just new and removed postings.
  • 📈 Signals, not just rows: surge, freeze, leadership hires, first-time functions, reposts, tools.
  • 🔔 Built-in Slack and webhook alerts, free.
  • 🔓 No login, no API key, no personal data.
  • 💵 Quiet companies cost almost nothing (one result per company per run).

⚠️ Limitations

  • Seven ATSs only: Greenhouse, Lever, Ashby, SmartRecruiters, Workable, Recruitee, Personio. Not BambooHR, Workday, Teamtailor or LinkedIn, and no EU-hosted Greenhouse or Lever boards yet. A company on another system shows ats_not_found (free).
  • Salary is posted ranges, not actual pay. Only postings that show a range have one: about half on Greenhouse and Ashby, none on SmartRecruiters (the list feed has no pay) and few elsewhere; European postings rarely do. Ranges read from the text can still be wrong in odd wording (about 1 in 40 snippets is missed, typos included), and a figure with no unit that is clearly an hourly rate is not read. A $ without a country hint is USD. Medians in the report are posted ranges of the open postings in your run, not market benchmarks.
  • Domain detection reads the company's public pages (home, /careers, /jobs) for a link to a supported board. If there is none it tries the company name on each ATS, and a guessed board must carry the company's name. For a wrong or missing match, list the board yourself (lever:acme) and use resetState to drop the saved lookup. A miss is remembered for 7 days.
  • SmartRecruiters boards over 2,000 postings are refused (board too large) rather than half read.
  • Workable and SmartRecruiters feeds carry no posting text, so techMentions is empty for them, description is null and a descriptionIncludes keyword never matches. Closed rows keep no text either. function, seniority, remote and techMentions come from fixed word lists, not AI: good for filtering and counting, not exact.
  • Changes are found by comparing with the snapshot saved at your last run, so you see changes between your runs, not their exact time. Tracked edits: title, department, team, location, workplace type, employment type and salary. Description edits are ignored.
  • Presets are lists of 20 companies checked on 2026-10-14; a company that moves to an unsupported ATS shows ats_not_found (free). A run finds at most 20 new company domains (the rest follow on the next run), so one preset fits one run.
  • If a feed suddenly returns no postings, or loses over 90% of them, the company is reported as suspicious_empty and no closures are reported until the count recovers (or you use resetState).
  • Companies are read one after another, about one second apart. Lever boards take 7 to 16 seconds each; the 40-minute run limit fits 100 boards. If one company has more rows than your remaining maximum cost per run allows, the run stops with a free run row naming that company and the cap it needs.
  • Slack and webhook URLs are checked by their text (https, a public-looking host; Slack only hooks.slack.com), not resolved.

❓ FAQ

Is this legal? The Actor reads public job board feeds that companies publish so that candidates and search engines can find their jobs. It collects no personal data: no recruiter names and no applicant data. Job description text is only included when you turn on includeDescription; it is the public posting text. As always, you are responsible for how you use the data and for following the laws that apply to you.

Is this a Greenhouse, Lever and Ashby jobs scraper? It reads the public job board feeds of those three and of SmartRecruiters, Workable, Recruitee and Personio, and keeps a snapshot so the next run shows only what changed.

How do I get an alert when a company posts new jobs or starts hiring? Add the company domain, set a watchlistName, schedule the run and paste a Slack webhook into slackWebhookUrl. You get a message only when something changed. Each company row also carries a signal (surge, freeze, expanding, contracting or steady).

How does change tracking work? At the end of each run the Actor saves a snapshot of every company's open postings in a key-value store named ats-hiring-signals-state in your account. The next run compares the live board with that snapshot and delivers the new, closed and edited postings.

What are watchlists? A watchlistName keeps its own snapshots. Use one per list of companies, or one per client, so their changes never mix. Use one watchlist per company per account unless each task has its own name.

How fresh is the data? Live from each company's public job feed at the time of the run.

How much will it cost me? $1.50 per 1,000 results on the Free and Starter plans, $1.25 on Scale, $1.00 on Business and Enterprise. See the worked examples in Pricing. Detection, failed boards, the digest, the report row and alerts are free, and a maximum cost per run caps your spend.

Can I avoid paying for the first-run listing? Yes. Turn emitBaseline off, set postedWithinDays, or use filters so only matching postings are listed.

Can I get salary from job postings? Yes, as a posted range. It comes from the feed where the company publishes it (for example Ashby, Lever, Recruitee and Personio when set), otherwise from the posting text by fixed rules (salarySource: "description"). Postings with no range have empty salary fields. See "Salary: where the numbers come from".

Do alerts cost anything? No. If a Slack or webhook URL is refused or fails, the digest row says why (alert) and the run still succeeds.

Something is wrong or missing. Who do I ask? Open an issue on the Actor's Issues tab. I reply within 12 hours.

🛠️ Need a custom feature?

Another ATS, a different signal, a field you need? Open an issue on the Actor page and describe it.

Changelog

  • 1.3.6 (2026-10-21): Personio: the feed's own salary range is read (salarySource: structured), and postings written only in the board's own language (for example German) now get their description, tools and descriptionIncludes matching.
  • 1.3.4 (2026-10-18): a refused charge stops the run with an explanation instead of failing it.
  • 1.3 (2026-10-17): the default input lists only the last 7 days' postings (postedWithinDays: 7), so a first try costs about $0.02 instead of $0.30; clear it before a watchlist's first run for the full listing.
  • 1.3 (2026-10-14): salary ranges read from the posting text when the feed has none (salarySource, salaryOte; 55% of postings on the 80 presets, up from 18%); free competitor report row (company comparison, median posted salary by function, top 25 tools, rising tools, report input) with a new report view; no change to prices or to the single result event.
  • 1.2 (2026-10-14): volume discounts by Apify plan ($1.50 down to $1.00 per 1,000); postedWithinDays; statuses (new-only and expired-jobs feeds); company presets (ai-labs, dev-tools, fintech, b2b-saas); optional description text and descriptionIncludes; job board feed recipe.
  • 1.1 (2026-10-14): SmartRecruiters, Workable, Recruitee and Personio; company domains in with free board detection; hiring signals per company (leadership hires, seniority, functions, surge/freeze, reposts, remote share, tool mentions); filters; watchlists; digest row; Slack and webhook alerts; emitBaseline is now on by default; dataset views.
  • 1.0 (2026-10-14): first version (Greenhouse, Lever, Ashby).