Greenhouse Jobs Scraper · Lever, Ashby, SmartRecruiters ATS avatar

Greenhouse Jobs Scraper · Lever, Ashby, SmartRecruiters ATS

Pricing

from $2.10 / 1,000 job records

Go to Apify Store
Greenhouse Jobs Scraper · Lever, Ashby, SmartRecruiters ATS

Greenhouse Jobs Scraper · Lever, Ashby, SmartRecruiters ATS

ATS jobs scraper for recruiting research and lead generation: Job listings straight from the Greenhouse, Lever, Ashby, Workable, Recruitee, Personio and SmartRecruiters APIs of the companies you name, with salary and experience level parsed out. Missing boards are never charged.

Pricing

from $2.10 / 1,000 job records

Rating

0.0

(0)

Developer

TheQuietStack

TheQuietStack

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

11 days ago

Last modified

Categories

Share

Greenhouse Jobs Scraper · Lever, Ashby, SmartRecruiters + Experience Level

Greenhouse jobs scraper, Lever jobs scraper and Ashby, Workable, SmartRecruiters job scraper in one: ATS job listings straight from the company job boards, one schema, salary parsed into numbers, and an experience level on every job so you can pull only internships, entry-level and junior roles (or only senior ones).

Get every open job of the companies you care about straight from their applicant tracking system: Greenhouse, Lever, Ashby, Workable, Recruitee, Personio and SmartRecruiters. One schema for all seven, with the salary range parsed into numbers (salaryMin, salaryMax, salaryCurrency, salaryPeriod), the experience level and required years of experience (experienceLevel, yearsExperienceMin), filters for keywords, location, remote, department, experience and posting date, and a hard result cap that really stops the run.

No login, no API key, no browser. The Actor reads the official public job-board APIs the companies themselves publish for their career pages:

ATSEndpoint used
Greenhouseboards-api.greenhouse.io/v1/boards/<slug>/jobs?content=true&pay_transparency=true
Leverapi.lever.co/v0/postings/<slug>?mode=json
Ashbyapi.ashbyhq.com/posting-api/job-board/<slug>?includeCompensation=true
Workableapply.workable.com/api/v1/widget/accounts/<slug>?details=true
Recruitee<slug>.recruitee.com/api/offers/
Personio<slug>.jobs.personio.de/xml?language=en (and ?language=de when a posting exists only in German)
SmartRecruitersapi.smartrecruiters.com/v1/companies/<CompanyId>/postings (paged) + one call per posting for its text

