Greenhouse, Lever & Ashby Jobs Scraper + Salary avatar

Greenhouse, Lever & Ashby Jobs Scraper + Salary

Pricing

$2.00 / 1,000 jobs

Go to Apify Store
Greenhouse, Lever & Ashby Jobs Scraper + Salary

Greenhouse, Lever & Ashby Jobs Scraper + Salary

Scrape open jobs from Greenhouse, Lever, Ashby, Workable, Recruitee and Teamtailor with salary normalised to min, max, currency and period. $2 per 1,000 jobs. Paste board URLs, domains or company names: the system is detected for you, and unsupported companies are free.

Pricing

$2.00 / 1,000 jobs

Rating

0.0

(0)

Developer

Meridian Labs

Meridian Labs

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

7 hours ago

Last modified

Share

Greenhouse, Lever & Ashby Jobs reads the open jobs of any company on Greenhouse, Lever, Ashby, Workable, Recruitee or Teamtailor, straight from the company's public job board, and gives every job a normalised salary: min, max, currency and period (hour, day, week, month or year), from the hiring system's own pay field or the ad text, with every pay zone or level. Paste job-board URLs, careers page URLs, plain domains (company.com) or company names: the hiring system is detected for you, and every company gets a free status row saying what was found. The input form is pre-filled, so the easiest way to try it is to click Start (about $0.09).

Greenhouse, Lever & Ashby Jobs result: salaries as published on Greenhouse, Lever, Ashby, Recruitee, Teamtailor and Workable, normalised to min, max, currency and period

It's built for recruiters and sourcers, sales and market-intelligence teams tracking who is hiring, salary and labour-market researchers, job-board builders and AI agents that need clean, structured job data from company career pages.

What does the Greenhouse, Lever & Ashby jobs scraper do?

For each company you give it, it finds the company's job board and returns one row per open job:

  • 💰 Normalised salary (salary): {min, max, currency, period, source, raw_text, basis, tiers, eu_pay_range}, e.g. $59,500 — $85,000 USD becomes 59500, 85000, USD, year.
  • 🏷️ Job basics: title, department, employment_type (full_time, part_time, contract, internship…), posted_at, updated_at, url, apply_url.
  • 📍 Location: the location as published plus city, region and an ISO country code, and workplace (remote, hybrid or onsite) with a strict remote flag (fully remote only).
  • 📝 Description as plain text or HTML (or none), with personal email addresses and phone numbers removed.
  • 🔁 Monitor mode: only jobs that are new or changed since your last run, plus free notices for jobs that closed.
  • 🆔 Stable IDs (greenhouse:tubitv:8099700) that stay the same across runs, and the source_url every row came from.

Which hiring systems are supported?

SystemFound fromSalary source
Greenhousejob-boards.greenhouse.io/<company>, embedded boards, careers pagesGreenhouse's pay-transparency block, else the ad text
Lever (US and EU)jobs.lever.co/<company>Lever's salary range field, else the ad text
Ashbyjobs.ashbyhq.com/<company>Ashby's compensation tiers, else the ad text
Workableapply.workable.com/<company>, embedded widgetsthe ad text
Recruitee<company>.recruitee.com or the company's own careers domainRecruitee's salary field, else the ad text
Teamtailor<company>.teamtailor.com or the company's own careers domainthe ad text

Not supported, and never charged: Workday, iCIMS, SmartRecruiters, Personio, SAP SuccessFactors, Oracle Taleo, Eightfold, BambooHR and others. Their public endpoints are blocked by robots.txt or bot checks, or their terms don't allow this use. When a company uses one of them you get a free status row with status: "not_supported" naming the system, for example "The board URL is on Workday. Workday is not supported…", so you know exactly why there are no jobs and can move on.

How is the salary normalised?

