Company Hiring Tracker - Greenhouse, Lever, Ashby and more avatar

Company Hiring Tracker - Greenhouse, Lever, Ashby and more

Pricing

from $3.20 / 1,000 companies

Go to Apify Store
Company Hiring Tracker - Greenhouse, Lever, Ashby and more

Company Hiring Tracker - Greenhouse, Lever, Ashby and more

Track which companies are hiring and what changed since your last run. Paste domains, careers pages or job board URLs (Greenhouse, Lever, Ashby, Workable, SmartRecruiters, Recruitee, Personio, Teamtailor, Workday). One record per company: open jobs, velocity, new and closed jobs. Or one per job.

Pricing

from $3.20 / 1,000 companies

Rating

0.0

(0)

Developer

Amit Aviv

Amit Aviv

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

4 hours ago

Last modified

Share

Paste a list of companies (website domains, careers pages, job board URLs or names) and get one hiring-signal record per company: how many jobs are open, in which functions, departments, countries and seniority levels, how fast new jobs are being posted, and which jobs were opened and closed since your previous run. Or switch to one record per job. The actor reads the public job board APIs that the job board vendors publish for showing a company's jobs on its own website, so a run takes seconds to minutes, needs no proxy, and you pay only for the companies (or jobs) delivered. A company whose job board cannot be found is not charged; it is named in the run summary with the reason.

Built for:

  • Outbound sales: companies that are hiring are companies that are spending. Spot accounts that just opened sales, data or security roles, or that are growing a team in your region.
  • VC and market research: hiring velocity and team mix across a portfolio or a market map, run weekly.
  • Competitor tracking: see the roles your competitors open and close, week by week.

What you get

Mode "companies" (default): one record per company

  • companyName, companyDomain, ats (which job board), boardUrl, careersPageUrl, matchedBy (how the board was found)
  • isHiring, openJobs, countComplete
  • Hiring velocity: jobsPostedLast7Days, jobsPostedLast30Days, jobsPostedLast90Days, jobsWithPostingDate, medianJobAgeDays, newestJobPostedAt, oldestJobPostedAt
  • Team mix: jobsByFunction, jobsByDepartment, jobsBySeniority, leadershipJobs, jobsByCountry, jobsByLocation (top 30), jobsByWorkplace, remoteJobs, jobsByEmploymentType, keywordMatches (counts for your title keywords)
  • latestJobs: the 10 most recently posted jobs (job id, title, department, function, seniority, location, date, link)
  • Changes since the previous run: isFirstRun, previousRunAt, openJobsAtPreviousRun, openJobsChange, newJobsSinceLastRun, closedJobsSinceLastRun, functionChanges, newJobs and closedJobs (up to 100 listed each; the counts are always complete)

Mode "jobs": one record per open job

  • title, companyName, companyDomain, ats, boardUrl, department, team, function, seniority, employmentType, workplace
  • location, locations, country (ISO code)
  • postedAt, daysSincePosted, updatedAt, url, applyUrl
  • salaryMin, salaryMax, salaryCurrency, salaryPeriod, salaryText where the board publishes pay
  • description (plain text, optional)

