ATS Jobs Scraper: Greenhouse, Lever, Ashby
Pricing
$1.50 / 1,000 jobs
ATS Jobs Scraper: Greenhouse, Lever, Ashby
Scrape job postings from Greenhouse, Lever, Ashby, Workable, Personio and Teamtailor. Enter company domains, the ATS is found for you. Get titles, locations, salaries and departments, track new and closed jobs, export to CSV, JSON or Excel.
Pricing
$1.50 / 1,000 jobs
Rating
0.0
(0)
Developer
Alexandre Bobichon
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
4 days ago
Last modified
Categories
Share
Scrape job postings from Greenhouse, Lever, Ashby, Workable, Personio and Teamtailor with one Actor. Give it a list of company domains and get back every job those companies have published, in one consistent format — titles, locations, departments, salaries when published, and new, changed and closed jobs since your last run.
Try it with the prefilled input: three companies, three different ATS, results in a few seconds.
What does this ATS jobs scraper do?
Most job scrapers ask you for an ATS board slug: greenhouse:aircallioinc, lever:contentsquare. Finding
those by hand for a list of a hundred companies is an afternoon of work, because the slug is often nothing
like the company name — aircall.io is aircallioinc on Greenhouse, epidemicsound.com is
epidemic-sound on Ashby.
This Actor takes aircall.io and works the rest out for you. It reads the company's careers page, finds the
job board behind it, and falls back to probing the six supported ATS APIs. Then it returns every published
job in a single schema, whichever ATS it came from.
It reads the official public job board APIs that these ATS provide for syndication — the same endpoints that power company careers pages and job aggregators. No browser, no login, no proxy needed.
Run it on a schedule and turn on change tracking to get new, changed and closed jobs since your last run.
Supported ATS and job board URLs
| ATS | Job board URLs it reads | Example input |
|---|---|---|
| Greenhouse | boards.greenhouse.io/{slug}, job-boards.greenhouse.io | greenhouse:stripe |
| Lever | jobs.lever.co/{slug} | https://jobs.lever.co/contentsquare |
| Ashby | jobs.ashbyhq.com/{slug} | ashby:linear |
| Workable | apply.workable.com/{slug} | workable:blueground |
| Personio | {slug}.jobs.personio.de | personio:urbansportsclub |
| Teamtailor | {slug}.teamtailor.com and custom careers domains | teamtailor:payfit |
Or just the company domain — stripe.com, linear.app — and the Actor finds the board.
Why scrape jobs from company career pages?
- Hiring-intent signals for B2B sales. A company that starts hiring five sales engineers is a company with a budget. Track your target accounts and get the new roles the day they appear.
- Portfolio monitoring. Funds and accelerators watch headcount plans across their portfolio without asking anyone for a spreadsheet.
- Recruiting and talent mapping. See who is hiring for a role, in which locations, at what salary when the employer publishes one.
- Building a job board. One normalised feed from six ATS, with stable IDs to deduplicate against.
- Competitive intelligence. Roles closing can say as much as roles opening.
How to scrape Greenhouse, Lever and Ashby jobs
- Put your companies in the Companies field, one per line. A domain (
stripe.com) is enough. - Leave Max jobs at its default for a first look, then raise it.
- Click Start and check the Output tab.
- Once you are happy with the result, open Schedules, run it daily, and switch on Track changes so each run tells you what moved.
Three ways to name a company
| Form | Example | When to use it |
|---|---|---|
| Company domain | stripe.com | The normal case. The Actor finds the ATS. |
| Job board URL | https://jobs.lever.co/contentsquare | You already have the board link. |
| ATS and slug | workable:blueground | Fastest and exact. Use it when a domain does not resolve. |
How well does domain resolution work?
Measured on a deliberately mixed benchmark of 50 companies: 40 resolved, 80 %. The rate depends a lot on the kind of company:
| Kind of company | Resolved |
|---|---|
| European scale-ups | 10/10 |
| US tech | 9/10 |
| UK and Ireland | 6/6 |
| DACH | 6/8 |
| Nordics | 4/6 |
| Large enterprises | 5/10 |
The resolver reads the company's careers page (including careers. and jobs. subdomains), then tries
the known ATS APIs with the domain name and a few common variants of it: datadoghq.com is found as
greenhouse:datadog, starlingbank.com as workable:starling-bank.
Large companies that run their own careers site are the weak spot. Shopify, Atlassian, Klarna, Revolut
and Bolt could not be resolved from their domain, because nothing on their careers page points at a
supported ATS, and several use an ATS this Actor does not read. They are not a failure of the run: the summary lists them as unresolved, and you can add
them with the ats:slug form once you know the slug.
Resolutions are cached for 30 days in your state store, so a scheduled run pays this cost only once per company.
Input
| Field | Type | Default | What it does |
|---|---|---|---|
companies | array | — | One company per line: a domain, a board URL or ats:slug. |
maxCompanies | integer | 50 | Companies processed in one run. |
maxItems | integer | 200 | Jobs saved in one run. The crawl stops when it is reached. |
maxJobsPerCompany | integer | 0 | Cap per company so one large board cannot use the whole budget. 0 means no limit. |
titleKeywords | array | — | Keep only jobs whose title contains one of these. |
locationKeywords | array | — | Keep only jobs whose location contains one of these. |
remoteOnly | boolean | false | Keep only jobs the ATS flags as remote. |
publishedAfter | string | — | Keep only jobs published on or after this date. |
includeDescription | boolean | false | Add the job description. See the FAQ. |
descriptionFormat | select | text | Plain text, HTML, or both. |
includeSourceSpecific | boolean | false | Add a sourceSpecific object with each ATS's extra fields. |
trackChanges | boolean | false | Compare with the previous run. |
onlyChanges | boolean | false | Save only new, changed and closed jobs. |
stateStoreName | string | ats-job-boards-state | Key-value store holding the state. One name per list you track. |
resolveDomains | boolean | true | Turn off to accept only board URLs and ats:slug. |
Filters are applied before saving, so a job you filtered out is never charged.
{"companies": ["stripe.com", "aircall.io", "https://jobs.lever.co/contentsquare"],"maxItems": 500,"titleKeywords": ["engineer", "developer"],"remoteOnly": true,"trackChanges": true}
Output
One item per job. Download as JSON, CSV, Excel or XML, or read it from the API.
{"jobId": "186545F8C1","globalId": "workable:blueground:186545F8C1","title": "Client Experience Coordinator (English & Greek Speaker)","url": "https://apply.workable.com/j/186545F8C1","applyUrl": "https://apply.workable.com/j/186545F8C1/apply","companyName": "Blueground","companySlug": "blueground","ats": "workable","department": "Shared Services","employmentType": "full_time","seniority": "Associate","isRemote": false,"location": "Athens, Attica, Greece","city": "Athens","country": "Greece","countryCode": "GR","publishedAt": "2026-02-12T00:00:00.000Z","boardUrl": "https://apply.workable.com/blueground","resolvedFrom": "input-slug","scrapedAt": "2026-09-16T11:30:25.864Z"}
Fields
| Field | Description |
|---|---|
jobId, globalId | The ATS job ID, and ats:company:job — stable across runs, use it to deduplicate. |
title, url, applyUrl | The job, its public page and its application form. |
companyName, companySlug, companyDomain, ats, boardUrl | Which company, on which ATS. |
resolvedFrom | How the board was found: input-slug, input-url, careers-page, api-probe or cache. |
department, team, seniority | As published. Not every ATS provides them. |
employmentType | full_time, part_time, contract, temporary, internship, volunteer or other. employmentTypeRaw keeps the original wording. |
workplaceType, isRemote | remote, hybrid or onsite, plus a plain boolean. |
location, city, region, country, countryCode, locations | A readable label, structured parts when the ATS gives them, and every location of a multi-site job. |
salaryMin, salaryMax, salaryCurrency, salaryPeriod, salaryRaw | Only when the employer publishes a range. salaryRaw keeps it exactly as written. |
publishedAt, updatedAt | ISO 8601. |
description, descriptionHtml | Off by default. |
sourceSpecific | Fields peculiar to one ATS, when you ask for them. |
changeStatus, changedFields, previousValues, previousScrapedAt, closedAt | Change tracking. |
scrapedAt | When this run read the board. |
A field the ATS does not publish is null, never missing. Field names and types do not change outside a
major version.
Tracking changes
Turn on Track changes and every job gets a changeStatus:
new— not on the board last run.changed—changedFieldslists what moved andpreviousValuesholds the old values.unchanged— still there, identical.closed— it was on the board last run and is gone now. Returned as an item, rebuilt from the saved state, so you can act on it even though the ATS no longer serves it.
Add Only save new, changed and closed jobs to skip the unchanged ones, so you are not charged to be
told nothing happened.
Two things worth knowing:
- The first run saves everything, because nothing is known yet.
- A run that stops early picks up where it stopped. If
maxItems,maxJobsPerCompanyor your cost limit cuts a board in half, the jobs it never reached are not reported as closed, and withonlyChangesthe next run moves on to them instead of charging you again for the ones already saved.
Use a different stateStoreName for each list you track independently.
Use it from the API, integrations or an AI agent
Every run is available through the Apify API, so this Actor works as a job postings API for your own code. Run it and get the jobs back in a single call:
$curl -X POST "https://api.apify.com/v2/acts/succinct_glider~ats-job-boards/run-sync-get-dataset-items?token=<YOUR_TOKEN>" -H "Content-Type: application/json" -d '{"companies": ["stripe.com", "linear.app"], "maxItems": 100}'
- Schedule it in Apify and send new jobs to Google Sheets, Slack, a webhook or your CRM through the built-in integrations, Make, Zapier or n8n.
- Give it to an AI agent: through the Apify MCP server, agents can call this Actor to look up who a company is hiring.
Pricing
$0.0015 per job saved. No start fee, so a run that finds nothing costs nothing.
You only pay for jobs that are actually saved: filtered-out jobs and, with onlyChanges, unchanged jobs are
not charged. Monitoring 50 companies daily with onlyChanges typically costs a few cents a day, because
most days only a handful of jobs move.
Set a maximum cost per run in the run options and the Actor stops cleanly when it is reached.
Tips
- Use
onlyChangeson a schedule. It is the cheapest way to monitor a large list. - Set
maxJobsPerCompanywhen your list mixes small companies with one that has hundreds of openings. - Leave descriptions off unless you need them. They make runs slower and heavier, and change nothing about what you pay.
- Use
ats:slugfor companies that do not resolve. Open their careers page, look at the URL of the job board, and take the slug from it. - Use
titleKeywordsandlocationKeywordsrather than filtering afterwards: filtered jobs are free.
FAQ
Where does the data come from?
The official public job board APIs of each ATS — the endpoints they publish so that job postings can be
syndicated to careers pages and aggregators. Greenhouse documents that "Job Board data is publicly
available, so authentication is not required for any GET endpoints"; Lever states that all postings in the
published state are publicly viewable; Ashby documents a public job posting API. No account, no key, no
scraping of pages behind a login.
Is this legal? The Actor reads public job postings that employers publish for redistribution, one company at a time, at about one request per second. It collects no personal data: no recruiter names, no emails, no phone numbers. You are responsible for how you use the output, in particular the job descriptions if you enable them — they are the employer's own content, and Workable and Teamtailor signal that their data should not be used to train models.
Which ATS are supported? Greenhouse, Lever, Ashby, Workable, Personio and Teamtailor. Recruitee is planned. Workday is not supported: its job endpoint is undocumented and internal.
A company came back with no jobs. Is it broken?
Check the run summary. empty means the board was read and the company has nothing published right now.
not_found means there is no board at that slug, and unresolved means the domain could not be matched to
an ATS. These are situations, not failures, and the run succeeds.
One ATS was down. Did I lose the whole run? No. Each company is read independently. A failure is recorded against that company and the others still produce results. The run only fails if no board at all could be read, or if the data looks structurally wrong.
My company has two job boards.
It happens after migrating ATS, when the old board is left up. The Actor keeps the one with the most recent
posting, reports the company as ambiguous in the summary, and lists the other candidates. Use ats:slug
to force a specific one.
Support
Found a bug, or an ATS you would like supported? Open an issue on the Issues tab and I will look at it. Include the input you used and the company that misbehaved.