ATS Jobs Scraper: Greenhouse, Lever, Ashby, Workday +24
Pricing
from $0.80 / 1,000 job results
ATS Jobs Scraper: Greenhouse, Lever, Ashby, Workday +24
Job postings from 28 ATS (Greenhouse, Lever, Ashby, Workday, SmartRecruiters, Workable...) in one normalised schema. Fetch listed companies live or search a daily snapshot. New/removed job tracking. $1 per 1,000 jobs.
Pricing
from $0.80 / 1,000 job results
Rating
0.0
(0)
Developer
Open Data Tools
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
13 hours ago
Last modified
Categories
Share
ATS Jobs Scraper: Greenhouse, Lever, Ashby, Workday and 24 more
This tool collects open job postings straight from company careers pages and gives them to you as one tidy list. Many companies publish jobs through an applicant tracking system (ATS), the hiring software behind their careers page, such as Greenhouse, Lever, Ashby or Workday. It reads 28 of these and puts every job into the same format, so you can pull all jobs from companies you choose, or search a built-in list of companies by job title, country, posting date and more.
Who it's for
- Recruiters and sourcers who want to see every open role at a set of target companies in one place.
- Job seekers who want a daily list of new roles at the employers they care about.
- Sales and research teams that track which companies are hiring, for which roles and in which countries.
- Job boards and aggregators that need a regular feed of postings in one consistent format.
Why use it
- 28 hiring platforms, one format: jobs from Greenhouse, Lever, Workday, SmartRecruiters, Personio and 23 others come back with the same fields.
- Paste a careers page: give it a careers-page URL or a company's own careers page and it detects which platform the company uses.
- Two ways to work: fetch the companies you list live, with full descriptions, or search across the built-in company index without listing anyone.
- Useful filters: narrow by title keywords, country, location, remote, hybrid or on-site, posting date, department, seniority, employment type, salary and visa sponsorship.
- Only what is new: change tracking can return only jobs that are new since your last run, and flag jobs that have been taken down.
- You pay only for what you keep: filters are applied before results are saved, so jobs you filter out are not charged.
- Public data only: it uses public job-board pages, respects robots.txt and needs no logins.
How to use it
- Open the Actor in Apify Console and choose a Mode. Pick Companies to fetch specific employers, or Search to search across the built-in company index.
- In Companies mode, paste careers-page links into Companies, for example
https://boards.greenhouse.io/stripeorhttps://jobs.lever.co/palantir. In Search mode, enter Title keywords such asdata engineerand Countries such asGB. - Optionally narrow the results with Posted within (days), Workplace type, Seniority, Only jobs with salary info or Exclude recruitment agencies, and set Max results.
- Click Start. When the run finishes, open the Output tab to see the jobs, and export them as JSON, CSV or Excel.
- To get a regular feed, tick Only new jobs since last run (and Also output removed jobs if you want to know about closed roles), then create a schedule under Schedules that runs daily with the same input.
- To send new jobs somewhere, add an integration such as Google Sheets, Slack, Zapier or Make, or a webhook that fires when the run finishes.
Pricing
You pay for each job saved to your results. The price per 1,000 jobs is $1.00 on the Apify Free plan, $0.90 on Bronze and $0.80 on Silver and above. Searching the daily snapshot costs nothing extra. Live search (which fetches every indexed company now) adds $0.005 per 1,000 companies scanned. Each run also has a tiny Apify start fee of $0.00005.
Example: a search that returns 2,000 jobs costs about $2.00 on the Free plan, $1.80 on Bronze or $1.60 on Silver. Jobs removed by your filters are not charged.
API and reference
This section is for developers and AI agents. It lists every input and output field, a sample record and code examples.
Summary
Fetches job postings from the public job-board endpoints of 28 applicant tracking systems (ATS) and returns them in one normalised schema. Two modes: companies fetches the boards you list live, with full descriptions; search queries a daily snapshot of the built-in company index (or fetches the index live) and filters by title, country, date and more. Optional change tracking returns only jobs that are new since the last run and emits status: "removed" rows for jobs that disappeared. Up to 100,000 jobs per run.
Supported ATS: Greenhouse, Lever, Ashby, Workday, SmartRecruiters, Workable, Recruitee, Oracle Cloud (Candidate Experience), BambooHR, Rippling, Teamtailor, Personio, Breezy HR, Gem, Paylocity, JazzHR, Manatal, Jobvite, Phenom, Homerun, HireHive, Hireology, JobScore, Flatchr, Talentadore, Jobsoid, PyjamaHR, Gupy.
Input
| Field | Type | Default | Description |
|---|---|---|---|
mode | companies, search | companies | refreshIndex also exists; it is used for index maintenance. |
companies | string[] | [] | Careers-page URL on a supported ATS, a company's own careers page (ATS auto-detected), ats:slug (e.g. greenhouse:stripe, workday:nvidia|wd5|NVIDIAExternalCareerSite) or a bare slug. |
searchSource | snapshot, live | snapshot | Search mode. snapshot reads the daily snapshot; live fetches every indexed company now. descriptionKeywords and visaSponsorshipOnly always use live. |
titleKeywords / excludeTitleKeywords | string[] | [] | Whole-word title match. All-caps terms such as QA, SRE, HR match case-sensitively. |
descriptionKeywords | string[] | [] | Match in title or description. |
countries | string[] | [] | ISO codes or names (GB, US, Germany), matched against all of a job's locations. |
locations | string[] | [] | Substring match on location, city, country or country code. |
includeRemoteInLocation | boolean | true | Remote jobs pass the location filter. |
workplaceTypes | string[] | all | remote, hybrid, onsite, unknown. |
postedWithinDays | integer | none | Only jobs first published within N days. |
departments | string[] | [] | Substring match on department or team. |
seniorityLevels | string[] | all | intern, junior, mid, senior, lead, director, executive (derived from title). |
employmentTypes | string[] | all | FULL_TIME, PART_TIME, CONTRACTOR, TEMPORARY, INTERN. |
salaryOnly | boolean | false | Only postings with pay information. |
visaSponsorshipOnly | boolean | false | Only postings whose description says sponsorship is available (rule-based). |
companyNameKeywords | string[] | [] | Search mode: filter companies by name or slug. |
atsPlatforms | string[] | all | Search mode: limit to these ATS. |
excludeAgencies | boolean | false | Drop staffing agencies and aggregators (name-based rules). |
includeDescription | boolean | true | Include plain-text description. |
descriptionMaxChars | integer | none | Truncate descriptions. |
onlyNewSinceLastRun | boolean | false | Return only jobs not seen in the previous run with the same stateKey. |
emitRemoved | boolean | false | Add status: "removed" rows for jobs gone since the previous run. |
stateKey | string | derived | Name of the change-tracking state. Defaults to a hash of mode, company list, title keywords and countries. |
maxItems | integer | 5000 | Max jobs returned, up to 100,000. |
maxCompanies | integer | all | Limit indexed companies scanned (search mode). |
maxJobsPerCompany | integer | 10000 | Cap for large paginated boards. |
maxDetailRequests | integer | about maxItems | Workday, BambooHR and Rippling need one extra request per job for description/date. |
maxConcurrency | integer | 16 (48 in search) | Companies fetched in parallel. |
failIfEmpty | boolean | false | Fail the run if nothing is returned. |
Filters are applied before records are written, so filtered-out jobs are not charged.
Output
| Field | Type | Description |
|---|---|---|
key | string | Stable ID: ats:companySlug:jobId |
status | string or null | new, active or removed when change tracking is on, otherwise null |
ats, companySlug, company, companyDomain, legalEntity | string or null | Source ATS and employer |
jobId, title, department, team | string or null | Posting identifiers and labels |
location, city, region, country, countryCode | string or null | Primary location, normalised |
allLocations, allCountryCodes | string[] | All listed locations / ISO country codes |
workplaceType, isRemote | string, boolean | remote, hybrid, onsite or unknown |
employmentType, employmentTypeNormalized | string or null | Raw and normalised employment type |
seniority, seniorityLevel | string or null | Raw (if provided) and title-derived level |
experienceYearsMin | integer or null | Parsed from the description |
salaryMin, salaryMax, salaryCurrency, salaryPeriod, salaryText, salarySource | mixed | Pay, from structured fields or parsed from text |
visaSponsorship | boolean or null | Parsed from the description |
postedAt, postedAtApprox, updatedAt | string, boolean | ISO 8601 dates; postedAtApprox is true when the date is estimated |
jobUrl, applyUrl | string | Posting and application URLs |
description | string or null | Plain text. null for snapshot search results. |
scrapedAt | string | ISO 8601 UTC |
removed rows contain only key, ats, companySlug, jobId, status and scrapedAt. A SUMMARY record in the run's key-value store has per-company status and totals.
Sample record (from a test run; description shortened):
{"ats": "workday","companySlug": "autodesk|wd1|Ext","company": "autodesk","jobId": "26WD101331","title": "Business Operations Management Program Manager","department": null,"team": null,"location": "EMEA - Spain - Barcelona - Tower B2","city": "Barcelona","region": null,"country": "Spain","countryCode": "ES","allLocations": ["EMEA - Spain - Barcelona - Tower B2","AMER - Canada - Quebec - Montreal - 10 Rue Duke"],"workplaceType": "unknown","isRemote": false,"employmentType": "Full time","seniority": null,"salaryMin": null,"salaryMax": null,"salaryCurrency": null,"salaryPeriod": null,"salaryText": null,"postedAt": "2026-10-20T00:00:00+00:00","updatedAt": null,"jobUrl": "https://autodesk.wd1.myworkdayjobs.com/Ext/job/EMEA---Spain---Barcelona---Tower-B2/Business-Operations-Management-Program-Manager_26WD101331-1","applyUrl": "https://autodesk.wd1.myworkdayjobs.com/Ext/job/EMEA---Spain---Barcelona---Tower-B2/Business-Operations-Management-Program-Manager_26WD101331-1/apply","description": "Job Requisition ID # \n\n26WD101331 \n\n L'affichage de poste en français suivra / The French ...","postedAtApprox": false,"key": "workday:autodesk|wd1|ext:26WD101331","allCountryCodes": ["CA","ES"],"seniorityLevel": "lead","employmentTypeNormalized": "FULL_TIME","experienceYearsMin": null,"visaSponsorship": null,"salarySource": null,"companyDomain": null,"legalEntity": "Autodesk S.A. (Spain)","status": null,"scrapedAt": "2026-10-20T13:23:15.498123+00:00"}
Pricing details
Pay per event, charged per record written to the dataset (apify-default-dataset-item): $0.001 on the Free plan, $0.0009 on Bronze, $0.0008 on Silver and above ($1.00 / $0.90 / $0.80 per 1,000). Live search mode only: $0.005 per 1,000 companies scanned (companies-scanned-1k); snapshot search has no extra charge. Plus the Apify Actor start event ($0.00005).
Usage
Apify API (synchronous run, returns dataset items):
curl -X POST "https://api.apify.com/v2/acts/opendata-tools~career-site-jobs-feed/run-sync-get-dataset-items" \-H "Authorization: Bearer $APIFY_TOKEN" \-H "Content-Type: application/json" \-d '{"companies": ["https://boards.greenhouse.io/stripe","https://jobs.lever.co/palantir","workday:nvidia|wd5|NVIDIAExternalCareerSite"],"titleKeywords": ["engineer"],"countries": ["GB", "US"],"maxItems": 500}'
Python (pip install apify-client):
import osfrom apify_client import ApifyClientclient = ApifyClient(os.environ["APIFY_TOKEN"])run = client.actor("opendata-tools/career-site-jobs-feed").call(run_input={"mode": "search","titleKeywords": ["data engineer", "analytics engineer"],"countries": ["GB"],"postedWithinDays": 14,"maxItems": 1000,})for job in client.dataset(run["defaultDatasetId"]).iterate_items():print(job["company"], job["title"], job["location"], job["jobUrl"])
JavaScript (npm install apify-client):
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: process.env.APIFY_TOKEN });// Daily schedule: new postings plus removed ones since the last runconst run = await client.actor('opendata-tools/career-site-jobs-feed').call({companies: ['greenhouse:airbnb', 'ashby:notion'],onlyNewSinceLastRun: true,emitRemoved: true,});const { items } = await client.dataset(run.defaultDatasetId).listItems();for (const job of items) console.log(job.status, job.key, job.title ?? '');
Limits
- Snapshot search rows have no
description. Use companies mode orsearchSource: "live"for descriptions. - Workday and BambooHR are refreshed in the snapshot on a 7-day rotation, with the newest 20 jobs per Workday employer. Use companies mode for complete live lists. Paylocity, Oracle and Phenom are available in companies mode and live search only.
- Not supported: iCIMS, Taleo, SuccessFactors, Pinpoint, Freshteam, HiBob, Comeet, and job boards such as LinkedIn and Indeed.
- Derived fields (seniority, visa, experience, parsed salary) are rule-based and can be wrong.
- Workday list dates are day-granular unless descriptions are fetched.
Data and attribution
Only public, unauthenticated job-board endpoints are used, and robots.txt is respected. Company lists come from MIT / CC BY open lists and the Common Crawl URL index. Some ATS endpoint details were adapted from ever-jobs (MIT). See NOTICE and ATTRIBUTION.md.
FAQ
Which job sites does it cover?
The 28 applicant tracking systems listed above, from Greenhouse and Lever to Gupy. It does not cover iCIMS, Taleo, SuccessFactors, Pinpoint, Freshteam, HiBob or Comeet, or job boards such as LinkedIn and Indeed.
Why do some results have no job description?
Results from snapshot search have no description. Use Companies mode, or set searchSource to live, to get descriptions.
How accurate are seniority, salary and visa details?
Seniority, visa sponsorship, years of experience and salaries parsed from text are worked out with rules, so they can be wrong. Check the original posting through jobUrl when it matters.
Am I charged for jobs my filters remove?
No. Filters are applied before records are written, so only the jobs in your results are charged.
Issues and feature requests: use the Issues tab.