Multi-ATS Jobs Scraper (Greenhouse, Lever, Ashby) avatar

Multi-ATS Jobs Scraper (Greenhouse, Lever, Ashby)

Pricing

from $2.00 / 1,000 job results

Go to Apify Store
Multi-ATS Jobs Scraper (Greenhouse, Lever, Ashby)

Multi-ATS Jobs Scraper (Greenhouse, Lever, Ashby)

Scrape public job postings from Greenhouse, Lever and Ashby career pages into one clean dataset. Filter by keyword, remote or location, get salary ranges, and use incremental mode to receive only new jobs on each run.

Pricing

from $2.00 / 1,000 job results

Rating

0.0

(0)

Developer

José Alaín Díaz Carrero

José Alaín Díaz Carrero

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

12 days ago

Last modified

Security

Limited permissions

Share

Collect public job postings from thousands of company career pages in one normalized format. Give it company names (or career-page URLs) hosted on Greenhouse, Lever or Ashby and get clean JSON/CSV/Excel with title, location, remote flag, department, employment type, salary range (when the company publishes it), dates and apply links.

It only uses the official public, no-authentication job-board APIs that these platforms provide for career pages. No login, no browser, no proxies, no HTML scraping, so it is fast, cheap and stable.

What you can do with it

  • Build a job board or newsletter from a list of target companies.
  • Track hiring at competitors or prospects (sales / recruiting intelligence).
  • Get only new jobs since your last run (schedule it hourly/daily and send results to Slack, email, a webhook or Google Sheets with Apify integrations).
  • Compare published salary ranges across companies.
  • Filter by keyword, remote, and location.

Input

FieldTypeDescription
companiesarrayObjects { "ats": "greenhouse" | "lever" | "ashby", "token": "<company slug>" }
boardUrlsarray of stringsCareer-page URLs; ATS and token are auto-detected (boards.greenhouse.io/x, job-boards.greenhouse.io/x, jobs.lever.co/x, jobs.ashbyhq.com/x)
keywordsarrayKeep jobs whose title/department/team contains any keyword
excludeKeywordsarrayDrop jobs containing any of these
searchInDescriptionbooleanAlso match keywords inside the description (default false)
remoteOnlybooleanOnly remote jobs
locationContainsarrayKeep jobs whose location contains any string (e.g. ["Chile", "United Kingdom", "EMEA"])
onlyNewSinceLastRunbooleanReturn only jobs not seen before (see below)
stateStoreNamestringNamed key-value store holding seen job IDs (default multi-ats-jobs-state)
includeDescriptionbooleanAdd plain-text description (default false)
maxItemsPerCompanyintegerCap per company (0 = unlimited)
maxConcurrencyintegerBoards fetched in parallel (default 3, max 10)

You need at least one company or board URL. The company token is the slug in the career-page URL, e.g. https://boards.greenhouse.io/airbnb → airbnb.

Example inputs

1. Everything from a few companies (minimal)

{
"companies": [
{ "ats": "greenhouse", "token": "airbnb" },
{ "ats": "lever", "token": "spotify" },
{ "ats": "ashby", "token": "ramp" }
]
}

2. Remote engineering jobs, filtered, from career-page URLs

{
"boardUrls": [
"https://job-boards.greenhouse.io/gitlab",
"https://jobs.lever.co/spotify"
],
"keywords": ["engineer", "data"],
"excludeKeywords": ["intern"],
"remoteOnly": true,
"locationContains": ["United States", "Canada"]
}

3. Daily job alert: only new jobs, capped per company

{
"companies": [
{ "ats": "greenhouse", "token": "gitlab" },
{ "ats": "ashby", "token": "ramp" }
],
"keywords": ["product manager"],
"onlyNewSinceLastRun": true,
"stateStoreName": "my-daily-pm-alert",
"maxItemsPerCompany": 50
}

Output

One dataset item per job (see sample_output.json in the repository for full examples):

{
"id": "8731718002",
"ats": "greenhouse",
"company": "GitLab",
"companyToken": "gitlab",
"title": "Principal Security Researcher",
"location": "Remote, Canada; Remote, United States",
"isRemote": true,
"department": "Product Security",
"team": null,
"employmentType": null,
"salaryMin": 203200,
"salaryMax": 275000,
"salaryCurrency": "USD",
"salaryInterval": "year",
"postedAt": "2026-09-05T17:47:29.000Z",
"updatedAt": "2026-10-15T19:24:46.000Z",
"applyUrl": "https://job-boards.greenhouse.io/gitlab/jobs/8731718002",
"jobUrl": "https://job-boards.greenhouse.io/gitlab/jobs/8731718002",
"scrapedAt": "2026-10-25T18:22:11.427Z"
}

