Greenhouse, Lever, Ashby, Workable, SmartRecruiters Job Scraper avatar

Greenhouse, Lever, Ashby, Workable, SmartRecruiters Job Scraper

Pricing

from $1.00 / 1,000 delivered jobs

Go to Apify Store
Greenhouse, Lever, Ashby, Workable, SmartRecruiters Job Scraper

Greenhouse, Lever, Ashby, Workable, SmartRecruiters Job Scraper

ATS jobs scraper: paste a Greenhouse, Lever, Ashby, Workable or SmartRecruiters job-board URL (boards.greenhouse.io/stripe) or a board slug and get up to the whole board as clean rows, with new/changed/removed tracking. From $1 per 1,000 jobs; descriptions and change rows $0.0005 each. No run fee.

Pricing

from $1.00 / 1,000 delivered jobs

Rating

0.0

(0)

Developer

David Brooks

David Brooks

Maintained by Community

Actor stats

0

Bookmarked

1

Total users

0

Monthly active users

11 hours ago

Last modified

Share

ATS jobs scraper for Greenhouse jobs, Lever jobs, Ashby, Workable and SmartRecruiters — paste a job-board URL or slug, get up to the whole board as rows. From $1 per 1,000 jobs, no run fee.

Greenhouse, Lever, Ashby, Workable and SmartRecruiters are the applicant tracking systems behind a large share of company careers pages, and this actor reads all five from the same input. Paste the job-board URL the careers page links to — boards.greenhouse.io/stripe, jobs.lever.co/spotify, jobs.ashbyhq.com/openai, apply.workable.com/rockstar-3, careers.smartrecruiters.com/BoschGroup — or the short form greenhouse:stripe, and up to the whole board (maxItems caps it, 50 by default) comes back in one flat schema. Every row carries the title, the company (the board's display name, or its slug where the board publishes none), the posting URL and an apply URL; location, city, region and ISO country code, remote or hybrid or onsite, work type, department, salary and the posted date are filled wherever the board publishes them, and Known limits says which board leaves out what. Switch on change tracking and each run also tells you which postings are new, which changed and which have come down since the last run.

What you get

  • Five applicant tracking systems behind one input, so a run can mix boards from all five — and up to 200 boards in a single run
  • One schema for all five: the same keys, the same sourceId format and a schemaVersion that never changes under you, whichever ATS a row came from
  • trackChanges gives every row a changeStatus of new, changed or unchanged against the previous run of that board, and postings that have come down arrive as rows with changeStatus: removed — a job-feed monitor with no diffing of your own
  • Location parsed into city, region and an ISO-3166 country code, even where the board publishes a country name ('United States') or a lower-case code ('mx') instead
  • Salary as numbers — salaryMin, salaryMax, salaryCurrency, salaryPeriod — on the boards that publish compensation, plus the board's own summary text in salaryText
  • Full descriptions are optional, so a list-only monitoring run stays cheap and pays nothing for payloads it does not want
  • A job-board URL or ats:slug with no board behind it is named in the run summary, and a run in which every board is missing fails with a typed error instead of succeeding with zero rows

Sample row:

{
"sourceId": "ashby:openai:8fb1615c-34bf-47c4-a1d1-b7b2f836bbd3",
"sourceUrl": "https://jobs.ashbyhq.com/openai/8fb1615c-34bf-47c4-a1d1-b7b2f836bbd3",
"source": "ashby",
"schemaVersion": 1,
"title": "Technical Program Manager, Compute Infrastructure",
"company": "openai",
"companyId": "openai",
"location": "San Francisco",
"city": "San Francisco",
"region": "California",
"country": "US",
"remote": "unknown",
"workType": "full-time",
"category": "Technical Program Management",
"subcategory": "Technical Program Management",
"salaryText": "$257K – $335K • Offers Equity",
"salaryMin": 257000,
"salaryMax": 335000,
"salaryCurrency": "USD",
"salaryPeriod": "year",
"postedAt": "2026-03-12T16:38:15.322Z",
"expiresAt": null,
"applyUrl": "https://jobs.ashbyhq.com/openai/8fb1615c-34bf-47c4-a1d1-b7b2f836bbd3/application",
"changeStatus": null
}

Input

FieldDescription
boardsOne entry per company: a job-board URL (https://boards.greenhouse.io/stripe, https://jobs.lever.co/spotify, https://jobs.ashbyhq.com/openai, https://apply.workable.com/rockstar-3, https://careers.smartrecruiters.com/BoschGroup) or the short form ats:slug. Up to 200 per run; anything else is rejected before the run starts, with the accepted forms listed
includeDescriptionFetch the full description (HTML and plain text). Charged per row that carries one
sinceISO date. Postings first published before it are skipped and never charged. A date filter only
trackChangesCompare each board against its previous run: rows carry changeStatus, and postings that have come down are delivered as removed
maxItemsHard cap on delivered jobs (default 50), enforced before the request that would exceed it
maxCostUsdHard stop on spend, enforced before each request
proxyConfigurationDatacenter Apify Proxy by default; switch it off for small runs
outputFormatflat (default) or nested

Pricing

Pay per event. You are charged only for delivered, validated results — never for empty pages, blocked requests, or duplicates. maxCostUsd is a hard stop enforced before each request.

EventPriceUnit
job$0.001per delivered job — $1.00 per 1,000
job-description$0.0005per row with a full description — only with includeDescription
company-diff$0.0005per new, changed or removed posting — only with trackChanges, and never on a board's first run
  • 1,000 jobs ≈ $1.00
  • 1,000 jobs with full descriptions ≈ $1.50
  • A daily monitor over 40 boards returning 200 postings with 20 changes ≈ $0.21 per run, about $6.30 a month
  • There is no per-run fee: a run pays only for the rows it delivers, so a run over boards that return no rows costs nothing at all

Why this actor

  • One actor covers Greenhouse, Lever, Ashby, Workable and SmartRecruiters: paste each board URL as the careers page links to it and the actor reads the system from the host, so a run can mix all five
  • No per-run start fee. Scheduled monitoring pays for the rows it delivers and nothing else, which matters when you run the same 40 boards every morning
  • A job-board URL or ats:slug with no board behind it is named in the run summary under meta.missingBoards, and a run in which every board is missing fails with a typed BoardNotFoundError, rather than finishing successfully with zero rows and letting a broken monitor look healthy for weeks
  • maxItems and maxCostUsd are enforced before the request that would exceed them, and the run summary says which cap stopped it
  • Change detection is stored per board and is only updated after a complete pass over that board's feed, so a run stopped by a cap never makes the next run report postings it simply never reached as 'new' or 'removed'. A board whose posting count happens to equal maxItems exactly still counts as a complete pass, and a removal a cap left no room for is kept and reported on the next run rather than dropped
  • Descriptions are priced separately and off by default, so a list-only feed watcher is not charged for payloads ten times the size it needs
  • No AI 'enrichment' and no inferred fields priced as data: every value is the board's own, and anything the board does not publish is null rather than guessed (the two fallbacks are company, which is the board slug where the board publishes no display name, and applyUrl, which is the posting page where it publishes no separate apply link)

Known limits

  • Five systems only — Greenhouse, Lever, Ashby, Workable and SmartRecruiters. Workday, iCIMS, Recruitee, Personio, Teamtailor and company career sites on their own software are not supported, and the actor does not guess a company's ATS from its name or domain: you give it the job-board URL (e.g. boards.greenhouse.io/stripe) or ats:slug
  • Greenhouse publishes no salary on its public job board API: none of the boards checked carried a pay range, so salaryMin, salaryMax and salaryText are null on Greenhouse rows. category, subcategory and country come from fields Greenhouse sends only with the full description, so they fill in with includeDescription — and for that reason category is left out of the Greenhouse change-detection hash, so turning descriptions on or off never reports a board as changed. The same holds on SmartRecruiters, whose own apply URL only comes with the full posting (without it, applyUrl is the posting page) and is excluded from the hash for that reason
  • Greenhouse also has no workplace field, so remote on a Greenhouse row is read from the location label ('Remote - Japan') and is unknown when the label does not say
  • Lever and Ashby publish no employer display name, so company on those rows is the board slug. Workable and SmartRecruiters do publish one, and Greenhouse sends it with every posting
  • Workable's API carries no work type and no salary, so workType, salaryMin and salaryMax are null on Workable rows
  • Ashby returns an entire board in a single response — 830 postings measured 14.5 MB, with the compensation fields this actor always requests — so maxItems limits what you are charged for, not how much is downloaded. The same is true of Greenhouse, which has no pagination either
  • Because of that, the actor asks the platform for at least 1 GB of memory and refuses a single response larger than 64 MB. Both numbers come from the same measurement — parsing a 14.5 MB board peaked at 168 MB resident, and the cost scales at roughly 6.9 MB of memory per MB of response on top of a 74 MB baseline, so a response at the 64 MB ceiling needs about 500 MB before a single row is mapped. Run it with less and the largest boards are killed by the container rather than reported. Raise the memory rather than the ceiling if you ever see a board refused for size
  • Workable and SmartRecruiters descriptions need one extra request per job, so includeDescription runs on those two boards are slower (Greenhouse, Lever and Ashby send the description with the listing)
  • SmartRecruiters' Posting API answers identically for a company id that does not exist and a company with no live postings, so the actor checks where the public careers page ends up to tell them apart. A company id it cannot confirm is reported as an error rather than as an empty board, because a silently empty monitor is worse than a loud one. Requests to that host are kept to one per second
  • Salary appears only where the board publishes it. Most do not: expect it on Ashby boards that display compensation, and rarely elsewhere
  • Change detection needs trackChanges and a complete pass over the board: a run in which maxItems or maxCostUsd cut a board's feed short deliberately leaves that board's stored baseline alone and reports no removals for it, so give a monitored board a maxItems above its posting count. Removal rows count against maxItems like any other row, and removals the cap had no room for stay in the baseline and are reported on the next run — meta.deferredRemovals says how many are still waiting. since is a posted-date filter and nothing else, the same as in every other actor in this account. If the platform restarts a run part-way through, the rows it had already delivered are counted as delivered: they are not billed again and they stay in the baseline
  • A board that is missing or empty is reported, not silently dropped: each one is logged as a warning while the run continues, and the run's RUN_SUMMARY record in the key-value store lists them under meta.missingBoards and meta.emptyBoards, alongside meta.requested, meta.delivered, meta.skipped, meta.diffMode, the per-board breakdown in meta.boards, and — in trackChanges runs — meta.newPostings, meta.changedPostings, meta.unchangedPostings, meta.removedPostings, meta.deferredRemovals and meta.removalOnlyBoards. A board that went quiet is listed under meta.removalOnlyBoards rather than meta.emptyBoards: its feed is empty, but every posting it used to carry was delivered as a removal row and charged, which is the opposite of nothing happening. Read that record to find out which of a long boards list actually produced rows. The run fails with a BoardNotFoundError only when every board is missing
  • Employer job postings only. No candidate, applicant or recruiter personal data is collected from any of the five systems

Use from an AI agent (MCP)

Add the actor as an MCP tool in Claude Desktop / Cursor via the Apify MCP server:

{ "mcpServers": { "ats-jobs": { "url": "https://mcp.apify.com?actors=<your-username>/ats-jobs", "headers": { "Authorization": "Bearer <APIFY_TOKEN>" } } } }

The tool accepts exactly the input fields above and returns the same rows, so an agent can ask for a company's open roles by passing the job-board URL (e.g. boards.greenhouse.io/stripe) or ats:slug it already found.

Use from n8n / Make

Use the Apify node → "Run actor" with actor <your-username>/ats-jobs on a daily schedule and a JSON input such as {"boards":["greenhouse:stripe","lever:spotify","ashby:openai"],"trackChanges":true,"maxItems":500}, then "Get dataset items" and filter on changeStatus to act only on what moved since yesterday.

FAQ

How do I find a company's board slug?

You do not need it. Open the company's careers page, and if it links to (or the address bar shows) one of the five ATS hosts, paste that job-board URL, e.g. boards.greenhouse.io/stripe. The actor takes the slug out of it, including from a single job's URL. A careers page on the company's own domain is not accepted.

What happens if a company moves to a different ATS?

The old board starts answering "not found" and that board is reported as missing in the run summary, with nothing charged for it. Add the new job-board URL (e.g. boards.greenhouse.io/stripe) or ats:slug and the rows continue under a new source value.

How does change detection know what changed?

After a complete pass over a board it stores a hash of the fields a feed watcher cares about — title, location, work type, remote arrangement, salary, department, posted date and apply URL — per posting. Descriptions are deliberately excluded, because boards re-render their HTML without the job changing. So is department on Greenhouse, which publishes it only alongside the full description — hashing it would have reported a whole board as changed the first time you turned includeDescription on. The apply URL on SmartRecruiters is left out for the same reason.

Why is salary null on most rows?

Because most boards do not publish it. The actor reports what the board sends and leaves the rest null rather than inventing a figure or inferring one from the job title.

Do I need a proxy?

Not for small runs. All five APIs answer datacenter and plain IPs today. The default is datacenter Apify Proxy with session rotation because each host rate-limits per address, which matters once you monitor dozens of boards on a schedule.

This actor reads five documented, public, keyless job board APIs: the Greenhouse Job Board API, the Lever Postings API, the Ashby Posting API, Workable's public careers widget API and the SmartRecruiters Posting API. It never logs in, never uses accounts or cookies from accounts, and never solves a challenge. It collects employer job advertisements only, and no personal data about candidates, applicants or recruiters. Request rates are conservative — one request per second per host, which honours Lever's published crawl delay and SmartRecruiters' documented API guidance — and 429 responses fail the run with a typed error instead of being retried in a loop. SmartRecruiters' robots.txt disallows crawling the API host even though the Posting API is published and documented for exactly this use; the rate limit above is our answer to that, and it is stated here rather than buried. Data is delivered to you for your own use; no corpus is built or resold.

Support

Open an issue on the Issues tab; first response within 6 hours on business days. Every run writes a RUN_SUMMARY record to the key-value store with items delivered, events charged, and the stop reason.