ATS Jobs Scraper: Greenhouse, Lever, Ashby avatar

ATS Jobs Scraper: Greenhouse, Lever, Ashby

Pricing

$1.50 / 1,000 jobs

Go to Apify Store
ATS Jobs Scraper: Greenhouse, Lever, Ashby

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

Alexandre Bobichon

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

4 days ago

Last modified

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 nameaircall.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

ATSJob board URLs it readsExample input
Greenhouseboards.greenhouse.io/{slug}, job-boards.greenhouse.iogreenhouse:stripe
Leverjobs.lever.co/{slug}https://jobs.lever.co/contentsquare
Ashbyjobs.ashbyhq.com/{slug}ashby:linear
Workableapply.workable.com/{slug}workable:blueground
Personio{slug}.jobs.personio.depersonio:urbansportsclub
Teamtailor{slug}.teamtailor.com and custom careers domainsteamtailor: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

  1. Put your companies in the Companies field, one per line. A domain (stripe.com) is enough.
  2. Leave Max jobs at its default for a first look, then raise it.
  3. Click Start and check the Output tab.
  4. 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

FormExampleWhen to use it
Company domainstripe.comThe normal case. The Actor finds the ATS.
Job board URLhttps://jobs.lever.co/contentsquareYou already have the board link.
ATS and slugworkable:bluegroundFastest 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 companyResolved
European scale-ups10/10
US tech9/10
UK and Ireland6/6
DACH6/8
Nordics4/6
Large enterprises5/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

FieldTypeDefaultWhat it does
companiesarrayOne company per line: a domain, a board URL or ats:slug.
maxCompaniesinteger50Companies processed in one run.
maxItemsinteger200Jobs saved in one run. The crawl stops when it is reached.
maxJobsPerCompanyinteger0Cap per company so one large board cannot use the whole budget. 0 means no limit.
titleKeywordsarrayKeep only jobs whose title contains one of these.
locationKeywordsarrayKeep only jobs whose location contains one of these.
remoteOnlybooleanfalseKeep only jobs the ATS flags as remote.
publishedAfterstringKeep only jobs published on or after this date.
includeDescriptionbooleanfalseAdd the job description. See the FAQ.
descriptionFormatselecttextPlain text, HTML, or both.
includeSourceSpecificbooleanfalseAdd a sourceSpecific object with each ATS's extra fields.
trackChangesbooleanfalseCompare with the previous run.
onlyChangesbooleanfalseSave only new, changed and closed jobs.
stateStoreNamestringats-job-boards-stateKey-value store holding the state. One name per list you track.
resolveDomainsbooleantrueTurn 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

FieldDescription
jobId, globalIdThe ATS job ID, and ats:company:job — stable across runs, use it to deduplicate.
title, url, applyUrlThe job, its public page and its application form.
companyName, companySlug, companyDomain, ats, boardUrlWhich company, on which ATS.
resolvedFromHow the board was found: input-slug, input-url, careers-page, api-probe or cache.
department, team, seniorityAs published. Not every ATS provides them.
employmentTypefull_time, part_time, contract, temporary, internship, volunteer or other. employmentTypeRaw keeps the original wording.
workplaceType, isRemoteremote, hybrid or onsite, plus a plain boolean.
location, city, region, country, countryCode, locationsA readable label, structured parts when the ATS gives them, and every location of a multi-site job.
salaryMin, salaryMax, salaryCurrency, salaryPeriod, salaryRawOnly when the employer publishes a range. salaryRaw keeps it exactly as written.
publishedAt, updatedAtISO 8601.
description, descriptionHtmlOff by default.
sourceSpecificFields peculiar to one ATS, when you ask for them.
changeStatus, changedFields, previousValues, previousScrapedAt, closedAtChange tracking.
scrapedAtWhen 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.
  • changedchangedFields lists what moved and previousValues holds 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, maxJobsPerCompany or your cost limit cuts a board in half, the jobs it never reached are not reported as closed, and with onlyChanges the 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 onlyChanges on a schedule. It is the cheapest way to monitor a large list.
  • Set maxJobsPerCompany when 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:slug for companies that do not resolve. Open their careers page, look at the URL of the job board, and take the slug from it.
  • Use titleKeywords and locationKeywords rather 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.