Company Hiring Signals – Greenhouse, Lever & Ashby Monitor
Pricing
from $4.00 / 1,000 company checkeds
Company Hiring Signals – Greenhouse, Lever & Ashby Monitor
Who is hiring at your target accounts? Finds each company's Greenhouse, Lever, Ashby, Workable, Recruitee, Personio or Workday job board; tracks new roles.
Pricing
from $4.00 / 1,000 company checkeds
Rating
0.0
(0)
Developer
Furkan Sümbül
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
6 days ago
Last modified
Categories
Share
Company Hiring Signals – Greenhouse, Lever, Ashby & more jobs monitor
For B2B sales and RevOps teams, recruiting agencies and investors who need to know which target accounts are hiring, for which roles, and what changed since last week. Paste a list of company domains. The Actor finds each company's public job board on Greenhouse, Lever, Ashby, Workable, Recruitee, Personio or Workday. You get one signal row per company: open roles, new and closed roles since the last run, a breakdown by function, seniority and country, the remote share, sales_hiring / eng_hiring flags and a hiring_surge flag. You can also get one row per job, with salary when the company publishes it.
Run it weekly with a monitorId and it becomes a hiring-intent feed for Clay, HubSpot, Slack or n8n.
Why this Actor
- You pay per company, not per job row. Most job Actors sell raw postings. This one also compares each run with the last and sums it up per company (an illustrative row might read "Acme: 120 open roles, +6 new, 2 closed, 41 in sales"). If you only need the signal, you never pay for the hundreds of individual postings behind it.
- Finds the job board for you. Give it
stripe.comand it works out that Stripe uses Greenhouse. It first guesses the board name across 6 ATS APIs, then checks the company's own/careerspage, which is also where it finds Workday career sites. Each guessed board is validated before use: the company domain must appear in the postings, or the company name must match. Empty, abandoned and same-name boards are rejected (for example, there is a "notion" Workable account that belongs to a London PR agency). - Built for schedules. With a
monitorId, the board each company resolved to and its last snapshot are stored between runs. Repeat runs make one API call per company, andonlyChangesbills only new, closed and changed jobs. - Official public job-board APIs only. These are the unauthenticated endpoints the ATS vendors provide so companies can list their jobs on other sites. No proxies, no logins, no LinkedIn. It respects robots.txt (including each Workday tenant's) and Lever's crawl delay.
- Clean, normalised fields across all 7 ATS: function, seniority, ISO country codes, remote/hybrid/on-site, employment type, salary range, posting date and apply URL.
Supported job boards
| ATS | Board URL example | What we read |
|---|---|---|
| Greenhouse | job-boards.greenhouse.io/stripe | all open jobs, departments, offices, pay range from the posting text |
| Lever | jobs.lever.co/palantir | postings, team, commitment, workplace type, salary range (rate-limited to 1 request/second as Lever asks) |
| Ashby | jobs.ashbyhq.com/ramp | jobs, structured compensation, workplace type, secondary locations |
| Workable | apply.workable.com/huggingface | jobs, locations, remote flag |
| Recruitee | bunq.recruitee.com | offers, salary, remote/hybrid flags |
| Personio | 1komma5grad.jobs.personio.de | XML feed: positions, offices, seniority, descriptions |
| Workday | nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite | the career site's own search API. It reads the newest postings, 20 per request and at most 1 request per second per tenant. The board size comes from Workday's own counts. The tenant's robots.txt is checked first. Posting dates are approximate. Descriptions cost one extra request per job. |
SmartRecruiters is deliberately not supported: its API host's robots.txt disallows automated access. iCIMS, Taleo, SuccessFactors and fully custom careers sites are not supported yet. Those companies get a row with error: "no supported ATS found", and you are not charged for them.
Input
{"companies": ["stripe.com","ramp.com","Notion | notion.so","https://jobs.lever.co/palantir","Hugging Face"],"mode": "signals","monitorId": "weekly-icp-accounts","onlyChanges": false,"functions": [],"countries": [],"titleKeywords": [],"remoteOnly": false,"includeDescriptions": false,"maxJobsPerCompany": 500}
- companies: a domain, a company name, an ATS board URL, or
Name | domain. Domains work best, because they enable validation and the careers-page fallback. Board URLs skip detection. - mode:
signals: one row per company.jobs: one row per job.both: signal rows and job rows in the same dataset. Filter on thetypefield to separate them.
- monitorId: turns on run-to-run diffs. The first run is a baseline.
- onlyChanges: job rows only for new, closed and changed jobs. Signal rows are always emitted.
- Filters (
titleKeywords,functions,countries,remoteOnly): apply to both the signal counts and the job rows.open_jobs_totalalways shows the unfiltered count. - maxJobsPerCompany: the maximum number of job rows per company. For Workday it also sets how many of the newest postings are read. The default is 500, the minimum read is 100, and Workday's own limit is 2,000.
- Advanced:
atsHint: skip probing ATS you know are not used.surgeThresholdPct: default 20.careersPageFallback: default on.maxConcurrency: default 5.
Output
The dataset has two views: Company signals (one row per company, type: "signal") and Jobs (one row per job, type: "job"). Run totals are saved as the RUN_SUMMARY record in the run's key-value store. The run's Output tab, and the output object of the run in the API, link to all three.
Real output from our v0.1.1 smoke run, trimmed. This is a baseline row (first run of a monitor):
{"type": "signal","input": "ramp.com","company_name": "Ramp","domain": "ramp.com","ats": "ashby","board_token": "ramp","board_url": "https://jobs.ashbyhq.com/ramp","open_jobs": 161,"open_jobs_total": 161,"new_since_last_run": null,"closed_since_last_run": null,"net_change_pct": null,"by_function": { "sales": 60, "engineering": 40, "product": 11, "finance": 10, "people": 10, "marketing": 8, "customer_success": 6, "design": 5, "operations": 5, "data": 4, "legal": 2 },"by_seniority": { "mid": 89, "manager": 26, "senior": 17, "lead": 10, "director": 7, "junior": 7, "intern": 5 },"by_country": { "US": 143, "CA": 22, "GB": 12 },"remote_share": 0.3,"sales_hiring": true,"eng_hiring": true,"hiring_surge": null,"newest_job_posted_at": "2026-10-14T22:22:02.947Z","is_baseline": true,"resolved_via": "api_probe","checked_at": "2026-10-14T22:53:42.680Z","error": null}
A company on an unsupported ATS gets an uncharged row:
{"input": "https://careers.smartrecruiters.com/DeliveryHero","ats": null,"open_jobs": null,"error": "SmartRecruiters is not supported (their robots.txt disallows automated access): smartrecruiters:deliveryhero"}
On later runs of the same monitor, the diff fields are filled in. The trimmed output below comes from a test run. To simulate a week of changes, we removed a few jobs from the stored snapshot and edited one title, so these new/closed counts are synthetic:
{"company_name": "Stripe","ats": "greenhouse","open_jobs": 726,"previous_open_jobs": 725,"new_since_last_run": 3,"closed_since_last_run": 2,"changed_since_last_run": 1,"net_change_pct": 0.1,"new_by_function": { "data": 3 },"top_new_titles": ["Data Scientist", "Data Analyst"],"sales_hiring": true,"hiring_surge": false,"is_baseline": false,"resolved_via": "cache","previous_checked_at": "2026-10-14T22:31:39.064Z"}
A job row (mode: "jobs" or "both"):
{"type": "job","company_name": "Ramp","ats": "ashby","job_id": "331dbcfd-3faf-4357-a7d8-3acd854b4b42","title": "Physical Security Lead","function": "operations","seniority": "lead","department": "Security","locations": ["New York, NY (HQ)"],"country": "US","workplace_type": "hybrid","is_remote": false,"employment_type": "full_time","salary_min": 134400,"salary_max": 215000,"salary_currency": "USD","salary_period": "year","posted_at": "2026-10-14T22:22:02.947Z","apply_url": "https://jobs.ashbyhq.com/ramp/331dbcfd-3faf-4357-a7d8-3acd854b4b42","status": "new","changed_fields": [],"first_seen_at": "2026-10-14T22:52:52.934Z","closed_at": null}
A Workday company (real output, trimmed). Only the newest 100 postings were read (maxJobsPerCompany: 100), so partial_read is true: the breakdowns cover jobs_analyzed, while open_jobs is Workday's own count:
{"company_name": "Salesforce","ats": "workday","board_url": "https://salesforce.wd12.myworkdayjobs.com/External_Career_Site","open_jobs": 1545,"open_jobs_total": 1545,"jobs_analyzed": 100,"partial_read": true,"by_function": { "sales": 40, "engineering": 26, "customer_success": 7, "operations": 7, "data": 6, "other": 4, "finance": 3, "product": 3, "marketing": 2, "design": 1, "people": 1 },"by_country": { "US": 32, "IE": 14, "MX": 12, "JP": 9, "GB": 6, "CA": 5, "IN": 4 },"remote_share": 0.1,"resolved_via": "known_board"}
Signal fields:
open_jobs: open roles after filters.jobs_analyzed/partial_read: how many postings were read and classified.partial_readis true only for large Workday sites read up to the cap. In that case, without filters,open_jobsandopen_jobs_totalare Workday's own counts, and the breakdowns cover the newestjobs_analyzedpostings.new_since_last_run/closed_since_last_run/changed_since_last_run:nullon the baseline run.net_change_pct: open roles compared with the previous run.by_function: counts for engineering, sales, marketing, product, design, data, operations, finance, people, legal, customer_success and other.by_seniority: counts for intern, junior, mid, senior, lead, manager, director, vp and executive.by_country: ISO-2 codes. A job listed in two countries counts in both.unknownmeans the location could not be resolved.remote_share: share of open roles that are remote or list a remote location.top_new_titles: up to 10 titles.sales_hiring/eng_hiring: at least one open sales or engineering role.hiring_surge: open roles grew by at leastsurgeThresholdPctand by at least 3 roles, or went from 0 to 5 or more.resolved_via: how the board was found. One ofboard_url,cache,api_probe,careers_pageorknown_board.
Job fields: posted_at_estimated is true when the date comes from a Workday label such as "Posted 3 Days Ago" ("30+ Days Ago" becomes 30 days). With includeDescriptions, Workday rows get the exact posting date from the job detail.
Job statuses: new (first seen in this run), existing (with changed_fields when the title, locations, department, workplace type or salary changed) and closed (no longer on the board, with closed_at). first_seen_at is kept across runs.
The default key-value store also gets a RUN_SUMMARY record with counts for the run.
Pricing (pay per event)
| Event | Price | When it is charged |
|---|---|---|
company-checked | $0.004 | Once per company whose job board was found and read. Includes detection and the signal row. |
job-row | $0.0008 | Each job row in the dataset. With onlyChanges, only new, closed and changed jobs. |
job-description | $0.0007 | Add-on per job row that carries description_text (only when includeDescriptions is on). |
apify-actor-start | Apify default | Once per run. |
Not charged:
- companies where no supported ATS was found, including companies on SmartRecruiters;
- invalid entries;
- API errors;
- anything after your run's spending limit is reached. The Actor stops cleanly and the remaining companies are re-checked on the next run.
Worked monthly example. You monitor 500 target accounts every Monday, which is about 4.3 runs a month.
| Setup | Cost per month |
|---|---|
| Signals only | 500 × 4.3 × $0.004 = $8.60 |
Signals plus mode: "both" with onlyChanges, assuming ~6 changed jobs per account per week | + 500 × 6 × 4.3 × $0.0008 = $10.32, total ≈ $19 |
| One-off export of every open job at 50 companies averaging 150 roles | 50 × $0.004 + 7,500 × $0.0008 = $6.20 |
Run it on a schedule
- Create a Task from this Actor with your company list. Set
monitorId(for exampleicp-weekly) andmode: "signals", or"both"withonlyChanges: true. - Run it once. This is the baseline.
- In Schedules, add the task with a weekly cron such as
0 7 * * 1(Monday 07:00). - Add an integration or webhook on "Run succeeded" (see below) to push the rows where
hiring_surgeorsales_hiringis true.
Use one monitorId per list. Changing filters between runs is safe, because the snapshot always covers all jobs.
Sales recipes
Clay table enrichment.
- In Clay, add an Apify enrichment ("Run Actor") on a table with a
Domaincolumn. - Pass
{"companies": ["{{Domain}}"], "mode": "signals"}and map the returnedopen_jobs,by_function.sales,sales_hiring,hiring_surge,top_new_titlesandboard_urlinto columns. - Use the columns to score and route accounts. For example: "hiring 3+ AEs, or added 5+ roles this month".
For large tables, run the Actor once over the whole list via a Task and import the dataset into Clay instead of one run per row. It is faster, and you still pay $0.004 per company.
Weekly HubSpot + Slack alert via n8n.
- Schedule Trigger, Monday 07:30.
- Apify node: "Run task and get dataset items" for your monitoring task.
- IF node:
{{$json.hiring_surge}} === true || {{$json.new_by_function?.sales}} >= 2. - HubSpot node: "Company → Update", matched by
domain. Writeopen_jobs,sales_hiring,net_change_pctandchecked_atinto custom properties, and optionally create a task for the account owner. - Slack node: post
{{company_name}}: +{{new_since_last_run}} roles ({{net_change_pct}}%), new: {{top_new_titles.join(", ")}}.
Make / Zapier. Use the Apify "Watch Actor runs" / "Actor run finished" trigger, then "Get dataset items". Filter on hiring_surge = true and send to Slack, email or your CRM.
Recruiting agencies. Set mode: "jobs", onlyChanges: true, functions: ["engineering"] and countries: ["DE", "NL"]. You get a daily feed of new engineering roles at your client list, with apply URLs.
Investors and analysts. Track a portfolio or watchlist weekly. Sort by net_change_pct and watch by_function shift from engineering to sales as companies move to go-to-market.
Integrations and API
- Apify API:
POST https://api.apify.com/v2/acts/furkansmbl~company-hiring-signals/run-sync-get-dataset-items?token=…with the input JSON returns the rows directly, which suits small lists. - Webhooks: trigger on
ACTOR.RUN.SUCCEEDEDand fetch the dataset. - Clay, n8n, Make, Zapier, HubSpot (via n8n/Make/Zapier), Google Sheets and Slack: all through Apify's official integrations.
How detection works
- Board URL given. It is used directly.
- Monitor cache. The board found in an earlier run is reused (one API call). If that board disappears or empties, detection runs again in case the company switched ATS.
- Token guessing.
- Tokens tried: the domain stem, the stem without hyphens, the stem plus "hq", the stem without "hq", and the company name without spaces.
- Greenhouse, Ashby, Recruitee and Personio are probed in parallel. Lever and Workable have strict rate limits, so they are probed only when the fast APIs found nothing conclusive.
- Careers-page fallback.
- The homepage,
/careersand/jobsare read, plus same-site careers pages linked from them. Dedicated careers hosts such asjobs.<domain>andcareers.<domain>are read first. Up to 6 pages per company, and robots.txt is respected. - These pages are searched for embed links such as
boards.greenhouse.io/<token>,jobs.ashbyhq.com/<token>,<tenant>.wd5.myworkdayjobs.com/<site>orwd3.myworkdaysite.com/recruiting/<tenant>/<site>. - Workday is never guessed. Tenant and site names can't be derived from a domain, so a Workday site is used only when:
- you pass its URL;
- the company's careers pages link to it; or
- the company is on a small built-in list of tenants we verified live: NVIDIA, Salesforce, Adobe, Intel, Cisco and Workday.
- Before any request to a Workday tenant, its robots.txt is checked for both the career-site path and the
/wday/cxs/API path. Disallowed sites (for example NVIDIA's research and talent-community sites) get an uncharged row explaining why.
- The homepage,
- Validation.
- A guessed board is used only if:
- the company domain appears in its postings or apply URLs; or
- the ATS-declared company name matches; or
- most postings mention the company name.
- Boards with zero jobs, or whose newest posting is over 18 months old, are rejected.
- A board linked from the company's own site is trusted.
- A guessed board is used only if:
How well does it work? We re-tested 44 well-known US and European tech companies with v0.2.0 in October 2026. 32 resolved automatically to the right board, including Stripe, Figma, Notion, Ramp, Datadog, Anthropic, OpenAI, Hugging Face, bunq, Palantir, Spotify, Mistral and Taxfix (found through its careers page). Three of the 12 misses (Delivery Hero, Canva, Wise) use SmartRecruiters, which we deliberately don't read. The other misses (for example Revolut, HubSpot, Zalando, Shopify and Klarna) have one of three causes: an ATS we don't support, a careers site that loads its board only with JavaScript, or a board name that cannot be guessed. For example, Trade Republic's Greenhouse board is traderepublicbank. In those cases, pass the board URL. Workday support was verified live on NVIDIA (2,714 open roles), Salesforce (1,545) and Intel (708). We also checked that a Workday site disallowed by robots.txt (NVIDIA's research site) is skipped without any API call.
Limits and honest FAQ
- Coverage. Seven ATS: Greenhouse, Lever, Ashby, Workable, Recruitee, Personio and Workday. iCIMS, Taleo and SuccessFactors are not covered yet.
- Workday specifics.
- Workday sites are found from a URL, a careers-page link or the small verified list. They are never guessed.
- On big sites we read the newest postings up to
maxJobsPerCompany(Workday stops at 2,000). Whenever a read stops early,partial_readis true, and:- jobs that leave the read window can't be told apart from closed jobs, so
closed_since_last_runisnulland no closed rows are emitted; - new roles and the net change in open roles are still reported.
- jobs that leave the read window can't be told apart from closed jobs, so
- Posting dates are approximate unless descriptions are on.
- Salary is not read from Workday.
- A Workday site whose robots.txt disallows the paths we need is skipped with an uncharged row.
- Why not SmartRecruiters? Its API host (
api.smartrecruiters.com) has a robots.txt that disallows all automated clients except LinkedIn's bot. We respect that. Companies on SmartRecruiters, including SmartRecruiters board URLs you pass in, come back asSmartRecruiters is not supported (their robots.txt disallows automated access). These rows are never charged and make no request to SmartRecruiters. - Can it pick the wrong board? Rarely. A same-name account on another ATS can slip through validation when the real company is not on any supported ATS and the impostor's postings mention the name. Pass the board URL (or
Name | domain) for accounts that matter. - Function and seniority are rule-based from the job title (English plus common German and Dutch terms), with the department and ATS category as fallbacks. Unusual titles and non-Latin-script titles may land in
other, or fall back to the ATS category. - Countries come from ATS country codes when present, otherwise from the location text (cities, states, countries). Unresolvable places show as
unknown. - Salary appears only when the ATS publishes it (Ashby, Lever, Recruitee) or the posting contains a clear range with a currency (common on US Greenhouse boards).
- The first run is a baseline. Diffs start from the second run of the same
monitorId. InonlyChangesmode the baseline run emits no job rows. - Companies with no supported ATS are re-checked after 6 days inside a monitor, so a weekly schedule always re-checks them. If an ATS API was unreachable or rate-limited during detection, the company gets an error row (not charged) and is retried on the next run.
- Speed. Known boards take about one second each. A Workday site takes about 1 second per 20 postings read, so about 25 seconds at the default 500. With descriptions, add 1 second per Workday job row. Detection for unknown companies takes 1–10 seconds, longer when Lever, Workable or a careers page must be checked. Workable rate-limits bursts hard (about 10 requests a minute per IP in our tests), so Workable lookups are paced at one every 3 seconds. A run over hundreds of new companies can therefore take several minutes; later runs reuse the cached board.
- What access does it need to my Apify account? Only Apify's limited permissions. It writes to the run's own dataset and key-value store and, with a
monitorId, to the named key-value storecompany-hiring-signals-<monitorId>that it creates in your account. It cannot read your other Actors, runs or storages. - Personal data. Rows contain company and job data only. Descriptions are stripped of emails, phone numbers, "your contact: …" lines and recruiter tags. Recruiter fields from the APIs are never output.
- Terms of use. The Actor reads public job-board endpoints that the ATS vendors publish for syndicating job ads. Job ads are the employer's content: use them to research and contact companies, not to republish the job descriptions as your own job board.
Changelog
Release notes are on this Actor's Changelog tab.