Greenhouse, Lever, Ashby & Workday Job Scraper | 7 ATS avatar

Greenhouse, Lever, Ashby & Workday Job Scraper | 7 ATS

Pricing

from $1.50 / 1,000 job postings

Go to Apify Store
Greenhouse, Lever, Ashby & Workday Job Scraper | 7 ATS

Greenhouse, Lever, Ashby & Workday Job Scraper | 7 ATS

Scrape job postings from 7 ATS - Greenhouse, Lever, Ashby, Workday, SmartRecruiters, Workable and Recruitee - into one unified schema. Every run tells you what changed since the last one: new, updated and removed listings. No login, no browser, no proxies. $1.50 per 1,000 jobs.

Pricing

from $1.50 / 1,000 job postings

Rating

0.0

(0)

Developer

Softyways

Softyways

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

2 days ago

Last modified

Share

Greenhouse, Lever, Ashby & Workday job scraper with change tracking

Scrape job postings from company career pages across 7 applicant tracking systems — Greenhouse, Lever, Ashby, Workday, SmartRecruiters, Workable and Recruitee — and get one clean dataset with every open role. Paste a careers URL, get structured jobs: title, location, department, employment type, apply link.

The part other scrapers leave to you: every run also tells you what changed since the last one — which jobs are new, which were edited, and which disappeared.

Runs on Apify, so you can schedule it daily, call it from the API, and pipe the output into a spreadsheet, a database or your own app.

What does this job scraper do?

It reads the public JSON endpoints the ATS platforms already serve to their own careers pages. No login, no browser, no proxies, no anti-bot circumvention — which is why a run of 745 jobs takes about three seconds.

For each career page you give it, you get:

  1. every currently open job posting, normalized into the same shape regardless of which ATS it came from;
  2. a changeType on every row: new, updated, unchanged or removed.

Which ATS platforms are supported?

PlatformExample URL to paste
Greenhousehttps://job-boards.greenhouse.io/stripe
Leverhttps://jobs.lever.co/spotify
Ashbyhttps://jobs.ashbyhq.com/linear
Workdayhttps://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite
SmartRecruitershttps://jobs.smartrecruiters.com/BoschGroup
Workablehttps://apply.workable.com/moneyfarm
Recruiteehttps://channable.recruitee.com

Paste the careers URL and the platform is detected automatically. You can also write it explicitly as greenhouse:stripe.

How to scrape jobs from a Greenhouse job board

  1. Open the company's careers page and copy the URL from the address bar.
  2. Paste it into the Career pages field. Add as many companies as you like.
  3. Click Start. The first run returns every open job, all marked new.
  4. Run it again tomorrow — or put it on a schedule — and the second run tells you what moved.

The same three steps work for Lever, Ashby, Workday, SmartRecruiters, Workable and Recruitee.

How do I know which jobs are new since yesterday?

Change tracking is on by default. State lives in a named key-value store (ats-jobs-tracker-<trackerName>) that survives between runs, so a daily schedule gives you a rolling feed of hiring changes.

  • new — was not in the previous run
  • updated — title, location, department or employment type changed; previousTitle and previousLocation hold the old values
  • unchanged — still there, untouched
  • removed — was in the previous run and is gone from the career page now

Set outputMode to changes to get only the first, second and fourth — useful when you are feeding alerts rather than rebuilding a table.

Use trackerName to keep separate sets of companies on separate histories.

What data do you get for each job?

FieldDescription
keyStable id across runs (ats:board:jobId)
ats, boardWhich platform and which company board it came from
companyNameCompany name as the platform reports it
titleJob title
department, teamAs categorized by the company
location, locationsPrimary location and the full list
remoteRemote flag where the platform exposes one
employmentTypeFull-time, contract, internship… where available
url, applyUrlLink to the posting and to the application form
publishedAt, updatedAtPlatform timestamps where available
descriptionFull text, only when includeDescription is on
changeTypenew, updated, unchanged, removed
firstSeenAt, lastSeenAt, removedAtWhen this Actor first and last saw the job
previousTitle, previousLocationWhat the values were before an update