Field availability per ATS

FieldGreenhouseLeverAshby
company namereal nameslugslug
department / teamdepartmentdepartment / teamdepartment / team
employmentTypeonly if the company uses a custom fieldyesyes
salarywhen the company publishes pay rangeswhen publishedwhen published
postedAtfirst publishedcreatedpublished
updatedAtyesnono

null means the platform did not provide the value. isRemote is taken from the ATS flag when available, otherwise detected from the location/title text.

Incremental mode ("only new jobs")

With onlyNewSinceLastRun: true the Actor remembers the IDs of jobs it already returned, per ATS + company, in a named key-value store (stateStoreName, default multi-ats-jobs-state). The first run returns everything that matches your filters; later runs return only postings that were not returned before.

How it behaves, step by step:

  1. The board is fetched and your filters (keywords, remote, location) are applied.
  2. Jobs whose ID is already stored are skipped.
  3. If maxItemsPerCompany is set, the remaining new jobs are cut to that number.
  4. Only the jobs actually returned are added to the stored "seen" list.

What this means in practice:

  • maxItemsPerCompany and incremental mode: jobs beyond the cap are not marked as seen. They are returned in a later run, so nothing is lost; with a cap of 50 and 120 new jobs you get 50 now, 50 on the next run and 20 on the one after.
  • Filtered-out jobs are not marked as seen either, so they appear if they start matching (for example after you change your keywords).
  • IDs of jobs that disappear from a board are dropped from the stored list, so the state stays small. A job that is removed and later re-posted with the same ID is treated as new.
  • If a run stops early (for example because your maximum run cost was reached), the jobs not yet returned are not marked as seen and will come in the next run.
  • Use a different stateStoreName for each independent monitoring setup (one per Schedule or per client). To start over, change the name or delete the store in Apify Console.
  • Combine with an Apify Schedule and an integration (Slack, email, webhook, Google Sheets) to build a job alert pipeline.

Pricing (pay per event)

The Actor is designed for pay-per-event pricing: a small start event plus a charge per job returned (job-result). It stops early if your maximum run cost is reached. Check the pricing section on this page for current prices.

FAQ

Is this legal / allowed? It calls the public job-board endpoints that Greenhouse, Lever and Ashby expose precisely so companies' postings can be embedded and consumed elsewhere. No authentication is bypassed and no private data is accessed. You are responsible for how you use the data and for complying with applicable laws and the terms of the career pages.

Does maxItemsPerCompany lose jobs in incremental mode? No. Jobs beyond the cap are not marked as seen, so they are returned in later runs. See "Incremental mode" above.

How do I reset incremental mode? Change stateStoreName or delete the key-value store with that name in Apify Console (Storage, Key-value stores).

What happens if a company has no jobs or its token is wrong? A company with no matching jobs simply returns nothing. A wrong token or a company that left the ATS is logged as a warning (usually HTTP 404) and the rest of the run continues.

Can I export to CSV, Excel or Google Sheets? Yes. The dataset can be downloaded as JSON, CSV, Excel, XML or HTML, or sent to Google Sheets, Slack, email or a webhook with Apify integrations.

Do I need proxies or cookies? No. The Actor only calls public endpoints, so no proxy, login or cookies are needed.

How do I find a company's token? Open the company's careers page; if it redirects to boards.greenhouse.io/<token>, jobs.lever.co/<token> or jobs.ashbyhq.com/<token>, that is the token. You can also just paste the URL into boardUrls.

A company failed with HTTP 404. The token is wrong or the company no longer uses that ATS. Failures are logged per company and never stop the run; other companies are still processed.

Why is company a slug for Lever and Ashby? Those APIs do not return the company display name.

Does it support EU Lever (api.eu.lever.co) or custom domains? Not yet; only the standard global endpoints.

How fast is it? A typical board with a few hundred jobs takes about one second. Memory use is low (256–512 MB is enough).

Can I get descriptions? Yes, set includeDescription: true (plain text).

This Actor only reads the public job-board APIs that Greenhouse, Lever and Ashby offer for companies' public career pages. It does not log in, does not bypass any access control, does not collect private data and does not scrape candidate or recruiter profiles. Job postings are published by the companies themselves to be seen and applied to. Greenhouse, Lever and Ashby are trademarks of their respective owners; this Actor is independent and not affiliated with or endorsed by them. You are responsible for how you use the data and for complying with applicable laws (including data protection rules) and the terms of the career pages you query.

Notes

  • Only public job-board APIs are used. No proxies needed.
  • Requests are retried with exponential backoff on 429/5xx and run with low concurrency.
  • Greenhouse boards with thousands of jobs return large responses; use a few companies per run for best speed.