Pay is read from the hiring system's own pay field first (source: "ats_field"), and from the ad text otherwise (source: "description_text"), in English, Dutch, German, French, Spanish, Polish and the Nordic languages, with European number formats (€ 3.035 - € 4.052 bruto per maand) and hourly, monthly and annual periods.

  • Every pay range, not just the first. Postings that list pay zones or levels ("Zone 1 … Zone 4", medior/senior) get all of them in tiers, each with its label; the headline min/max is the first one.
  • Base pay first. When a posting shows both base pay and OTE / total compensation, the base range is the headline and basis says "base". If only OTE is given you get it with basis: "ote".
  • Typos flagged, not hidden. If an employer types 73.8 in an annual pay field (meaning 73,800), you get the number as entered with suspect: true and the reason.
  • EU pay transparency. eu_pay_range is true for jobs in the EU/EEA that publish pay (the EU Pay Transparency Directive 2023/970), false outside it and null when the country is unknown.
  • Tested: on 50 fresh jobs showing pay (September 2026), 49 were read exactly right: values, currency, period and every tier.

Why use this job scraper?

  • 💰 Salary you can sort and filter, not a string: comparable min/max/period across systems, countries and currencies.
  • 🔎 Paste anything. A board URL resolves with a single request. For a domain it scans the careers pages and checks the six systems' public APIs; in our test on 50 fresh companies it found the right board for 47 from their domain alone. A board is only reported after its API answered, so it doesn't return another company's jobs.
  • 🧾 Clean billing. You pay per job saved. Company status rows, unsupported and not-found companies, duplicates and closed-job notices are free. The same board given twice is fetched and charged once, and a restarted run never charges a job twice.
  • 🔁 Monitoring that doesn't bill you for nothing. In monitor mode only new or changed jobs are saved and charged.
  • 🛡️ Personal-data clean. Individual people's email addresses and phone numbers are removed from descriptions, and Recruitee's per-job inbox address is never output. Role mailboxes such as careers@ stay, because that's how candidates apply.
  • 🤝 Polite and simple. Public job-board endpoints only: no login, no API keys, no proxies. robots.txt is obeyed, including Lever's crawl delay, with one request at a time per hiring system.
  • 🤖 AI-agent ready. A documented output schema, table views and stable field names. It works from the Apify API, integrations (Make, Zapier, n8n, Google Sheets) and MCP clients.

How do I scrape jobs from Greenhouse, Lever or Ashby?

  1. Paste your companies, one per line: job-board URLs work best, but careers page URLs, domains and names also work.
  2. Optionally add filters: title keywords, locations, fully remote only, posted within N days, only jobs showing a salary.
  3. Choose the mode: all open jobs, or monitor for new and changed jobs only.
  4. Click Start. Open the Jobs view, or export JSON, CSV or Excel. The Companies view shows what happened with each company.

Greenhouse, Lever & Ashby Jobs input form: a list of job-board URLs, careers pages, domains or company names

How much does it cost to scrape jobs?

$2 per 1,000 jobs ($0.002 per job saved). That's the only charge.

  • ✅ Charged: each job row saved to your dataset, after your filters and maxJobsPerCompany.
  • 🆓 Free: company status rows; companies on unsupported systems (not_supported); companies with no board found (not_found); boards that failed to load; duplicates; closed-job notices in monitor mode; anything skipped by your spending limit or the run's time limit.
  • 🔁 Monitor mode: only new or changed jobs are charged. Unchanged jobs cost nothing. The first run of a monitor sees every job as new.
  • Examples: a company with 40 open jobs = $0.08; 100 companies with 30 jobs each = $6; a daily monitor on the same 100 companies that finds 50 new or changed jobs = $0.10 that day.
  • Free to try: Apify's free plan includes $5 of platform usage every month, which covers about 2,500 jobs here. Platform usage is included in the price.
  • Set a maximum cost per run and the Actor stops exactly there. The status rows show how many jobs were not saved because of it, and in monitor mode those jobs are still "new" next time.

Input

