ATS Job Scraper: Greenhouse, Lever, Workday, Oracle +10 More
Pricing
from $1.50 / 1,000 job results
ATS Job Scraper: Greenhouse, Lever, Workday, Oracle +10 More
Open jobs from Greenhouse, Lever, Ashby, Workday, Oracle HCM, BambooHR, Workable, Recruitee, SmartRecruiters, Personio, Rippling, Teamtailor, Breezy & Gem boards as unified JSON: title, salary, location, remote, apply link. New/removed job alerts. No login. $1.50 per 1,000 jobs.
Pricing
from $1.50 / 1,000 job results
Rating
0.0
(0)
Developer
SteadyScrape
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
4 hours ago
Last modified
Categories
Share
ATS Job Feed & Scraper: Greenhouse, Lever, Ashby, Workday, Oracle, Workable, BambooHR & More
Get every open job posting from any company's Greenhouse, Lever, Ashby, Workday, Oracle Cloud HCM, BambooHR, Workable, Recruitee, SmartRecruiters, Personio, Rippling, Teamtailor, Breezy HR or Gem careers page as one clean, unified JSON feed. You give it a list of company job boards (URLs, slugs, or company names from the built-in list), and it returns one record per job with title, department, salary range, remote/hybrid/onsite, parsed city/region/country, employment type, posted date, job and apply links, and the full description. Schedule it daily with Only new jobs and you get a change feed of new (and removed) postings.
- Official public job-board APIs. It reads the JSON endpoints each ATS publishes for career pages. No login, no cookies, no proxies, no browser.
- 14 ATS platforms, 1 schema. Mix Greenhouse, Lever, Ashby, Workday, Oracle Cloud HCM, BambooHR, Workable, Recruitee, SmartRecruiters, Personio, Rippling, Teamtailor, Breezy HR and Gem boards in a single run and get identical fields for all of them.
- Paste any board URL. The platform is auto-detected. A bare company slug like
figmaworks too, and so does a company name or domain from the built-in company list (for exampleBosch Grouporstripe.com). - Job change detection. Output only jobs you haven't received before, plus optional "removed" records when a job is taken down.
- Salary data. Structured where the ATS exposes it (Ashby compensation, Greenhouse pay transparency, Lever
salaryRange, Recruitee, SmartRecruiters, Personio, Rippling and Breezy salary fields), otherwise parsed from the description text.salary.sourcetells you which. - Pay per result. $1.50 per 1,000 jobs. Removed-job records and the run summary are free.
- Robust. Retries with backoff, one broken board never fails the run, and it saves its results before the run timeout instead of losing them. A per-company status report is saved to
OUTPUT.
Note: This Actor fetches the boards you list. It does not search every company on the internet for a keyword. To find jobs at many companies, list their boards (you can pass hundreds in one run) and filter by keyword, location, department or date.
What can you use it for?
- Job alerts. "Tell me when Stripe, Ramp or NVIDIA posts a new engineering role in Canada." Run on a schedule with Only new jobs and send results to email, Slack or a webhook.
- Recruiter and sales leads from hiring signals. See which companies are hiring for which roles, where and at what pay. A company hiring its first data engineer or a new sales team is a buying signal for many B2B products.
- Labor-market and salary research. Collect structured salary ranges, locations, remote share and departments across many employers.
- Job boards and aggregators. Feed a niche job board (for example "remote AI jobs" or "climate tech jobs") with fresh postings every day, with apply links that go straight to the employer's ATS.
- AI agents and LLM workflows. Agents can call this Actor through the Apify API or Apify MCP server to answer "what roles is company X hiring for?" with structured, current data.
Quick start
- Click Try for free (or Start).
- In Company job boards, paste the careers page URLs of the companies you want, for example:
https://job-boards.greenhouse.io/stripehttps://jobs.lever.co/spotifyhttps://jobs.ashbyhq.com/ramphttps://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite
- Optionally add filters (title keywords, locations, remote only, posted within N days) and a Max jobs per company cap.
- Click Start. Jobs appear in the Output tab, where you can download them as JSON, CSV, Excel or HTML, or read them through the API.
Example input
{"companies": ["https://job-boards.greenhouse.io/stripe","https://jobs.lever.co/zoox","https://jobs.ashbyhq.com/ramp","https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite"],"keywords": ["engineer"],"excludeKeywords": ["intern"],"locations": ["US", "Canada"],"postedWithinDays": 14,"maxJobsPerCompany": 200,"onlyNewSinceLastRun": true,"emitRemovedJobs": true}
Run it from the API
curl -X POST "https://api.apify.com/v2/acts/steady-scrape~ats-job-feed/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \-H "Content-Type: application/json" \-d '{"companies": ["greenhouse:stripe", "ashby:ramp"], "keywords": ["engineer"], "maxJobsPerCompany": 50}'
How to find a company's job board (slug)
Open the company's careers page, click any job, and look at the address bar. The URL tells you the ATS and the slug. You can paste the board URL as is, or a job URL from it, or use the short platform:slug form.
| ATS | What the URL looks like | Slug | Short form |
|---|---|---|---|
| Greenhouse | job-boards.greenhouse.io/stripe or boards.greenhouse.io/stripe, or an embedded board boards.greenhouse.io/embed/job_board?for=discord | stripe | greenhouse:stripe |
| Lever | jobs.lever.co/spotify (EU boards: jobs.eu.lever.co/<company>) | spotify | lever:spotify / lever-eu:<company> |
| Ashby | jobs.ashbyhq.com/ramp | ramp | ashby:ramp |
| Workday | nvidia.wd5.myworkdayjobs.com/en-US/NVIDIAExternalCareerSite | tenant nvidia, data center wd5, site NVIDIAExternalCareerSite | workday:nvidia.wd5/NVIDIAExternalCareerSite |
| Workday (alt. domain) | wd3.myworkdaysite.com/recruiting/<tenant>/<site> | paste the URL | |
| BambooHR | acme.bamboohr.com/careers | acme | bamboohr:acme |
| Workable | apply.workable.com/huggingface/ | huggingface | workable:huggingface |
| Recruitee | acme.recruitee.com | acme | recruitee:acme |
| SmartRecruiters | jobs.smartrecruiters.com/Wise or careers.smartrecruiters.com/Wise | company ID Wise | smartrecruiters:Wise |
| Personio | acme.jobs.personio.de (or .com) | acme | personio:acme |
| Oracle Cloud HCM | jpmc.fa.oraclecloud.com/hcmUI/CandidateExperience/en/sites/CX_1001/jobs | host jpmc.fa.oraclecloud.com, site CX_1001 (or the site name in the URL) | oraclehcm:jpmc.fa/CX_1001 |
| Rippling | ats.rippling.com/rippling/jobs | rippling | rippling:rippling |
| Teamtailor | career.teamtailor.com/jobs (regional hosts like acme.na.teamtailor.com work too) | career | teamtailor:career, or teamtailor:careers.acme.com for a custom domain |
| Breezy HR | acme.breezy.hr | acme | breezy:acme |
| Gem | jobs.gem.com/accel | accel | gem:accel |
Tips:
- If a company's careers page is on its own domain (e.g.
company.com/careers), click a job: the apply page usually reveals the ATS URL. - A bare slug (e.g.
figma) is first looked up in the built-in company list, then tried on Greenhouse, Ashby and Lever. Workday always needs the full URL or theworkday:tenant.wdN/siteform, because the site name differs per company. For the other platforms, use the URL or theplatform:slugform. Oracle Cloud HCM needs the full Candidate Experience URL (ororaclehcm:host/site), because each company has its own host and site. Teamtailor boards on a company's own domain needteamtailor:<that domain>. - The language part of Workday URLs (
/en-US/) is optional and ignored. - If a slug is wrong, that board shows
Board not found (404)in the run summary and the other boards still run.
Built-in company list
You can also enter a company name or domain (Bosch Group, ServiceNow, stripe.com, https://wise.com). The Actor looks it up in a small bundled table of about 580 company job boards: about 380 on the first 9 platforms (Greenhouse through Personio) and 40 each on Rippling, Teamtailor, Breezy HR, Gem and Oracle Cloud HCM. Every board in the table was checked live and had open jobs when the table was built (October 2026). Matching ignores case, punctuation, www., the domain ending and suffixes like Inc or GmbH.
- The list is small on purpose. It is not a directory of every company. If a name is not in it, the run summary lists it under
unresolvedInputswith a clear message, and you can paste the company's careers board URL instead. - Names that matched are listed under
resolvedByLookupin the run summary, and those rows haveresolvedVia: "lookup:<name>", so you can check what each name was mapped to. - An explicit URL or
platform:slugalways wins over the list. - Companies sometimes switch ATS. If a looked-up board stops working, paste the current careers URL.
The table was compiled from these open datasets (see Data sources and credits).
Pricing
This Actor uses pay-per-event pricing:
- $1.50 per 1,000 jobs delivered ($0.0015 per job result).
- A tiny Actor-start fee of $0.00005 per run (at up to 1 GB memory).
- Free: removed-job records, error reports and the run summary.
You pay only for job records written to the dataset. Filters are applied before charging, so jobs you filter out cost nothing.
| What you fetch | Cost |
|---|---|
| 100 jobs | about $0.15 |
| 1,000 jobs | about $1.50 |
| 5,000 jobs (a full first pull of several large boards) | about $7.50 |
| Daily Only new jobs feed that finds 200 new jobs per day | about $0.30 per day |
| Daily Only new jobs feed with no new jobs that day | about $0.00005 (start fee only) |
Platform usage (compute) is included in the price. You can also set a maximum total charge for a run in the run options. The Actor stops cleanly at that limit and does not deliver jobs it can't bill, so you never pay more than you set.
Only new jobs and removed jobs
Turn on Only new jobs since last run (onlyNewSinceLastRun) for scheduled runs:
- The Actor remembers which job IDs it has already delivered for each board, in a named key-value store (
stateStoreName, defaultats-job-feed-state). - Each run outputs only jobs it has not delivered before, marked
changeType: "new". You pay only for those. - First run: by default every current job is output once (
firstRunEmitsAll: true). Set it tofalseto record a silent baseline and get only jobs posted after that. - Removed jobs: with
emitRemovedJobs: true, jobs that disappeared from a board since the last run are output as free records withchangeType: "removed",firstSeenAtandremovedDetectedAt. This needs a complete listing of the board, so for Workday it only works when no cap or keyword pre-filter limited the listing. On SmartRecruiters, a job missing from the listing is re-checked directly before it is reported as removed, because its paged listing can skip a job while the board is changing. - Max jobs per company in this mode caps the new jobs per run. Jobs held back by the cap are delivered on later runs.
- Use different
stateStoreNamevalues for independent feeds (for example one per customer or per alert).
Stops early instead of timing out
If a run is about to hit its timeout (for example you passed many large Workday, BambooHR or SmartRecruiters boards with a short timeout), the Actor stops starting new requests shortly before the deadline (about 20% of the timeout, at most 2 minutes), saves every job collected so far and writes the run summary. The run still succeeds, and the status message says how many boards were completed. The summary lists incompleteBoards and notStartedBoards. The Only new state is updated only for boards that finished, so nothing is lost or skipped next time. To get everything, raise the timeout or reduce the boards or maxJobsPerCompany.
Input
| Field | Description |
|---|---|
companies (required) | Board URLs, job URLs, platform:slug, or company names/domains from the built-in list. See how to find a board. |
keywords / excludeKeywords | Keep jobs whose title contains any of / none of these words (case-insensitive). |
locations | City, region, country name or ISO country code ("US", "Toronto", "Germany"). |
remoteOnly | Only remote jobs. |
departments | Substring match on department or team. |
postedWithinDays | Only jobs posted in the last N days. Jobs with no date are kept. |
includeDescription | Include the HTML and plain-text description (default on). |
maxJobsPerCompany | Cap per board (in Only new mode: cap on new jobs per board). |
onlyNewSinceLastRun | Output only jobs not delivered in earlier runs. |
firstRunEmitsAll | With Only new: on a board's first run, output everything (default) or just record a baseline. |
emitRemovedJobs | With Only new: also output free changeType: "removed" records. |
stateStoreName | Name of the state store. Use different names for independent feeds. |
maxConcurrency, maxRetries | Politeness and robustness settings. |
Output
One dataset item per job, with the same fields on every platform. Example:
{"jobId": "ashby:ramp:34413f8d-26bf-4bbc-8ade-eb309a0e2245","sourceJobId": "34413f8d-26bf-4bbc-8ade-eb309a0e2245","platform": "ashby","boardSlug": "ramp","boardUrl": "https://jobs.ashbyhq.com/ramp","company": "Ramp","title": "Security Engineer, Cloud","department": "Engineering","team": "Backend","locations": [{"raw": "New York, NY (HQ)", "city": "New York City", "region": "New York", "country": "United States", "countryCode": "US", "isRemote": false},{"raw": "Remote (Canada)", "city": null, "region": null, "country": "Canada", "countryCode": "CA", "isRemote": true}],"locationText": "New York, NY (HQ)","isRemote": true,"workplaceType": "HYBRID","employmentType": "FULL_TIME","employmentTypeRaw": "FullTime","salary": {"min": 211400, "max": 290600, "currency": "USD", "interval": "YEAR", "source": "structured", "raw": "$211.4K - $290.6K"},"postedAt": "2026-04-07T17:12:35.753000Z","updatedAt": null,"jobUrl": "https://jobs.ashbyhq.com/ramp/34413f8d-26bf-4bbc-8ade-eb309a0e2245","applyUrl": "https://jobs.ashbyhq.com/ramp/34413f8d-26bf-4bbc-8ade-eb309a0e2245/application","requisitionId": null,"descriptionHtml": "<h1><strong>About Ramp</strong></h1>...","descriptionText": "About Ramp\n\nRamp is building ...","changeType": "new","scrapedAt": "2026-10-25T21:10:02Z"}
Output fields
| Field | Type | Description |
|---|---|---|
jobId | string | Stable unique ID: platform:board:sourceJobId. Use it to deduplicate. |
sourceJobId | string | The job's ID in the ATS. |
platform | string | greenhouse, lever, ashby, workday, bamboohr, workable, recruitee, smartrecruiters, personio, oraclehcm, rippling, teamtailor, breezy or gem. |
boardSlug | string | Board identifier (e.g. stripe, nvidia.wd5/NVIDIAExternalCareerSite, jpmc.fa/CX_1001). |
boardUrl | string | Public careers board URL. |
company | string | Company name as reported by the ATS (or the slug). |
title | string | Job title. |
department / team | string | Department and team, where the ATS provides them. |
locations | array | Parsed locations: raw, city, region, country, countryCode (ISO 3166-1 alpha-2), isRemote. |
locationText | string | Primary location as written in the ATS. |
isRemote | boolean | True if the job is remote or any of its locations is remote. |
workplaceType | string | REMOTE, HYBRID, ONSITE or null. |
employmentType | string | FULL_TIME, PART_TIME, CONTRACT, INTERN, TEMPORARY, OTHER or null. |
employmentTypeRaw | string | Employment type exactly as the ATS reports it. |
salary | object | min, max, currency, interval (YEAR, MONTH, WEEK, DAY, HOUR), source (structured or text), raw. Null if no pay is published. |
postedAt / updatedAt | string | ISO 8601 UTC timestamps, where available. |
jobUrl / applyUrl | string | Job page and application page. |
requisitionId | string | Employer's requisition number, where available. |
descriptionHtml / descriptionText | string | Full description as HTML and plain text (if includeDescription is on). |
changeType | string | new or removed in Only new mode, otherwise null. |
firstSeenAt / removedDetectedAt | string | Only on removed records. |
scrapedAt | string | When the record was fetched (ISO 8601 UTC). |
The run summary is saved to the key-value store record OUTPUT: totals plus per-board status, counts, duration and errors (e.g. Board not found (404)), plus unresolvedInputs (entries that were not a board URL, slug or known company) and resolvedByLookup (company names matched in the built-in list).
What each platform provides
| Field | Greenhouse | Lever | Ashby | Workday |
|---|---|---|---|---|
| Salary | pay-transparency ranges, else text | salaryRange, else text | compensation API, else text | text only |
| Department / team | ✓ | ✓ (team; dept if set) | ✓ | – |
| Employment type | only via custom metadata | ✓ | ✓ | ✓ |
| Workplace type | from location text | ✓ | ✓ | from location text |
| Posted date | first published | created | published | start date / "Posted N days ago" |
| Updated date | ✓ | – | – | – |
| Field | BambooHR | Workable | Recruitee | SmartRecruiters | Personio |
|---|---|---|---|---|---|
| Salary | compensation text on the job page | text only | ✓ salary fields | compensation fields (needs descriptions on), else text | salary fields where published, else text |
| Department / team | department | ✓ (team = function) | department | department or function | ✓ (team = category) |
| Employment type | ✓ | ✓ | ✓ | ✓ | ✓ |
| Workplace type | ✓ (onsite / remote / hybrid) | remote flag only | ✓ | remote / hybrid flags | from location text |
| Posted date | ✓ | published | published | released | created |
| Updated date | – | – | ✓ | – | – |
| Field | Oracle Cloud HCM | Rippling | Teamtailor | Breezy HR | Gem |
|---|---|---|---|---|---|
| Salary | text only | pay range fields (needs descriptions on), else text | text only | ✓ salary field (structured on the job page) | text only |
| Department / team | business unit or job family / job function | ✓ | ✓ (team = role) | department | ✓ |
| Employment type | ✓ (schedule) | ✓ (needs descriptions on) | – | ✓ | ✓ |
| Workplace type | ✓ where the employer sets it | ✓ | ✓ (remote status) | remote flag | ✓ |
| Posted date | ✓ | created (needs descriptions on) | published | published | first published |
| Updated date | – | – | – | – | ✓ |
Requests per board: Greenhouse, Lever, Ashby, Workable, Recruitee, Personio, Teamtailor and Gem return a whole board in one or a few requests. BambooHR always needs one request per job (the posted date and pay are only on the job page). SmartRecruiters, Oracle Cloud HCM, Rippling and Breezy HR need one request per job only when descriptions are on; Workday always does. Oracle lists 200 jobs per request and returns at most the newest 10,000 jobs per site. maxJobsPerCompany limits these per-job requests.
Performance
Tested on the Apify platform on Oct 25, 2026 at 256 MB, with the first four platforms:
- 670 jobs from 8 boards (2 per platform, capped at 100 jobs per company) in 47 s.
- A second run in Only new mode took 6 s. It delivered the 205 jobs that the cap had held back and nothing it had already delivered.
- The default memory of 256 MB is enough. Peak memory was about 147 MB.
Greenhouse, Lever, Ashby, Workable, Recruitee, Personio, Teamtailor and Gem return a whole board in a few requests, so they are fast. Workday, BambooHR and (with descriptions on) Oracle, Rippling, Breezy and SmartRecruiters need one request per job for details, so large boards on them take longer.
How it compares
Verified on Apify Store, Oct 25, 2026. Prices shown are the Free-tier price per job.
| Actor | Platforms | Price per 1,000 jobs | Change detection |
|---|---|---|---|
| This Actor | Greenhouse, Lever, Ashby, Workday, Oracle Cloud HCM, BambooHR, Workable, Recruitee, SmartRecruiters, Personio, Rippling, Teamtailor, Breezy HR, Gem | $1.50 | New + removed jobs |
| bovi/greenhouse-lever-ashby-job-scraper | Greenhouse, Lever, Ashby, Recruitee, SmartRecruiters, Personio (no Workday) | $1.50 | n/a |
| fantastic-jobs/greenhouse-jobs-api / workday-jobs-api | One ATS per Actor | $2.00 | n/a |
| memo23/career-site-ats-jobs-api | 116 ATS platforms | $4.00 + $0.03 per run | n/a |
Source: Apify Store API (api.apify.com/v2/store, field currentPricingInfo), fetched Oct 25, 2026 (competitor rows; this Actor's platform list updated Oct 26, 2026). Competitor prices can change at any time. "n/a" means not checked.
FAQ
Is it legal to scrape job postings? (Not legal advice.) This Actor reads only public job postings that companies publish so that they get distributed, through the public JSON endpoints that power their own careers pages. It uses no login, collects no personal data about candidates or employees, and doesn't bypass any access control. You are responsible for how you use the data, e.g. respecting each site's terms when you republish it.
Will I get rate-limited or blocked? Unlikely at normal volumes. The Actor sends at most 5 parallel requests by default (maxConcurrency), and retries 429/5xx errors with exponential backoff (maxRetries). No proxies are needed. If a board fails anyway, it is reported in OUTPUT and the other boards still finish.
How do I run it on a schedule? In Apify Console, open the Actor, save your input as a task, then add a Schedule (e.g. daily at 08:00). Turn on Only new jobs so each run returns only fresh postings.
Can I send the jobs to Google Sheets, Slack, Zapier, Make or my own app? Yes. Use Apify integrations (Google Sheets, Slack, email, Zapier, Make, n8n and others) on the Actor or task, or add a webhook that calls your URL when a run succeeds. You can also read the dataset through the Apify API in JSON, CSV or Excel format.
Can AI agents use it? Yes. It can be called through the Apify API and the Apify MCP server. The input and output fields are fully described so agents can call it correctly.
Why are some fields null? The ATS doesn't publish them (see the platform table). Nothing is invented: salary is parsed from text only when an explicit currency range appears, and salary.source says so.
Can it search all jobs on Greenhouse or Workday by keyword? No. These ATSs have no public cross-company search. You list the company boards you care about (or company names from the built-in list), and the Actor fetches and filters them.
Workday boards with more than 2,000 jobs? Workday's public list endpoint appears to cap at 2,000 (NVIDIA reports exactly total: 2000). Use keywords or postedWithinDays to narrow very large boards.
What happens if I set a low timeout or a max charge? The Actor stops cleanly and saves what it has (see Stops early instead of timing out and Pricing).
More tools from SteadyScrape
- Career Site Jobs Search: search open jobs by keyword across tens of thousands of company career sites, with remote, country, company and date filters.
- Remote Jobs API: remote-only jobs from employer career sites, filtered by where you can work from (countries, regions, time zones).
Feedback and support
Found a bug, need another ATS, want a company added to the built-in list, or want a new field? Please open an issue in the Issues tab of this Actor.
Data sources and credits
The built-in company list was compiled from these open datasets, and every board was then re-checked live:
- job-board-aggregator by Riley Dorrington (MIT License).
- job-board-directory by kalebconfer-sys (CC0 1.0, public domain).
- Hiring data from LastRound AI, licensed under CC BY 4.0. Source: LastRound AI Salary Transparency Study 2026 (dataset). Changes: the company/board entries were filtered to boards that were live with open jobs, normalized to
platform:slugform and merged with the other sources. - SmartRecruiters company IDs were added by hand and checked live.
- ats-career-page-urls by latmay, licensed under CC BY 4.0 (Rippling, Teamtailor, Breezy HR, Gem and Oracle Cloud HCM boards). Changes: reduced to board identifiers, filtered to boards that were live with open jobs, company names read from the boards.
- state-of-ats-2026 by Kayvan Zahiri (MIT License): Oracle Cloud HCM career-site hosts.
- Board URLs found in the public Common Crawl URL index.
Job postings themselves are fetched live from each company's public careers board at run time.