ATS Jobs API - Workday, Greenhouse, Lever, Ashby +8 job scraper
Pricing
from $0.95 / 1,000 job postings
ATS Jobs API - Workday, Greenhouse, Lever, Ashby +8 job scraper
Every public job from company career pages on 12 ATS (Workday, Greenhouse, Lever, Ashby, Personio, BambooHR, Rippling, SmartRecruiters, Workable ...). Auto-detect, presets, seniority, salary, Markdown, new/closed job alerts. $1.20 per 1,000 jobs.
Pricing
from $0.95 / 1,000 job postings
Rating
0.0
(0)
Developer
WEIGUO HAN
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
14 days ago
Last modified
Categories
Security
Limited permissionsShare
ATS Jobs API - Workday, Greenhouse, Lever, Ashby, Personio, BambooHR & 6 more job scrapers in one (no API key)
Extract every public job posting from any company's careers page in one run. Give the Actor a careers URL, a company domain, an ats:slug pair - or just pick a company preset (AI labs, fintech, remote-first, big tech). It auto-detects the applicant tracking system (ATS) among 12 platforms, calls the vendor's public JSON / XML job feed (no HTML scraping, no login, no API key, no proxy), and returns clean, normalized jobs: title, seniority, locations, remote / hybrid / onsite, department, employment type, posted/updated dates, salary range when published, and the full description as Markdown and plain text.
Run it on a schedule and it also tells you what changed since last time: every job is labelled new, updated or unchanged, and jobs that disappeared are emitted as closed (free). That makes it a drop-in hiring-signal feed for sales prospecting, recruiting intelligence, job boards, salary research and AI agents.
Pricing: $1.20 per 1,000 jobs (pay per event, no extra start fee) - the same price on all 12 ATS platforms, including Workday.
Why this Actor
What this Actor delivers (facts checked 2026-10-16):
| This Actor | |
|---|---|
| ATS coverage | 12: Workday, Greenhouse, Lever, Ashby, SmartRecruiters, Workable, Recruitee, Personio, BambooHR, Breezy HR, Rippling, Gem - one Actor, one schema |
| Price per 1,000 jobs | $1.20 flat, the same on all 12 ATS including Workday; no extra start fee beyond Apify's platform default |
| What is free | closed job records, filtered-out jobs, failed boards, and ATS detection for careers URLs, ats:slug inputs and presets |
| Input | Careers URL, company domain, greenhouse:gitlab style slug or a curated preset - ATS auto-detected |
| Change detection | new / updated / unchanged / closed (closed records free) |
| Salary | Normalized salaryMin/Max/Currency/Period (Ashby, Lever, Recruitee, Greenhouse pay ranges, Breezy, Rippling) |
| Seniority | Normalized level (intern ... executive) from title + ATS experience level, filterable |
| Description | Markdown and plain text (RAG / LLM ready) |
| Reliability | One slow or broken board never fails the run (per-company time budget, partial results kept) |
| Politeness | <= 2 requests/s per host, exponential backoff, Retry-After, robots.txt |
Supported ATS platforms and the endpoints used
All sources are public, read-only, unauthenticated endpoints that the vendors provide for displaying jobs on third-party sites.
| ATS | Public endpoint used | Pagination | Vendor documentation |
|---|---|---|---|
| Greenhouse | GET https://boards-api.greenhouse.io/v1/boards/{board_token}/jobs?content=true (EU: boards-api.eu.greenhouse.io) | single response (page fallback when meta.total is larger) | Greenhouse Job Board API - "Job Board data is publicly available, so authentication is not required for any GET endpoints" |
| Lever | GET https://api.lever.co/v0/postings/{site}?mode=json&skip={n}&limit=100 (EU: api.eu.lever.co) | skip / limit | Lever Postings API |
| Ashby | GET https://api.ashbyhq.com/posting-api/job-board/{board}?includeCompensation=true | single response | Ashby Public Job Posting API |
| SmartRecruiters | GET https://api.smartrecruiters.com/v1/companies/{company}/postings?limit=100&offset={n} + GET .../postings/{id} for the description | offset / totalFound | SmartRecruiters Posting API |
| Workable | GET https://apply.workable.com/api/v1/widget/accounts/{subdomain}?details=true | single response | Workable job widget / JSON feed |
| Recruitee | GET https://{company}.recruitee.com/api/offers/ | single response | Recruitee Careers Site API - token required from 2027-02-10 (optional input provided) |
| Personio | GET https://{company}.jobs.personio.de/xml?language=en (also .jobs.personio.com) | single response, descriptions included | Personio XML feed |
| BambooHR | GET https://{company}.bamboohr.com/careers/list + GET .../careers/{id}/detail for the description | single response | The anonymous JSON behind every BambooHR /careers page (robots.txt allows it) |
| Breezy HR | GET https://{company}.breezy.hr/json?verbose=true | single response, descriptions included | Breezy's public careers-portal JSON feed |
| Rippling | GET https://api.rippling.com/platform/api/ats/v1/board/{board}/jobs + .../jobs/{uuid} for the description | single response | The anonymous API behind hosted Rippling boards (ats.rippling.com/{board}/jobs) |
| Gem | GET https://api.gem.com/job_board/v0/{board}/job_posts/ | single response, descriptions included | The anonymous API behind hosted Gem boards (jobs.gem.com/{board}) |
| Workday | POST https://{tenant}.wd{N}.myworkdayjobs.com/wday/cxs/{tenant}/{site}/jobs ({"appliedFacets":{},"limit":20,"offset":n,"searchText":""}) + GET .../wday/cxs/{tenant}/{site}{externalPath} for the description | offset / total, 20 per page | Workday does not document this endpoint; it is the employer career site's own anonymous JSON API used by every Workday careers page. The employer host's robots.txt is honoured. |
How to scrape jobs from a careers page (tutorial)
- Open the Actor and either pick a Company preset (no research needed) or paste one or more entries into Companies. Any of these work:
- a careers URL:
https://boards.greenhouse.io/gitlab,https://jobs.lever.co/palantir,https://jobs.ashbyhq.com/ashby,https://jobs.smartrecruiters.com/BoschGroup,https://apply.workable.com/blueground,https://bunq.recruitee.com,https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite,https://acme.jobs.personio.de,https://acme.bamboohr.com/careers,https://acme.breezy.hr,https://ats.rippling.com/acme/jobs,https://jobs.gem.com/acme - an explicit pair:
greenhouse:gitlab,lever:palantir,ashby:ashby,smartrecruiters:BoschGroup,workable:blueground,recruitee:bunq - a bare domain or name:
gitlab.com- the Actor scans the company's/careersand/jobspages for ATS links and probes each vendor API with the domain label as a slug. Successful detections are cached for 7 days.
- a careers URL:
- Optionally set Max jobs per company, Title keywords, Exclude title keywords, Seniority levels, Locations, Remote only or Posted after.
- Keep Detect changes on if you will run the Actor repeatedly (daily schedule, webhook, integration).
- Click Start. Results land in the dataset (table / JSON / CSV / Excel) and a per-company summary is stored under the
SUMMARYkey of the run's key-value store.
Company presets
| Preset | Companies (verified 2026-10-16) |
|---|---|
ai-labs | Anthropic, OpenAI, Scale AI, Cohere, Hugging Face, Perplexity, Databricks, ElevenLabs, Replit, LangChain, Harvey, Cursor, Together AI, Modal, Writer, Sierra, Character.AI, Stability AI, Pinecone |
fintech | Stripe, Ramp, Brex, Robinhood, Coinbase, Chime, Affirm, Monzo, N26, SoFi, Toast, Block |
remote-first | GitLab, Zapier, Canonical, DuckDuckGo, Automattic, PostHog, Supabase, Grafana Labs, Elastic, Mozilla, Wikimedia |
big-tech | NVIDIA, Salesforce, Adobe, Intel, Mastercard, Workday (all Workday) + Cloudflare, Datadog, Airbnb, Pinterest, Dropbox, Reddit, Discord |
Presets are added to your own Companies list (duplicates removed). Preset boards are given as ATS URLs or slugs, so they are never charged for detection.
Input example: senior engineering roles at AI labs, daily monitor
{"presets": ["ai-labs"],"keywords": ["engineer"],"excludeKeywords": ["intern"],"seniority": ["senior", "staff", "principal"],"detectChanges": true,"emitUnchanged": false}
Input example: mixed list
{"companies": ["https://boards.greenhouse.io/gitlab","lever:palantir","https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite","gitlab.com"],"includeDescription": true,"keywords": ["engineer", "data"],"remoteOnly": false,"detectChanges": true,"emitUnchanged": false,"maxJobsPerCompany": 0}
Output example (one dataset item)
{"id": "greenhouse:gitlab:8860302002","jobId": "8860302002","title": "Account Executive - France","company": "GitLab","companySlug": "gitlab","ats": "greenhouse","url": "https://job-boards.greenhouse.io/gitlab/jobs/8860302002","applyUrl": "https://job-boards.greenhouse.io/gitlab/jobs/8860302002","location": "Remote, France","locations": ["Remote, France", "France"],"remote": true,"workplaceType": "remote","department": "EMEA - Commercial","team": null,"employmentType": null,"postedAt": "2026-10-02T15:31:50Z","updatedAt": "2026-10-02T15:31:50Z","descriptionMarkdown": "GitLab is the intelligent orchestration platform...","descriptionText": "GitLab is the intelligent orchestration platform...","salaryMin": null,"salaryMax": null,"salaryCurrency": null,"salaryPeriod": null,"language": "en","seniority": null,"change": "new","firstSeenAt": "2026-10-07T05:30:12Z","scrapedAt": "2026-10-07T05:30:12Z","fingerprint": "6c4d2b1e9a0f7c3d5e8b","raw": { "...": "untouched vendor fields (disable with includeRaw=false)" }}
An Ashby job with published compensation carries "salaryMin": 110000, "salaryMax": 185000, "salaryCurrency": "EUR", "salaryPeriod": "year".
A closed job (present in the previous run, gone now) is a small free record:
{ "id": "lever:palantir:6ed76ce8-...", "title": "Administrative Business Partner", "company": "palantir", "ats": "lever", "url": "https://jobs.lever.co/palantir/6ed76ce8-...", "change": "closed", "firstSeenAt": "2026-09-01T08:00:00Z", "lastSeenAt": "2026-10-06T08:00:00Z", "closedAt": "2026-10-07T08:00:00Z" }
Change detection (new / updated / closed jobs)
With detectChanges on, the Actor keeps a fingerprint (title, locations, department, employment type, salary, vendor updatedAt, description hash) of every job per board in the named key-value store stateStoreName (default ats-jobs-api-state). On the next run each job is labelled:
new- not seen before (firstSeenAtis set now)updated- fingerprint changedunchanged- identical (setemitUnchanged: falseto skip and not pay for these)closed- present last time, missing now (free,emitClosed)
The description hash is part of the fingerprint, so toggling includeDescription between runs reports every job as updated once. closed is only computed when the whole board was listed in both runs; if a run is truncated by maxJobsPerCompany, filters, a rate limit or a vendor error, the state is merged instead of replaced and no false "closed" events are produced. Use different stateStoreName values for independent monitors.
Pricing (pay per event)
| Event | What it means | Price |
|---|---|---|
job | One job record written to the dataset (new, updated or unchanged) | $0.0012 ($1.20 / 1,000 jobs) |
company-detected | ATS auto-detected from a bare domain or company name (careers-page scan + API probing). Not charged for ATS URLs or ats:slug inputs | $0.01 per company |
| Actor start | Apify's standard synthetic start event at the platform default | platform default |
Closed-job records, filtered-out jobs and failed boards are never charged. Proposed volume discounts for higher Apify plans: job $0.0010 (Silver) / $0.0008 (Gold); company-detected $0.008 / $0.006.
Cost examples
- Monitor 50 companies via their careers URLs, ~2,000 open jobs, daily run with
emitUnchanged: false: ~40 new/updated jobs a day -> about $0.05 per day (plus the start event). - One-off export of 10 Greenhouse/Lever/Workday boards with 5,000 jobs including descriptions -> $6.00.
- Lead list of 200 bare domains, auto-detected once (then cached 7 days), 8,000 jobs -> $2.00 detection + $9.60 jobs = $11.60.
Use cases
- Sales & lead generation - "who is hiring for X right now" signals for outbound (export to CSV, Google Sheets, HubSpot via Zapier/Make/n8n).
- Recruiting & talent intelligence - track competitor headcount plans, new offices, remote policy shifts.
- Job boards & aggregators - one normalized feed instead of twelve integrations.
- Salary research - Ashby, Lever, Recruitee and Greenhouse pay-transparency ranges in one schema.
- AI agents / MCP - "What is NVIDIA hiring for in Munich?" becomes one Actor call; Markdown descriptions are ready for RAG.
Limitations and good-to-know
- Workday: the endpoint is undocumented by Workday (stable for years, but it can change). Descriptions need one request per job; at the polite 2 req/s limit a 2,000-job tenant takes ~17 minutes - set
includeDescription: falsefor fast monitoring. Workday boards must be given as a careers URL (tenant + site cannot be guessed from a domain). - BambooHR and Rippling descriptions also need one request per job (2 req/s per host); turn
includeDescriptionoff for very large boards or raisemaxSecondsPerCompany. - Seniority is derived from the job title (e.g. "Senior", "Staff", "Head of", "Werkstudent") and, for Personio and BambooHR, the employer's own experience level. Titles that say nothing about level get
seniority: null; with the Seniority filter on, those jobs are dropped. - SmartRecruiters descriptions also need one request per job. Its API host's robots.txt disallows all generic crawlers while the vendor's Posting API documentation permits anonymous reads; the default
robotsPolicy: vendor-apifollows the API terms,strictdisables SmartRecruiters. - Recruitee announced an
X-Careers-Sites-Tokenrequirement from 2027-02-10; paste your token intorecruiteeCareersSiteTokenonce it is enforced. - Auto-detection from a domain works when the company's
/careersor/jobspage links to its ATS or when its slug equals the domain label (gitlab.com->gitlab). JavaScript-only careers pages with a non-obvious slug cannot be detected - pass the careers URL instead. remotecombines vendor flags with a keyword heuristic on location/title;employmentTypekeeps the vendor's wording (e.g.Full-time,Full time,FullTimeis mapped).- Salary is only present when the employer publishes it through the ATS.
- No personal data is collected: the Actor reads job advertisements only (recruiter e-mails in Recruitee's
mailbox_emailare kept inrawonly if you leaveincludeRawon). - The Actor stops a board on persistent
429,401/403or robots.txt disallow and reports it in the run summary instead of hammering the host. - One board never fails the run. Each company has its own time budget (
maxSecondsPerCompany, default 180 s) and error isolation: a slow, blocked or broken board is cut off with a warning inSUMMARY, its jobs fetched so far are kept, and the other companies finish normally. The run only fails when every board failed and nothing was written. - Change-detection state lives in the named key-value store
stateStoreName(defaultats-jobs-api-state). If that store cannot be opened the Actor falls back to the run's own store and says so in the log /SUMMARY.stateStoreinstead of failing.
FAQ
Do I need API keys or accounts? No. Every source is a public, unauthenticated endpoint.
Is scraping these job boards allowed? Greenhouse, Lever, Ashby, SmartRecruiters, Workable and Recruitee publish these endpoints precisely so third parties can display the jobs. Workday career sites expose the same JSON their own page uses; the Actor honours each employer host's robots.txt and sends at most 2 requests/second. You remain responsible for how you use the data.
How do I get only new jobs? Keep detectChanges: true and set emitUnchanged: false; schedule the Actor daily and connect a webhook or integration.
Which ATS do you support? Workday, Greenhouse, Lever, Ashby, SmartRecruiters, Workable, Recruitee, Personio, BambooHR, Breezy HR, Rippling and Gem - 12 in total, all through their public job feeds. Missing one you need? Open an issue with an example careers URL.
Can I search all jobs on an ATS without a company list? No - ATS vendors do not publish a global job index. Start from a preset or your own target-account list; the Actor then gives you every job at those companies plus daily changes.
Can I pass a list of company names? Yes ("Acme" -> tries acme.com), but detection is best-effort; careers URLs are always reliable and never charged for detection.
Why are there fewer jobs than the careers page shows? Ashby only exposes listed jobs; Greenhouse boards may hide internal postings; filters and maxJobsPerCompany reduce the count - check SUMMARY in the key-value store for totalReported per company.
Can it run locally? Yes: apify run with the Apify CLI, or python -m src inside the venv. Unit tests: pytest.
Changelog
- 1.1.0 (2026-10-16) - 5 new ATS: Personio, BambooHR, Breezy HR, Rippling, Gem (12 total, all auto-detected from careers pages); normalized
seniorityfield and Seniority filter; Exclude title keywords filter; company presets (AI labs, fintech, remote-first, big tech); salary parsing for text salaries (Breezy) and Rippling pay ranges; cleaner Markdown (no****from nested bold). - 1.0.1 (2026-10-09) - per-company time budget (
maxSecondsPerCompany) and error isolation; named state store falls back to the run store instead of crashing; default example boards are now GitLab (Greenhouse), Palantir (Lever) and Ashby (Ashby) so the 20-job sample finishes in well under a minute; partial failures are summarised in the status message. - 1.0.0 - initial release: 7 ATS adapters, auto-detection, change detection, pay-per-event charging.