Request a tool
All toolsMCP serverRequest a toolPlatformsCategories
Multi-ATS Jobs Scraper (Greenhouse, Lever, Ashby) icon

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

Scrape open jobs from Greenhouse, Lever, and Ashby boards by company. Get title, location, department, and apply link as JSON, CSV, or Excel. No key needed.

Run this in the cloudRun on Apify →

Job Market Scrapers

How it works

  1. 1
    Open it on Apify

    Hit Run on Apify — it opens the tool in the cloud, no install.

  2. 2
    Set the inputs

    Adjust companies, ats, company (sensible defaults are pre-filled).

  3. 3
    Click Run

    The tool runs on Apify’s cloud and collects the data for you.

  4. 4
    Export the results

    Download as JSON, CSV or Excel, or pipe straight into your app, Google Sheets, or an AI agent.

Inputs

FieldWhat it doesType
companiesList of job boards to scrape, each as "ats:token". The ats is greenhouse, lever or ashby; the token is the company's board slug. Examples: "greenhouse:stripe", "greenhouse:airbnb", "lever:spotify", "ashby:ramp", "ashby:openai". Find the slug in a careers-page URL (boards.greenhouse.io/<token>, jobs.lever.co/<company>, jobs.ashbyhq.com/<name>). See the README for more examples and where to find each token.array
atsAlternative to the list above: pick one ATS and pair it with a single Company below. Ignored when Companies is filled.string
companyThe single company's board slug to scrape with the ATS selected above (e.g. "stripe"). Ignored when Companies is filled.string
maxItemsCap the number of postings emitted per company. Leave at 0 (or empty) to return every open posting on each board.integer
notionConnectorOptional. Write each job as a page into your Notion when the run finishes. Authorize a Notion connector once in Settings → API & Integrations → MCP connectors, then pick it here. Leave empty to skip (default) — results are always saved to the dataset regardless.string
notionParentIdOptional. The Notion data source ID of the database to write into (only used if a Notion connector is set). Leave empty to create the pages privately in your workspace instead.string

What you get

A structured dataset — each result includes fields like:

atscompanydepartmentdescriptionemploymentTypelocationpostedAtsalarytitleurl

Export every run as JSON, CSV or Excel, or send it to your app, a database, Google Sheets, or an AI agent.

3 ready-to-run use cases

Stripe Jobs Scraper - Greenhouse Open Roles & Apply Links

Every open Stripe role from its Greenhouse board, with job title, location, department and apply link. A clean jobs feed for recruiters and sourcers.

Fintech Hiring Tracker - Plaid, Brex & Mercury Job Data

Who Plaid, Brex and Mercury are hiring right now, pulled from their ATS boards. Headcount and team growth signals for competitor and market analysts.

Startup Jobs Feed Builder - Multi-Company ATS Aggregator

Live openings from fast-growing startups merged into one structured jobs feed for a job board or newsletter. Add or drop companies as you go.

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

Scrape open job listings straight from the public JSON APIs of the three most common applicant-tracking systems — Greenhouse, Lever and Ashby. No API key, no login, no anti-bot to fight. Point it at a list of company boards and get every posting back in one normalized schema.

Thousands of companies post their jobs through exactly these three boards. Instead of writing a scraper per company, give this actor a list like ["greenhouse:stripe","lever:spotify","ashby:ramp"] and it figures out the right endpoint for each and merges the results.

Input

FieldNotes
companiesArray of "ats:token" strings. ats is greenhouse, lever, or ashby; token is the company's board slug. e.g. greenhouse:stripe, lever:spotify, ashby:openai.
ats + companyConvenience pair for a single board (e.g. ats: "greenhouse", company: "stripe"). Ignored when companies is set.
maxItemsMax postings per company. 0 (default) = all open postings.
proxyConfigurationOptional, off by default. These public APIs have no anti-bot, so no proxy is needed. Only enable one if you hit IP-based rate limits across many boards.

Where do I find the token?

It's the slug in the public careers URL:

  • Greenhouse → boards.greenhouse.io/<token> (also <token>.greenhouse.io)
  • Lever → jobs.lever.co/<company>
  • Ashby → jobs.ashbyhq.com/<name>

Output

One dataset row per job posting, deduped by url:

{
  "ok": true,
  "ats": "greenhouse",
  "company": "stripe",
  "title": "Account Executive, AI Sales",
  "location": "San Francisco, CA",
  "department": "1650 AI GTM Strategy & Solutions",
  "employmentType": "FullTime",
  "url": "https://stripe.com/jobs/search?gh_jid=7964697",
  "postedAt": "2026-06-05T15:44:04-04:00",
  "description": "Who we are… (plain text, HTML stripped)",
  "salary": "$211.4K – $290.6K • Offers Equity"
}

Field availability differs by ATS, so several fields can be null even on a successful row:

FieldGreenhouseLeverAshby
employmentTypenull (not exposed)✓ (commitment)
salarynull (not exposed)best-effort (often null)✓ (structured, may be null)
department✓ (dept / team)✓ (dept / team)
location / postedAt / url✓ when present, else null✓ when present, else null✓ when present, else null

title and company are always present on a success row. Every other field above is null when the board doesn't supply it — this is honest data, not a failed scrape.

Greenhouse job descriptions come HTML-entity-encoded; the actor decodes and strips them to clean plain text.

Diagnostic rows (ok:false)

When a board has no open postings, uses a wrong/retired slug, or hits a network/blocked error, the actor pushes a single ok:false diagnostic row instead of job rows, for example:

{ "ok": false, "errorCode": "NO_RESULTS", "error": "The request succeeded but returned no results for this input.", "ats": "greenhouse", "company": "acme", "note": "No open postings for greenhouse:acme." }

An ok:false row means that one board had nothing to return — not that the run failed. These rows are never charged. Filter on ok:true to get only billable job postings. Empty input produces an ok:false row with errorCode: "BAD_INPUT" instead of crashing the run.

Billing & empty boards

Billed per job posting returned (job), and only for genuine ok:true rows. A board with no open postings (or a wrong/retired slug) produces a single ok:false diagnostic row explaining why — and is not charged. Network/blocked errors and empty/invalid input likewise return a coded ok:false diagnostic row instead of failing the whole run, and are never charged.

Example

{
  "companies": ["greenhouse:stripe", "greenhouse:airbnb", "lever:spotify", "ashby:ramp", "ashby:openai"],
  "maxItems": 0
}