ATS Jobs Scraper: Greenhouse, Lever, Ashby & Workday
Pricing
from $2.00 / 1,000 job scrapeds
ATS Jobs Scraper: Greenhouse, Lever, Ashby & Workday
Scrape every open job from a company's ATS by domain or board URL. Auto-detects Greenhouse, Lever, Ashby, Workday, SmartRecruiters and Workable from the careers page and pulls official public JSON feeds. Flat job schema, salary, remote flag, filters. No proxies.
Pricing
from $2.00 / 1,000 job scrapeds
Rating
0.0
(0)
Developer
Majd Hijazin
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
4 days ago
Last modified
Categories
Share
ATS Jobs Scraper — Greenhouse, Lever, Ashby, Workday, SmartRecruiters
Give it a company domain, get every open job back. ATS Jobs Scraper finds a company's careers page, detects which applicant tracking system (ATS) it uses, and pulls all open positions from that ATS's official public job feed. You get one clean, flat dataset row per job: title, department, location, remote flag, employment type, posted date, apply URL, salary when published, and optional full description.
- 6 ATSs, one schema: Greenhouse jobs, Lever jobs, Ashby jobs, Workday jobs, SmartRecruiters jobs and Workable jobs all come out in the same format.
- Domain in, jobs out: pass
stripe.comorcrowdstrike.com. You don't need to know the board URL or slug. - Official JSON feeds, no HTML guessing: data comes from
boards-api.greenhouse.io,api.lever.co,api.ashbyhq.com, Workday'scxsJSON,api.smartrecruiters.comand Workable's public widget API. These are the same feeds the companies' own careers pages use, so the data is complete, current and stable. - No proxies, no browser: plain HTTPS. This keeps runs fast (10+ companies in under a minute) and cheap.
- $0.002 per job. You pay $0.01 per company only when an ATS is actually found. Domains with no supported ATS cost nothing.
Common uses: hiring-signal lead generation (who is hiring SDRs or data engineers right now), job board and aggregator feeds, competitor hiring intelligence, recruiting and sourcing pipelines, labor-market research and AI agents that need structured job data.
How it works
- Careers page scraper. For each domain the Actor fetches the homepage, follows careers/jobs links (including
careers./jobs.subdomains) and common paths like/careers. It finds ATS references in links, embeds, iframes, scripts and Next.js payloads. It obeysrobots.txtand fetches at most one HTML page per second per host. - ATS slug probe (fallback). If the site links to no ATS (common with custom React careers pages), the Actor tries the public ATS APIs with slugs derived from the domain, e.g.
stripe.com→greenhouse:stripe. A probe counts only when the board's own company name, or text in its postings, matches the domain. This keeps it from attributing another company's board. - Official feed. Jobs are read from the ATS's public JSON API, paging where the API requires it (Workday, SmartRecruiters).
- Normalize, filter, dedupe. Every record is mapped to the schema below. Your filters are applied, results are sorted newest first, and
maxJobsPerCompanyis enforced before anything is saved or charged.
| ATS | Detected from | Feed used | Posted date | Salary |
|---|---|---|---|---|
| Greenhouse | boards.greenhouse.io, job-boards.greenhouse.io, embed ?for= | boards-api.greenhouse.io/v1/boards/{slug}/jobs?content=true | first_published (exact) | parsed from pay-range text |
| Lever (US & EU) | jobs.lever.co, jobs.eu.lever.co | api.lever.co/v0/postings/{slug} | createdAt (exact) | salaryRange (structured) |
| Ashby | jobs.ashbyhq.com, embedded boards | api.ashbyhq.com/posting-api/job-board/{slug} | publishedAt (exact) | compensation tiers (structured) |
| Workday | *.wdN.myworkdayjobs.com, wdN.myworkdaysite.com | /wday/cxs/{tenant}/{site}/jobs | "Posted N days ago" (day precision), exact with descriptions on | parsed from description |
| SmartRecruiters | jobs. / careers.smartrecruiters.com | api.smartrecruiters.com/v1/companies/{id}/postings | releasedDate (exact) | parsed from description |
| Workable | apply.workable.com, *.workable.com | apply.workable.com/api/v1/widget/accounts/{slug} | published_on (date) | parsed from description |
Input
| Field | Type | What it does |
|---|---|---|
domains | array of strings | Company websites, e.g. ["stripe.com", "linear.app"]. ATS is auto-detected. |
boardUrls | array of strings | Known boards: any board URL, or shorthands greenhouse:figma, lever:palantir, lever:eu/slug, ashby:ramp, workday:visa/Visa@wd5, smartrecruiters:ServiceNow, workable:huggingface. |
keyword | string | Keeps jobs whose title, department or team contain all the words. "machine learning" engineer matches the quoted phrase and the word. |
location | string | Keeps jobs whose location contains the text. Use ` |
remote | boolean | Keeps only remote jobs. |
postedWithinDays | integer | Keeps only jobs first published in the last N days. Jobs with no date are excluded. |
maxJobsPerCompany | integer (default 500) | Caps the number of jobs saved and charged per company, newest first. |
includeDescription | boolean (default false) | Adds the plain-text descriptionText. For Workday it also adds the exact posting date and employment type. |
detectBySlug | boolean (default true) | Turns the slug-probe fallback on or off. |
maxConcurrency | integer (default 4) | Number of companies processed in parallel. Each host is still rate-limited. |
Example: engineering jobs posted in the last 30 days at four companies
{"domains": ["stripe.com", "linear.app", "crowdstrike.com"],"boardUrls": ["lever:palantir"],"keyword": "engineer","postedWithinDays": 30,"maxJobsPerCompany": 50}
Example: every open job at a list of target accounts (lead generation)
{ "domains": ["ramp.com", "notion.so", "datadoghq.com", "airbnb.com", "servicenow.com", "visa.com"] }
Output
One dataset item per job. Every field is always present; unknown values are null.
{"company": "Ramp","domain": "ramp.com","ats": "ashby","atsSlug": "ramp","jobId": "b7514711-8a29-4bd2-a97a-967be14e336f","title": "Senior Backend Engineer | Token Spend Management","department": "Engineering","team": "Backend","location": "New York, NY (HQ); Remote (Canada); Remote (US)","remote": true,"workplaceType": "hybrid","employmentType": "Full-time","postedAt": "2026-10-20T13:40:20.264Z","postedAtApproximate": false,"updatedAt": null,"applyUrl": "https://jobs.ashbyhq.com/ramp/b7514711-8a29-4bd2-a97a-967be14e336f/application","jobUrl": "https://jobs.ashbyhq.com/ramp/b7514711-8a29-4bd2-a97a-967be14e336f","descriptionText": null,"salaryMin": 204000,"salaryMax": 280000,"salaryCurrency": "USD","salaryPeriod": "YEAR","salaryText": "$204K - $280K","scrapedAt": "2026-10-20T18:14:55.512Z","sourceUrl": "https://api.ashbyhq.com/posting-api/job-board/ramp?includeCompensation=true"}
| Field | Meaning |
|---|---|
company, domain | Company name (from the ATS, else the website) and the domain you submitted (null for direct boards). |
ats, atsSlug | greenhouse · lever · ashby · workday · smartrecruiters · workable, plus the board identifier (Workday: tenant/site). |
jobId | The ATS's own job ID (Workday: requisition ID). Stable across runs, so you can diff runs to find new or closed roles. |
title, department, team | As published. |
location, remote, workplaceType | Location text (multiple locations joined with ; ). remote is true/false/null. workplaceType is remote/hybrid/onsite/null. |
employmentType | Full-time, Part-time, Contract, Internship, or the raw value. |
postedAt, postedAtApproximate, updatedAt | ISO 8601 UTC. postedAtApproximate is true when the date comes from Workday's relative label. |
applyUrl, jobUrl | Direct application link and public job page. |
descriptionText | Plain text, only when includeDescription is on. |
salaryMin, salaryMax, salaryCurrency, salaryPeriod, salaryText | Structured pay from Ashby and Lever, otherwise a pay range stated in the posting ($150,000—$200,000 USD). null when the employer publishes none. |
scrapedAt, sourceUrl | When the record was fetched and from which public ATS endpoint. |
The run also writes an OUTPUT record in the key-value store with one entry per input. Each entry has the detected boards, the detection method (careers_page, api_slug_probe, direct_input), job counts, timing, a clear status (ok, no_matching_jobs, no_ats_found, website_unreachable, ats_error, budget_exhausted, invalid_input) and any errors. The dataset has three views: Overview, Salaries and All fields.
Pricing (pay per event)
| Event | Price | When it is charged |
|---|---|---|
company-resolved | $0.01 | Once per input domain where at least one supported ATS board was found and read. |
job-scraped | $0.002 | Once per job saved to the dataset, after your filters and maxJobsPerCompany. |
You are never charged for domains with no supported ATS, unreachable websites, invalid input, or ATS detection on direct boardUrls (those pay only per job).
Examples:
- 10 company domains, 50 jobs each: 10 × $0.01 + 500 × $0.002 = $1.10
- All 3,614 open jobs at 12 large employers (Stripe, OpenAI, Visa, ServiceNow…, capped at 500 each): $7.35
- Checking 1,000 domains for engineering roles posted this week, 120 with matches: about $1.20 + $0.002 per matching job
Spending limit. Set Maximum cost per run (maxTotalChargeUsd) in the run options. The Actor never charges past it. When the budget runs out, it saves what has been paid for, marks the remaining companies budget_exhausted in OUTPUT, and finishes successfully. It does not charge a company fee unless the budget also covers that company's jobs. If the budget can't pay for even one job, the run stops right away with BUDGET_TOO_LOW.
Call it from the API, code or an AI agent
HTTP (synchronous, returns the jobs):
curl -X POST "https://api.apify.com/v2/acts/impressionable_lupine~ats-jobs-scraper/run-sync-get-dataset-items?token=$APIFY_TOKEN" \-H "Content-Type: application/json" \-d '{"domains":["linear.app","figma.com"],"keyword":"engineer","maxJobsPerCompany":20}'
JavaScript:
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: process.env.APIFY_TOKEN });const run = await client.actor('impressionable_lupine/ats-jobs-scraper').call({ domains: ['stripe.com'], postedWithinDays: 7 });const { items } = await client.dataset(run.defaultDatasetId).listItems();
Python:
from apify_client import ApifyClientclient = ApifyClient("<APIFY_TOKEN>")run = client.actor("impressionable_lupine/ats-jobs-scraper").call(run_input={"domains": ["crowdstrike.com"], "remote": True})jobs = list(client.dataset(run["defaultDatasetId"]).iterate_items())
MCP (Claude, Cursor, any MCP client). Add the Apify MCP server with this Actor enabled:
{"mcpServers": {"apify": {"url": "https://mcp.apify.com/?actors=impressionable_lupine/ats-jobs-scraper","headers": { "Authorization": "Bearer <APIFY_TOKEN>" }}}}
Then ask: "Which of these 20 companies are hiring data engineers in Europe? Use ats-jobs-scraper." The agent calls the Actor with domains, keyword and location, and gets the structured jobs back.
Tested on real companies
Local end-to-end run, 20 Oct 2026, 14 domains, no proxies. 12 of the 12 companies on a supported ATS resolved, with 3,614 jobs in 48 seconds: Linear, Ramp and OpenAI (Ashby); Figma, Stripe, Airbnb and Datadog (Greenhouse); Palantir (Lever); Visa and CrowdStrike (Workday); ServiceNow (SmartRecruiters); Hugging Face (Workable). The two negative controls returned a clear free no_ats_found: NVIDIA (Eightfold, not supported) and Bosch (careers host disallows crawlers in robots.txt).
Limitations
- Supported ATSs only: Greenhouse, Lever, Ashby, Workday, SmartRecruiters and Workable. Companies on iCIMS, Taleo, SuccessFactors, Eightfold, Phenom, BambooHR, Teamtailor, Personio or a fully in-house system come back as
no_ats_foundand are not charged. If you know a company's board URL on a supported ATS, pass it inboardUrls. - robots.txt is respected on company websites. If a careers site disallows crawlers, detection by page is skipped, and the slug probe or a direct
boardUrlsentry is the way in. - Workday returns at most 2,000 jobs per site through its public feed. Dates in listings are "Posted N days ago" (day precision; "30+ days" has no exact date) unless
includeDescriptionis on. Department isn't exposed in listings. - Slug probing only accepts boards whose name or postings match the domain. Companies whose ATS slug differs from their domain (e.g.
BoschGroup) need a careers-page link or aboardUrlsentry. - Salary appears only when the employer publishes it. Text-derived ranges are conservative and need a currency symbol on both ends.
- Each run reads live data. To track new or closed roles, schedule the Actor and compare
jobIds between runs.
FAQ
Is it legal to scrape ATS job boards? The Actor reads only public job postings from the official, unauthenticated feeds the ATS vendors provide for careers pages. No login, no personal data, and robots.txt is honoured on company sites. Check that your use fits your jurisdiction and the sites' terms.
How is this different from a LinkedIn or Indeed scraper? It goes to the source. ATS feeds are where companies publish first, they include roles never syndicated to job boards, and they carry the ATS's job ID, exact posting date and the direct apply link, with no duplicates or reposts.
Do I need the Greenhouse/Lever slug? No. Give the domain. If you already have the board URL or slug, put it in boardUrls to skip detection and the $0.01 company fee.
Why was a domain no_ats_found? The company uses an ATS that isn't supported, its careers page loads jobs from an unsupported source, or robots.txt blocked the careers host. The OUTPUT record shows which pages were checked and why. You are not charged.
Can I get only new jobs? Use postedWithinDays: 1 on a daily schedule, or diff jobId across runs.
How fast is it? About 2–10 s per company for Greenhouse, Lever, Ashby and Workable. Large Workday sites take 10–40 s because the feed pages 20 jobs at a time. Companies run in parallel.
Does it use proxies? No. Official feeds don't need them, and that's why the price per job can be low.
What happens if an ATS changes its API? Each adapter validates the response shape. A change shows up as an explicit ATS_FORMAT_CHANGED error for that company, never as silently wrong data.
Keywords: ats jobs scraper, greenhouse jobs, lever jobs, workday jobs, careers page scraper, ashby jobs scraper, smartrecruiters jobs, workable jobs, job postings API, company jobs API, hiring signals, job board scraper.