Both modes write a run summary (OUTPUT_SUMMARY in the run's key-value store): what was requested, delivered and charged, which companies could not be resolved and why, and why the run stopped.

What it does not return: recruiter or hiring-manager names, e-mails or phone numbers, applicant data, anything behind a login, and jobs from career sites built on job boards it does not support (see Limits).

Supported job boards

Job boardRecognised fromFound by name guessPosting datesSalary
Greenhouseboards.greenhouse.io/…, job-boards.greenhouse.io/…, embedsyesyesjobs mode: pay-range block or a "Salary: $X - $Y" line
Leverjobs.lever.co/…, jobs.eu.lever.co/…yesyeswhen published
Ashbyjobs.ashbyhq.com/…yesyeswhen published
Workableapply.workable.com/…, yesyesno
SmartRecruiterscareers.smartrecruiters.com/…, jobs.smartrecruiters.com/…noyesno
Recruiteeyesyeswhen published
Personioyes (only with a live board page)yesno
Teamtailoryesyesno
Workdaynoup to 30 days only (see Limits)no

How a company is resolved to its job board

  1. A job board URL (https://jobs.lever.co/palantir) or board:name (greenhouse:stripe) is used as given. matchedBy: "board-url".
  2. A domain or careers page URL (stripe.com, https://linear.app/careers): the actor reads the page given, the homepage, then up to a few careers pages they link to (4 pages by default), looking for links to or embeds of a supported job board. A board found there is used only when it is verifiably the company's: the page embeds it, or the board's name resembles the domain or the website's name, or its job pages live on the company's domain, or its company name matches. A careers page that links another company's board (remote.com/jobs links the job site Jobgether's Lever board) is not taken for the company's own. matchedBy: "careers-page".
  3. Otherwise, or for a company name (Stripe), the name is tried on Greenhouse, Lever, Ashby, Workable, Recruitee, Personio and Teamtailor. A board found this way is used only when it has open jobs and is verifiably the company's: its job pages are on the company's domain (guess-domain), its company name is the same (guess-name), or its job texts name the company (guess-text). Turn this off with Try the company name on the job boards.
  4. A LinkedIn, Crunchbase, Y Combinator, Wellfound, Glassdoor or Indeed company URL is read as the company name in it (linkedin.com/company/back-market is tried as "back market"); other pages of such sites are refused with a message. The directory's own job board is never used.

Measured on 101 well-known tech company domains (run TysjMPtBua6LNzPIZ, 2026-10-14): 72 resolved (26 from the careers page, 46 by a verified name guess), 29 not: 21 link no supported board (their careers sites are custom or use an unsupported vendor), 5 refused requests from the cloud (HTTP 403/429), 2 link another company's board, 1 links a board that no longer exists. Unresolved companies were not charged. For them, paste the job board URL.

How to use

  1. Paste your companies into Companies, one per line. Domains from a CRM export work as they are.
  2. Keep Output on "One record per company" for hiring signals, or choose "One record per job".
  3. Press Start. For a weekly signal, save the input as a task and schedule it: from the second run on, every company record says what changed.

Input

FieldTypeDefaultWhat it does
companieslist of textnone (required)Domains, careers pages, job board URLs, board:name, company names, or LinkedIn/Crunchbase/Y Combinator company URLs. Duplicates (the same board reached twice) are delivered and charged once.
modecompanies or jobscompaniesOne record per company (event "company") or per job (event "job").
keywordslist of textemptyTitle keywords. Companies mode: counted per keyword in keywordMatches. Jobs mode: only jobs whose title contains one of them.
locationKeywordslist of textemptyJobs mode: only jobs whose location or country contains one of them.
remoteOnlyyes/nonoJobs mode: only remote jobs.
postedWithinDaysnumberemptyJobs mode: only jobs posted in the last N days (undated jobs are left out).
maxJobsPerCompanynumber500Jobs mode: at most this many jobs per company, newest first.
includeDescriptionyes/nonoJobs mode: add the description as plain text (see Legal for what is removed).
onlyNewyes/nonoJobs mode: deliver and charge only jobs not delivered by earlier runs.
monitorKeytextemptyName of an independent tracking history (e.g. one per list or per client).
guessBoardsyes/noyesTry the company name on the job boards when the website links none.
maxPagesPerWebsitenumber4Pages of a company's website read to find its board (1-10).
proxyConfigurationproxyoffNot needed for the job board APIs; can help when company websites refuse cloud requests.
{
"companies": ["stripe.com", "https://jobs.lever.co/palantir", "linear.app"]
}

Example result (companies mode)

From run rZw7wrA18GdDIZ4GC on 2026-10-14 (build 0.1.12), shortened (latestJobs cut to one entry); this was a repeat run, so the change fields are filled:

{
"id": "ashby:linear",
"companyName": "Linear",
"companyDomain": "linear.app",
"ats": "ashby",
"boardUrl": "https://jobs.ashbyhq.com/Linear",
"careersPageUrl": "https://linear.app/careers",
"matchedBy": "careers-page",
"isHiring": true,
"openJobs": 30,
"countComplete": true,
"jobsPostedLast7Days": 0,
"jobsPostedLast30Days": 4,
"jobsPostedLast90Days": 11,
"jobsWithPostingDate": 30,
"medianJobAgeDays": 133,
"newestJobPostedAt": "2026-10-06T00:55:56.942Z",
"oldestJobPostedAt": "2021-04-27T20:13:45.158Z",
"jobsByFunction": {"Design": 7, "Engineering": 7, "Customer Success & Support": 5, "Sales": 5, "Data & AI": 2, "Marketing": 2, "Product": 2},
"jobsByDepartment": {"GTM": 15, "Product": 13, "Operations": 2},
"jobsBySeniority": {"mid": 23, "lead": 7},
"leadershipJobs": 0,
"jobsByCountry": {"US": 18, "unknown": 8, "GB": 2, "SG": 2},
"jobsByLocation": {"North America": 18, "Europe": 8, "London": 2, "Singapore": 2},
"jobsByWorkplace": {"remote": 27, "hybrid": 3, "onsite": 0, "unspecified": 0},
"remoteJobs": 27,
"jobsByEmploymentType": {"full-time": 30},
"keywordMatches": null,
"latestJobs": [
{"jobId": "6dd5500a-4bd4-4f57-ba09-7b07acefecf4", "title": "Account Executive, Enterprise", "department": "GTM", "function": "Sales", "seniority": "mid", "location": "Singapore", "postedAt": "2026-10-06T00:55:56.942Z", "url": "https://jobs.ashbyhq.com/Linear/6dd5500a-4bd4-4f57-ba09-7b07acefecf4"}
],
"isFirstRun": false,
"previousRunAt": "2026-10-14T17:05:19.976Z",
"openJobsAtPreviousRun": 30,
"openJobsChange": 0,
"newJobsSinceLastRun": 0,
"closedJobsSinceLastRun": 0,
"functionChanges": {},
"newJobs": [],
"closedJobs": [],
"input": "linear.app",
"scrapedAt": "2026-10-14T18:27:37.043Z"
}

On a first run for a company (isFirstRun: true) the change fields are null and the lists empty. newJobs, closedJobs and latestJobs hold jobId, title, department, function, seniority, location, postedAt and url of each job.

Example result (jobs mode)

From run qiRhXpdHzjZIaTzVN on 2026-10-14, unedited:

{
"id": "greenhouse:stripe:8267736",
"jobId": "8267736",
"title": "Software Engineer",
"companyName": "Stripe",
"companyDomain": "stripe.com",
"ats": "greenhouse",
"boardUrl": "https://job-boards.greenhouse.io/stripe",
"department": "Acquisitions & Other",
"team": "Global Operations",
"function": "Engineering",
"seniority": "mid",
"employmentType": null,
"workplace": null,
"location": "Seattle, WA",
"locations": ["Seattle, WA"],
"country": "US",
"postedAt": "2026-10-09T16:47:42.000Z",
"daysSincePosted": 4,
"updatedAt": "2026-10-09T16:47:59.000Z",
"url": "https://stripe.com/jobs/search?gh_jid=8267736",
"applyUrl": null,
"salaryMin": 190400,
"salaryMax": 285600,
"salaryCurrency": "USD",
"salaryPeriod": "year",
"salaryText": "$190,400.00 - $285,600.00/yr",
"description": null,
"input": "stripe.com",
"scrapedAt": "2026-10-14T16:16:32.137Z"
}
FieldMeaning
id<ats>:<board>:<job id> (jobs) or <ats>:<board> (companies); stable between runs.
functionOne of Engineering, Data & AI, Product, Design, Sales, Marketing, Customer Success & Support, Finance, People & HR, Legal & Compliance, Operations, Security, IT, Healthcare, Other: from the title, else the department. A heuristic.
seniorityintern, entry, mid, senior, lead, manager, director, executive: from the title (and the board's own experience level where it has one). A heuristic; "mid" when the title says nothing. leadershipJobs counts manager, director and executive.
employmentTypefull-time, part-time, contract, temporary, internship, apprenticeship, volunteer, or null when the board does not say.
workplaceremote, hybrid, onsite, or null: the board's own value, else "remote"/"hybrid" in the location or title.
countryISO code from the board or read from the location text (city, state, country); null (unknown in counts) when unsure.
countCompletefalse when a board could not be read to the end (SmartRecruiters and Workday are paged; the counts then cover what was read and no job is reported closed).

Fields a board does not provide are null, never missing.

Pricing

Pay per event. You pay for what is delivered, not for time or memory.

EventFreeBronzeSilverGold and above
Actor start (once per run, per GB of memory; the default 512 MB counts as one)$0.0036$0.003$0.0027$0.0024
Company record delivered (companies mode)$0.0048$0.004$0.0036$0.0032
Job record delivered (jobs mode)$0.0006$0.0005$0.00045$0.0004

Worked examples (Bronze):

  • A weekly check of 250 accounts, of which 200 resolve: 200 × $0.004 + $0.003 = $0.803 per run, $3.21 for four weekly runs. The 50 unresolved accounts cost nothing.

  • All open jobs of 10 companies, 1,500 jobs in total: 1,500 × $0.0005 + $0.003 = $0.753.

  • A daily "new jobs only" schedule over the same 10 companies that finds 12 new jobs: 12 × $0.0005 + $0.003 = $0.009; a day with nothing new costs $0.003.

  • Companies whose board is not found, and jobs that are filtered out, are not charged. The same board reached twice in one input (for example stripe.com and greenhouse:stripe) is delivered and charged once.

  • The maximum total charge you set for a run is respected: the actor stops before it would go over, keeps what was delivered, and says so in the status message (run JrWcGWiXyDIu0uygr: a $0.012 limit delivered 2 companies).

Reliability and failure behaviour

What happenedRun statusStatus message
Records deliveredSucceededScraped 3 companies from 3 queries
Some companies not resolvedSucceededScraped 72 companies from 101 queries — 29 of 101 queries failed ("revolut.com": no job board found for revolut.com: its website could not be read (HTTP 403) …)
Jobs mode, filters match nothingSucceeded0 results: the job board has no jobs for this query — Linear: 30 open jobs, none matched the filters
No company could be resolvedFailedNo companies delivered: no job board found for …. No results were charged (only the start fee applies).
Input the actor cannot useFailed, within secondsNothing to do: the input has no "companies". …
Maximum charge reachedSucceeded… — stopped at the maximum charge set for this run
Nothing new (onlyNew)Succeeded0 new jobs in 2 monitored queries (no results charged; only the start fee applies)
  • Requests are retried up to 3 times with increasing pauses on network errors, HTTP 429 and 5xx. Lever is read at most once per second, as its robots.txt asks.
  • Records are delivered one company at a time. A run that is aborted, times out or is resurrected keeps what was delivered and never delivers or charges the same record twice.
  • Counts were checked against the boards' own totals on 2026-10-14: Stripe 724 (Greenhouse), Databricks 894 (Greenhouse), Bosch about 5,030 (SmartRecruiters; within 1 of the board's own total, which moves while the 50 pages are read), Hugging Face 6 (Workable), bunq 21 (Recruitee), Zendesk 142 (Workday; 140 later that day). Velocity, department, workplace and employment-type counts were recomputed from the raw APIs for Stripe, Palantir and Linear and matched.
  • In every test run of 2026-10-14 the charged events equalled the records delivered (for example 11,157 jobs = 11,157 job events in run L31awqxudPsWisX5C).

Tracking changes between runs

Companies mode remembers each company's open jobs after every run (key-value store company-hiring-tracker-monitor in your own account, one entry per job board and tracking key). The next run compares and fills newJobsSinceLastRun, closedJobsSinceLastRun, openJobsChange, functionChanges, newJobs and closedJobs. The comparison is by job id, so a job that is re-posted under a new id counts as closed and new. History is kept per job board, so stripe.com and greenhouse:stripe share it. A job is reported closed only when both runs read the whole board. Use a different Tracking key (monitorKey) for each independent schedule; runs with the same key compare against each other.

Jobs mode with onlyNew delivers and charges only jobs that earlier onlyNew runs did not deliver (run fnHcxTptR16WocsIS delivered 344 jobs, the repeat zOJMrd4xx5K3S0BZS delivered 0 and charged only the start). This history is kept per company as you wrote it and per tracking key: keep the same text for a company between runs, because stripe.com and greenhouse:stripe are two histories and switching between them delivers every open job again. Each history remembers up to 10,000 job ids. Delete the store or change the tracking key to start over.

To be notified, add an integration to the scheduled task (email, Slack, webhook) on "run succeeded".

Limits

  • Job boards not supported: iCIMS, Taleo, SuccessFactors, Eightfold, Phenom, BambooHR, Jobvite, custom career sites. Companies on them are not resolved (and not charged). 21 of 101 well-known tech domains in run TysjMPtBua6LNzPIZ were in this group.
  • Workday is used only when the input or the company's website names the career site. Workday shows posting dates as "Posted N Days Ago" up to "30+ Days Ago": jobs older than 30 days have no date, so oldestJobPostedAt, medianJobAgeDays and the 90-day count describe only the dated jobs (jobsWithPostingDate tells how many; Zendesk: 96 of 142 dated). Workday jobs have no department or employment type, no location when they are offered in several, and no description. Workday publishes no company name: when the input is a career-site URL, companyName is the site's tenant ("Zendesk" for zendesk.wd1.myworkdayjobs.com) and companyDomain is empty.
  • SmartRecruiters is used only when named (an unknown company id is refused, not reported as "not hiring"). No descriptions. Very large boards change while they are paged, so the count can differ by one or two from the board's own total (Bosch: 5,031 read vs 5,032 listed on 2026-10-14).
  • Company websites that refuse requests from cloud servers (HTTP 403/429) cannot be scanned; the name guess still runs. A proxy can help, or paste the board URL.
  • Function, seniority, workplace and country are derived from free text. They are heuristics: "Account Executive" is Sales/mid, "Staff Engineer" is lead, a title that says nothing about level is "mid". A bare "Commercial" in a title counts as Sales only when no other function matches ("Software Engineer - Commercial" is Engineering).
  • Salary appears only where the board publishes it: Ashby, Lever and Recruitee fields, Greenhouse pay-range blocks and "Salary: $X - $Y" lines (jobs mode only; on 2026-10-14 Databricks had 492 of 894 jobs with pay, Stripe 24 of 724).
  • EU-hosted Greenhouse boards (job-boards.eu.greenhouse.io) are not supported: no public EU board API was found on 2026-10-14.
  • Memory: the default 512 MB was enough for 11,157 jobs with descriptions from 6 boards including two of 4,000-5,000 jobs (peak 414 MB, run L31awqxudPsWisX5C). For larger lists with descriptions, set 1024 MB.
  • Speed: 101 companies in companies mode took about 6 minutes (most of it reading company websites); companies given as board URLs take seconds each.

FAQ

Do I need a proxy? No. The job board APIs are public. A proxy only helps with company websites that refuse cloud requests.

Why is a company missing? The run summary (OUTPUT_SUMMARY, failedInputs) gives the reason for each: website unreadable, no supported board linked, a linked board that is not the company's, or no board under its name. Paste the board URL to track it anyway.

Why does a company show fewer jobs than its careers page? Some companies list jobs from several boards or agencies on one page; the actor reads one board per company. countComplete: false also means a paged board could not be read to the end.

Can I run it from code? Yes: the API, the Apify clients for JavaScript and Python, Make, Zapier, n8n. The Output tab shows the URLs.

What happens when a board changes? A board that answers with errors fails for that company with the reason; nothing is charged for it.

This actor reads job postings that companies publish for anyone to see, through the public APIs their job board vendors provide for exactly that purpose. Company records contain counts and job titles, no personal data. Job records never include recruiter or hiring-manager names or contact details. Optional descriptions are employer-written text: e-mail addresses, phone numbers, personal profile links and staff names introduced by phrases such as "report to", "interview with" or "your recruiter" are removed; this is a heuristic, and a description can still name a person in another way, so treat descriptions as you would any text that may contain personal data.

You are responsible for how you use the data: respect the job boards' terms, copyright and database rights, and data-protection law (GDPR and others). Do not use the output to contact individuals without a lawful basis. If you are unsure, ask a lawyer before you build on it.

  • Europages Company Scraper: European B2B companies by industry keyword and country, each with its website domain, which you can paste straight into this actor's Companies list to see which of them are hiring.
  • Welcome to the Jungle Scraper: job ads and per-company hiring signals (open jobs by department, tech tools, headcount) for companies that recruit through Welcome to the Jungle, common in France, rather than through their own job board.
  • SEEK Jobs Scraper: job ads from SEEK, the main job site in Australia and New Zealand, by keyword, location and classification, for employers that post there instead of on a job board this actor reads.

Changelog

  • 0.1 (2026-10-14): first version. Companies and jobs modes; Greenhouse, Lever, Ashby, Workable, SmartRecruiters, Recruitee, Personio, Teamtailor and Workday; careers-page and name-guess resolution with verification; changes since the previous run; onlyNew for jobs.