FieldWhat it does
companiesJob-board URLs, careers page URLs, domains or company names, one per line. Duplicates are removed.
systemsOnly use these hiring systems (default: all six).
titleKeywords / excludeTitleKeywordsKeep / drop jobs whose title contains any of these words (whole words, case-insensitive).
locationsKeep jobs whose location, city or region contains one of these, or whose country matches a 2-letter code (US, DE).
remoteOnlyFully remote jobs only (hybrid isn't remote).
postedWithinDaysJobs first published in the last N days.
onlyWithSalaryOnly jobs that show a salary.
modeall (every open job) or newSinceLastRun (monitor: new or changed jobs only, plus free closed notices).
monitorNameSeparate memories for separate watch lists (monitor mode).
includeDescriptiontext (default), html or none (structured fields only). Same price.
maxJobsPerCompanySave at most N jobs per company. Leave empty for all.
includeCompanyRowsPut the free company status rows in the dataset (default) or only in the COMPANIES record.
maxConcurrencyCompanies processed in parallel (1–10).
failOnErrorsMark the run failed if a board can't be fetched or a board URL no longer exists (for schedules).

Example:

{
"companies": ["https://job-boards.greenhouse.io/tubitv", "https://jobs.lever.co/genbio", "flowx.ai"],
"onlyWithSalary": true,
"includeDescription": "none"
}

Output example

The Jobs view on the Output tab gives one line per job, salary first:

Jobs view in Apify Console: job title, company, salary min and max, currency, period, location, country, workplace, type and hiring system

A job row (description shortened):

{
"record_type": "job",
"id": "greenhouse:tubitv:8099700",
"company": "Tubi",
"system": "greenhouse",
"board_url": "https://job-boards.greenhouse.io/tubitv",
"title": "Associate, Content Valuation",
"department": "Content",
"location": "Los Angeles, CA (Hybrid)",
"country": "US",
"remote": false,
"workplace": "hybrid",
"posted_at": "2026-07-31T18:25:29Z",
"url": "https://job-boards.greenhouse.io/tubitv/jobs/8099700",
"salary": {
"min": 59500, "max": 85000, "currency": "USD", "period": "year",
"source": "ats_field", "raw_text": "$59,500 — $85,000 USD",
"basis": null, "suspect": false, "eu_pay_range": false, "tiers": []
},
"language": "en",
"change_status": null,
"fetched_at": "2026-09-25T09:39:10Z",
"source_url": "https://boards-api.greenhouse.io/v1/boards/tubitv/jobs?content=true",
"description_text": "About the Role:\n\nTubi is seeking a highly analytical and commercially minded Associate…"
}

A multi-zone salary:

"salary": {
"min": 151800, "max": 178400, "currency": "USD", "period": "year", "source": "ats_field", "basis": "base",
"tiers": [
{"label": "Zone 1", "min": 151800, "max": 178400, "currency": "USD", "period": "year"},
{"label": "Zone 2", "min": 136700, "max": 160600, "currency": "USD", "period": "year"},
{"label": "Zone 3", "min": 121500, "max": 142700, "currency": "USD", "period": "year"},
{"label": "Zone 4", "min": 106300, "max": 124900, "currency": "USD", "period": "year"}
]
}

A company status row (free):

{
"record_type": "company",
"input": "https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite",
"status": "not_supported",
"reason": "The board URL is on Workday. Workday is not supported: Workday has no public job-board API we can use under its terms. Supported: Greenhouse, Lever, Ashby, Workable, Recruitee and Teamtailor.",
"jobs_saved": 0,
"jobs_charged": 0,
"charged": false
}

Job rows and company rows share one dataset; filter on record_type = "job" for jobs only, or switch off includeCompanyRows.

Personal data

Job ads sometimes include a recruiter's own email address or phone number. This Actor:

  • replaces personal-looking email addresses with [email removed] and phone numbers with [phone removed], in both text and HTML descriptions and in salary text;
  • keeps role mailboxes (careers@, recruiting@, jobs@, accommodations@, and their Dutch, German, Polish, French and Nordic equivalents), because they're how candidates apply;
  • never outputs Recruitee's per-job mailbox_email field;
  • does not remove people's names written in the ad text (e.g. "contact Maria"). Use includeDescription: none for structured fields only.

None of the six endpoints exposes recruiter or hiring-manager contact fields, and this Actor doesn't try to find any.

How do I get a daily feed of new jobs?

  1. Paste the companies, add your filters, set Mode to Monitor: only new or changed jobs since the last run (newSinceLastRun) and click Save as a new task.
  2. Open Schedules → Create new, pick the task and choose daily.
  3. The first run returns every open job; after that, each run returns only jobs that are new or changed, plus free closed notices. Unchanged jobs cost nothing.

Only want the last 24 hours? Set Posted within the last N days to 1. A daily monitor on 100 companies that finds 50 new or changed jobs a day costs about $0.10 a day.

How do I send jobs to Google Sheets, Make, Zapier, n8n or an AI agent?

  • Google Sheets and Drive: on the Actor's or task's Integrations tab, add the Google Drive integration, or use Make, Zapier or n8n (below) with a Google Sheets "add rows" step.
  • Make, Zapier and n8n: each has an official Apify app or node. Trigger on "Actor run finished", then get the dataset items. Ask for the jobs view to get one line per job with the salary columns.
  • Webhooks: Integrations → HTTP webhook on "Run succeeded" sends the run details to your URL.
  • AI agents (MCP): add https://mcp.apify.com?actors=meridianlabs/greenhouse-lever-ashby-jobs-scraper to your MCP client (Claude, Cursor, VS Code and others). The agent can then pull a company's open jobs and salaries.

Python (pip install apify-client):

from apify_client import ApifyClient
client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("meridianlabs/greenhouse-lever-ashby-jobs-scraper").call(run_input={
"companies": ["https://job-boards.greenhouse.io/tubitv", "https://jobs.ashbyhq.com/revenuecat"],
"onlyWithSalary": True,
})
for row in client.dataset(run.default_dataset_id).iterate_items():
if row["record_type"] == "job":
s = row["salary"]
print(row["company"], row["title"], s["min"], s["max"], s["currency"], s["period"])

For a daily feed, run it as a scheduled task in monitor mode (see How do I get a daily feed of new jobs?).

Limitations

  • Six hiring systems only. Companies on Workday, iCIMS, SmartRecruiters, Personio and others get a free not_supported row, not jobs.
  • You bring the companies. This is a live fetch of the companies you list, not a keyword search across every company on the internet.
  • Detection from a name is weaker than from a URL or domain (38 of 50 in our test, against 47 of 50 from the domain). Careers pages built entirely in JavaScript may hide the board; paste the board URL for those.
  • Salary is only as good as what the employer publishes. Most jobs outside pay-transparency regions show none. Amounts are never guessed; suspicious entries are flagged, not changed.
  • Teamtailor's feed doesn't give an employment type, and Workable's doesn't give an updated date.

FAQ

This Actor reads the public job-board endpoints the hiring systems publish so that anyone can list a company's open jobs (Greenhouse, for example, documents that "Job Board data is publicly available, so authentication is not required"). It doesn't log in, use keys or get around robots.txt or bot checks, and it asks for each board once per run. The job ads belong to the employers; use the data in line with the laws that apply to you, especially if you republish ads. Not affiliated with or endorsed by Greenhouse, Lever, Ashby, Workable, Recruitee or Teamtailor.

How do I find a company's job-board URL?

Open the company's careers page and click through to a job: the address usually shows the system (jobs.lever.co/…, job-boards.greenhouse.io/…, jobs.ashbyhq.com/…). Or just paste the company's domain and let the Actor find it.

Why did a company come back "not_found"?

Nothing on its careers pages pointed to a supported board, and no board on the six systems matched its name. Common causes: the company uses an unsupported system hidden behind a JavaScript careers page, or no system at all. The reason says what was tried. Paste the board URL if you know it.

Does monitor mode charge for jobs I've already received?

No. Unchanged jobs are skipped and free. A job counts as changed when its title, location, department, employment type, workplace, pay, apply link or description changes. Closed jobs come back as free change_status: "closed" rows.

What happens if a run times out or restarts?

Companies that are finished are not fetched or charged again after a restart. When the time limit is near, the remaining companies get free skipped rows and the run still ends as succeeded.

More from Meridian Labs

  • Google Trends Scraper & API: Google Trends for hundreds of keywords at once, with a plain-English summary per keyword (rising or falling, year on year, peak, seasonality). $5 per 1,000 keyword lookups.
  • Keyword Search Volume + Google Trends: monthly search volume, CPC, competition and keyword difficulty (Google Ads figures) plus a Google Trends summary for every keyword. From $3 per 1,000 keywords.

Support

Found a board that doesn't parse right, or a salary format it misses? Open an issue on the Issues tab with the board URL. We read every one.

If this saved you time, a rating helps others find it; if something's off, open an issue and we'll fix it fast.