You can download the dataset in JSON, CSV, Excel, HTML or XML, or read it straight from the API.

Example row:

{
"ats": "greenhouse",
"board": "figma",
"companyName": "Figma",
"jobId": "5364702004",
"title": "Account Executive, Emerging Enterprise",
"department": "Sales",
"location": "Berlin, Germany",
"locations": ["Berlin, DE"],
"employmentType": null,
"remote": null,
"url": "https://boards.greenhouse.io/figma/jobs/5364702004",
"publishedAt": "2024-11-01T06:05:10-04:00",
"changeType": "new",
"firstSeenAt": "2026-07-31T12:00:00.000Z",
"previousTitle": null
}

Input

FieldDefaultWhat it does
boardsList of career page URLs. Required.
trackChangestrueCompare with the previous run and label every job.
outputModeallall returns every job; changes returns only new, updated and removed ones.
trackerNamedefaultRuns sharing a name share history.
includeDescriptionfalseAdds the full job description. Slower, much larger output.
maxConcurrency5How many career pages to fetch at once.

How much does it cost to scrape job postings?

$1.50 per 1,000 jobs, billed per row returned. No monthly fee, no minimum.

You trackRoughly
10 small companies (~300 jobs)$0.45 per run
A daily watch on 3,000 jobs$4.50 per run
One-off pull of 10,000 jobs$15.00

Two ways to spend less: leave includeDescription off, and set outputMode to changes once your first run has established the baseline — after that you only pay for what actually moved, which on a normal day is a small fraction of the board.

Apify's free plan comes with monthly credits, so you can try it without paying anything.

Cost limit

Every run has a maximum total charge, and one dataset row is one charge. A run that finds more jobs than its limit pays for cannot return all of them.

When that happens this Actor stops writing instead of letting the extra rows disappear quietly:

  • the rows it can still pay for go to changes first — new, updated and removed listings before unchanged ones;
  • the jobs left out are not recorded as seen, so the next run reports them again instead of swallowing them forever;
  • RUN_SUMMARY carries costLimitReached, itemsWithheldByCostLimit and costLimitUsd.

To fit a large run: raise Maximum cost per run in the run options, set outputMode to changes, or split the career pages across several runs.

What happens when a career page breaks?

A board that goes quiet is never reported as "everything removed", because a false removal would have you take down listings that are still open:

  • if a career page errors (network, 404, rate limit), its jobs are carried over untouched;
  • if it answers with an empty list, removals are withheld until it comes back empty twice in a row — on a single run, a silent format change and a company that genuinely stopped hiring look identical.

Boards held back this way are listed under removalsWithheld in RUN_SUMMARY, together with counts, failures and cost-limit status.

FAQ

This Actor reads public job postings through the endpoints the ATS platforms serve to their own careers pages — the same data any visitor sees, without logging in or bypassing any protection. Job postings are company records, not personal data: no candidate profiles, no emails, no contact details are collected. You are responsible for how you use the output; if you are unsure about your use case, take legal advice.

Do I need a proxy?

No. These are public JSON endpoints, so there is nothing to rotate around. That also keeps runs fast and cheap.

Why are department and posting date empty for Workday jobs?

Workday's listing endpoint returns the title, location and requisition id, and a relative string like "Posted Today" instead of a date. Fetching the real values would cost one extra request per job, so they are left empty rather than guessed.

Can I get only the changes?

Yes — set outputMode to changes. You still pay only for the rows returned.

Why is the description excluded by default?

ATS platforms rewrite whitespace and tracking parameters in descriptions constantly. Including them multiplies dataset size, and comparing them would flag every job as updated every day. The change fingerprint deliberately ignores the description.

What about Personio?

Personio's public search endpoint answers 429 to every request, including a single one after a long pause. That is a closed door, not a rate limit, and this Actor does not work around protections.

Can you add another ATS?

Open an issue on the Actor's Issues tab with the careers URL of a company that uses it. Any platform with a public JSON endpoint is straightforward to add.

Development

$apify run
$npm test