What you get

  • Experience level on every job, with its reason. experienceLevel is INTERNSHIP, ENTRY_LEVEL, MID_LEVEL, SENIOR or DIRECTOR_EXECUTIVE. An explicit word in the title decides first (Intern, Working Student, Werkstudent, Junior, New Grad, Graduate, Trainee, Apprentice, Senior, Sr., Staff, Principal, Lead, Director, Head of, VP; Engineer II = mid, III = senior); otherwise the ATS's own level field (SmartRecruiters, Workable, Personio, Recruitee). experienceLevelSource (title / ats) and experienceLevelRaw show what decided it. No marker and no field → null, never a guess. False friends are handled: "Warehouse Staff", "Staff Accountant", "Art Director", "Lead Generation" and "University Recruiter, Interns" do not get a level.
  • Required years of experience as a number. yearsExperienceMin / yearsExperienceMax from phrases like "3+ years of experience", "2-4 years experience", "at least five years of hands-on experience", "mindestens 3 Jahre Berufserfahrung" or "no prior experience required" (= 0), with the sentence in yearsExperienceText. Several requirements → the highest minimum. Company boasts ("we have 25 years of experience") are ignored. Filter with maxYearsExperience.
  • Salary as numbers, with a receipt. First from the ATS's own pay fields (Greenhouse pay transparency ranges, Lever salaryRange, Ashby compensation tiers, Recruitee salary, Personio salaryInformation; Workable's public feed has no pay field, so there it is always the text). If the company only wrote the range into the posting text, it is parsed from there, and salaryText shows the exact sentence it came from. salarySource says which (structured or text). Equity, bonuses, welcome bonuses, relocation money, meal vouchers, learning budgets, "per session" rates and "$100B market" style numbers are ignored. Austrian-style minimums ("Mindestgehalt € 3.200,- brutto monatlich", "Minimum EUR 48,000 gross annually") fill salaryMin only, because the posting names no upper bound. Nothing is estimated: no range in the posting → the salary fields are null.
  • Pay zones kept. When a company lists several ranges (pay zones, tiers), salaryMin/salaryMax span them and every range is in salaryRanges with its label.
  • maxResults is a hard cap. Set 50 and you get 50 rows, even with several boards running in parallel. Each board is sorted newest first, so a small cap keeps the freshest jobs.
  • You pay for jobs, not for attempts. A board that does not exist (HTTP 404; on Personio a redirect away from the board), a board that exists but has 0 open jobs, a failed request, a filtered-out job or a job you already received (onlyNew) is listed in the run summary and never charged. Missing and empty boards are reported separately, so a typo in a slug never looks like "no openings".
  • Only new jobs, on a schedule. Turn on onlyNew and schedule the Actor daily: it remembers the job IDs it returned (per board, in a named key-value store in your own account) and next time returns only postings it has not shown you before.
  • Paste whatever you have. greenhouse:stripe, personio:prewave, smartrecruiters:ServiceNow, a board URL (https://jobs.lever.co/zoox, https://jobs.ashbyhq.com/ramp, https://apply.workable.com/skroutz, https://channable.recruitee.com, https://prewave.jobs.personio.de, https://jobs.smartrecruiters.com/Canva, EU hosts), a company careers page (a linked board of any of the seven is detected in its HTML), or just a slug (tried on Greenhouse, Lever, Ashby, Workable, Recruitee, Personio, then SmartRecruiters).
  • Strong in Europe. Recruitee (Netherlands) and Personio (Germany, Austria) are where European companies post; both carry a structured pay field, and Austrian postings state a legal minimum salary that is parsed from the text.
  • Polite by design. At most one request per second per ATS service (all Recruitee and all Personio company subdomains count as one service each). One request returns a company's whole board (Personio: at most two, English and German). SmartRecruiters is the exception: its list has no job text, so each posting needs one more call; these run at up to 4 per second and only for jobs that already passed your filters and fit maxResults.

Input example

{
"boards": [
"greenhouse:airbnb",
"https://jobs.lever.co/zoox",
"ashby:ramp",
"https://www.duolingo.com/careers",
"personio:prewave",
"recruitee:channable"
],
"keywords": ["engineer", "data"],
"locations": ["New York", "Remote"],
"remote": "any",
"newSinceDays": 14,
"onlyWithSalary": true,
"maxResults": 200
}

Entry-level and internships only, posted in the last 30 days, at most 1 year of experience asked:

{
"boards": ["greenhouse:stripe", "lever:palantir", "ashby:openai", "smartrecruiters:BoschGroup"],
"experienceLevels": ["INTERNSHIP", "ENTRY_LEVEL", "UNSPECIFIED"],
"maxYearsExperience": 1,
"newSinceDays": 30,
"maxResults": 300
}

UNSPECIFIED keeps jobs whose title and ATS state no level; maxYearsExperience then drops those that ask for more than one year in the text. Leave UNSPECIFIED out for a strict list (only jobs explicitly marked intern/entry/junior/graduate).

Watch list, only new postings, run daily:

{
"boards": ["ashby:openai", "greenhouse:anthropic", "lever:palantir"],
"departments": ["Engineering"],
"onlyNew": true,
"seenStoreName": "my-ai-labs-watchlist",
"includeDescription": false
}

Via the API you can pass objects and set the company name yourself:

{ "boards": [{ "ats": "lever", "slug": "palantir", "company": "Palantir" }] }

Output example (one row, shortened)

{
"ats": "ashby",
"companySlug": "ramp",
"jobId": "465799ed-c58b-4c6a-a290-2e95b52ef0bb",
"title": "Tech Lead and Manager (TLM), Production Engineering",
"company": "Ramp",
"url": "https://jobs.ashbyhq.com/ramp/465799ed-c58b-4c6a-a290-2e95b52ef0bb",
"location": "New York, NY (HQ)",
"locations": ["New York, NY (HQ)"],
"country": "USA",
"remote": true,
"workplaceType": "remote",
"department": "Engineering",
"team": "Engineering",
"employmentType": "FULL_TIME",
"postedAt": "2026-09-28T23:10:15.801Z",
"updatedAt": null,
"salaryMin": 240000,
"salaryMax": 330000,
"salaryCurrency": "USD",
"salaryPeriod": "YEAR",
"salarySource": "structured",
"salaryRanges": null,
"salaryText": null,
"descriptionText": "ABOUT RAMP\n\nRamp is building ...",
"descriptionHtml": null,
"applyUrl": "https://jobs.ashbyhq.com/ramp/465799ed-c58b-4c6a-a290-2e95b52ef0bb/application",
"requisitionId": null,
"experienceLevel": "SENIOR",
"experienceLevelSource": "title",
"experienceLevelRaw": "Lead",
"yearsExperienceMin": 2,
"yearsExperienceMax": null,
"yearsExperienceSource": "text",
"yearsExperienceText": "- 2+ years of software engineering experience shipping high-quality architectures for critical systems",
"hasSalary": true,
"scrapedAt": "2026-09-30T10:49:28.604Z"
}
FieldMeaning
jobId + atsStable ID of the posting in its ATS. Used for de-duplication within a run and across runs (onlyNew).
salaryMin / salaryMaxNumbers in the posting's own currency and period. No conversion. A single stated amount fills salaryMax (and salaryMin unless it says "up to").
salaryPeriodYEAR, MONTH, WEEK, DAY or HOUR.
salarySourcestructured = the ATS pay field; text = parsed from the posting, see salaryText.
salaryRangesAll ranges when the posting lists several (pay zones, tiers).
remote / workplaceTypeFrom the ATS's workplace field; otherwise remote when a location says remote/anywhere.
employmentTypeFULL_TIME, PART_TIME, CONTRACT, INTERN. Greenhouse has no standard field for it, so it is only set when the company added it as a custom field. Personio working students are PART_TIME.
postedAtGreenhouse first_published, Lever createdAt, Ashby publishedAt, Workable published_on (date only), Recruitee published_at, Personio createdAt, SmartRecruiters releasedDate.
experienceLevelINTERNSHIP, ENTRY_LEVEL, MID_LEVEL, SENIOR, DIRECTOR_EXECUTIVE or null. experienceLevelSource = title or ats, experienceLevelRaw = the word or ATS label that decided it. ATS labels: SmartRecruiters/Workable "Associate" and "Mid-Senior level" = MID_LEVEL; Personio "experienced" = MID_LEVEL; Recruitee "experienced" = SENIOR.
yearsExperienceMin / MaxYears the posting asks for. yearsExperienceSource = text (sentence in yearsExperienceText) or ats (Personio's years field). null = not stated.
locationsEvery location of the posting. Workable's feed repeats a job once per location; those copies are merged into one row with all locations.

The run summary (key-value store record SUMMARY) lists per board: jobs on the board, jobs that matched your filters, jobs written; plus boards not found, empty boards, failed requests, careers pages without a detectable board, counts per filter, and how many events were charged.

How much salary data is there?

Measured on 30 Sep 2026 across 40 well-known company boards (20 Greenhouse, 10 Lever, 10 Ashby): about 61 % of Greenhouse jobs, 62 % of Lever jobs and 69 % of Ashby jobs came with a salary range, and about a fifth of those ranges existed only in the posting text, not in the ATS pay field.

The same day, 10 company boards with open jobs per European-heavy ATS:

ATSJobsWith salaryFrom pay field / textBoards with any salary
Workable18719 (10 %)0 / 195 of 10
Recruitee23817 (7 %)17 / 04 of 10
Personio37012 (3 %)2 / 105 of 10

On 4 Oct 2026, three SmartRecruiters boards (ServiceNow, Canva, Ubisoft; 1,169 jobs): 320 with salary (27 %), all from the text; ServiceNow 44 %, Ubisoft 0 %.

How many jobs get an experience level?

Measured on 4 Oct 2026 on 21 boards (3 per ATS, 3,760 jobs):

ATSJobsWith levelWith yearsLevel or years
Greenhouse37547 %43 %65 %
Lever63545 %40 %62 %
Ashby1,11324 %48 %60 %
Workable10548 %11 %50 %
Recruitee3291 %9 %91 %
Personio331100 %87 %100 %
SmartRecruiters1,16984 %44 %92 %

A hand check of 45 random title levels and 40 random years sentences found no misreads. Titles without a level word (e.g. "Account Executive", "Forward Deployed Engineer") stay null; that is the honest answer, not a gap to fill with guesses.

How much you get depends on the company and the country, not on the ATS: US postings almost always carry a range (pay transparency laws), many European ones do not. Austrian postings carry a legal minimum (one Austrian Personio board: 8 of 8 jobs), while one German board with 319 jobs had a range on only 1. Use onlyWithSalary to keep just the jobs that state pay.

Compared with other ways to get these jobs

  • Job aggregators and pre-built job databases cover far more companies and ATS platforms (including Workday and others). This Actor covers only Greenhouse, Lever, Ashby, Workable, Recruitee, Personio and SmartRecruiters, and only the companies you name, but reads them live at run time, so a posting closed this morning is not in your data.
  • Single-ATS scrapers need one run and one output format per ATS. Here all seven share one schema, including one experience-level vocabulary.
  • Generic scrapers often stop at the raw salary text. Here you get numbers you can sort and filter on, plus the sentence they came from.

If you need Workday or iCIMS, or discovery of companies you do not know yet, a broader tool is a better fit.

Pricing

Pay per event: $3.00 per 1,000 jobs (job-record, $0.003) plus $0.001 per company that returned at least one job (company-scanned). Example: 500 jobs from 20 companies = $1.50 + $0.02 = $1.52. Boards that are missing, empty or fail, and jobs you filtered out or already saw, cost nothing. Your "Max cost per run" is respected: the Actor stops before pushing a row it cannot charge.

Limits and honesty

  • Only boards the company publishes publicly. Nothing behind a login, no application forms, no applicant data.
  • Careers-page detection reads the page HTML. Boards that are loaded only by JavaScript are not found; use the board URL or ats:slug instead. The summary tells you when this happens.
  • Lever answers some company slugs with an empty list (HTTP 200, 0 jobs) instead of 404. Those are reported as empty, not as missing. Workable and Personio answer many existing accounts with 0 jobs (HTTP 200); also reported as empty, never as missing.
  • Personio's feed carries a posting's text only in the language it was written in. The Actor reads the English feed and, only if a posting came back without text, the German one.
  • SmartRecruiters' list API answers any company id, real or invented, with "0 jobs". When a list comes back empty, the Actor checks careers.smartrecruiters.com/<id> once: an unknown id is redirected to SmartRecruiters' home page and reported as not found; a real company is reported as an empty board. SmartRecruiters ids are case-sensitive (ServiceNow). With includeDescription off and no text-based filter, no per-job calls are made, so salary* and yearsExperience* stay null for SmartRecruiters (the level still comes from its field).
  • Experience levels come from words in the title and from ATS fields, in English plus common German and French words. Years come from English and German sentences. Other languages get a level only from the ATS field.
  • Recruitee boards on a company's own domain (without .recruitee.com) are not detected from the careers page; pass recruitee:<slug>.
  • Some company websites refuse non-browser clients (HTTP 403/404/429). Careers-page detection then fails with that status in the summary; the board itself is unaffected, pass its URL or ats:slug.
  • Salary text parsing covers the common English and German phrasings ($120,000 - $150,000, $120-150k per year, CAD $79,600 - CAD $99,500, 70.000 € - 90.000 € brutto, $37.63/hr—$77.48/hr, € 3.200,- brutto monatlich, Jahresbruttogehalt von mindestens EUR 45.000). Unusual wordings stay null rather than guessed.