ATS Jobs Scraper — Greenhouse + Lever + Ashby (Unified)
Pricing
from $2.00 / 1,000 results
ATS Jobs Scraper — Greenhouse + Lever + Ashby (Unified)
Scrapes public job postings from Greenhouse, Lever, and Ashby company boards and normalizes them into one consistent schema.
Pricing
from $2.00 / 1,000 results
Rating
0.0
(0)
Developer
Conor G
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
6 hours ago
Last modified
Categories
Share
ATS Jobs Scraper — Greenhouse, Lever, Ashby, Workday, SmartRecruiters, Workable, Recruitee, Personio, BambooHR
Scrape live job postings from any company that publishes its careers page on Greenhouse, Lever, Ashby, Workday, SmartRecruiters, Workable, Recruitee, Personio, or BambooHR — nine of the most common applicant tracking systems — and get them back in one consistent, normalized schema. Give it a list of company names or career page URLs; it figures out which ATS (or ATSes) each company uses and pulls every open role.
Why this actor
Most job-board scrapers on the Store are built for a single ATS. A real target list — especially outside big US tech — spans several of these platforms, and stitching together different output shapes from separate actors gets old fast. This actor:
- Covers nine platforms in one run, one schema, one dataset.
- Talks only to each platform's public, unauthenticated JSON (or XML, for Personio) API — no browser rendering, no HTML scraping, no cookies. That means it's fast, cheap to run, and far less likely to break when a company redesigns its careers page.
- Auto-detects which platform a company is on when you give it a bare name, or trusts your input outright when you give it a direct board URL.
- De-duplicates postings (by platform + job ID, so a company that happens to be found on two platforms never produces duplicate rows) and gives you a clear per-company status report (found where, how many jobs, and why a company came back empty) so you're never left guessing.
What it scrapes
| Platform | Endpoint used | Bare-slug auto-detect |
|---|---|---|
| Greenhouse | boards-api.greenhouse.io/v1/boards/{slug}/jobs | Yes |
| Lever | api.lever.co/v0/postings/{slug} | Yes |
| Ashby | api.ashbyhq.com/posting-api/job-board/{slug} | Yes |
| SmartRecruiters | api.smartrecruiters.com/v1/companies/{slug}/postings | Yes |
| Workable | apply.workable.com/api/v1/widget/accounts/{slug} | Yes |
| Recruitee | {slug}.recruitee.com/api/offers/ | Yes |
| Personio | {slug}.jobs.personio.de/xml | Yes |
| BambooHR | {slug}.bamboohr.com/careers/list | Yes |
| Workday | {tenant}.{shard}.myworkdayjobs.com/wday/cxs/{tenant}/{site}/jobs | No — paste a board URL |
These are the same public feeds each platform's own embeddable job widget uses — they're meant to be read programmatically, are not authentication-gated, and are stable across a company's own page redesigns.
Workday has no guessable bare-slug shape. A Workday board URL encodes a
tenant, a site name, and a shard (wd1, wd3, wd5, ...) that are all
independent of each other and of the company's name, so there's nothing to
probe from a bare company name. Paste the board URL instead (e.g.
https://intel.wd1.myworkdayjobs.com/External) and the actor extracts all
three parts automatically.
Deliberately not supported
Teamtailor. Its public careers-page data is served through an endpoint that requires an API key/token rather than being openly readable, unlike the platforms above. Since this actor only talks to genuinely public, unauthenticated endpoints — no accounts, no keys, no per-customer credentials — Teamtailor is out of scope rather than half-supported.
JazzHR. Its public board ({slug}.applytojob.com/apply) is
server-rendered HTML with the job list baked directly into the page markup —
there is no accompanying JSON or XML endpoint behind it at a guessable URL.
JazzHR does offer a "Listings XML feed" and a REST API, but both are opt-in
per customer and gated behind a customer-specific API key, not something
reachable from a bare company slug the way the platforms above are. Scraping
the HTML itself would mean parsing markup rather than a stable data feed —
the same reason this actor doesn't touch iCIMS — so JazzHR is out of scope.
iCIMS. Investigated and rejected. iCIMS career sites (e.g.
{company}.icims.com) don't expose a stable, guessable, unauthenticated JSON
endpoint: requests to their job-search pages come back with a 302 into a
session-bootstrapping servlet and a fresh JSESSIONID cookie, and whatever
JSON eventually loads client-side is undocumented, varies by iCIMS module
(Recruit/Onboard/Connect/Apply) and by customer, and changes between iCIMS
platform releases. That's session-dependent, effectively browser-rendered
territory, not a public API this actor can rely on — so iCIMS is out of
scope.
Input
| Field | Type | Description |
|---|---|---|
companies | array of strings | Required. Each entry is either a bare slug (e.g. "stripe") — every bare-slug-capable platform is probed automatically — or a full board URL naming one platform explicitly (e.g. "https://boards.greenhouse.io/stripe", "https://jobs.lever.co/palantir", "https://jobs.ashbyhq.com/ramp", "https://jobs.smartrecruiters.com/Visa", "https://apply.workable.com/huggingface", "https://helloprint.recruitee.com", "https://personio.jobs.personio.de", "https://flyio.bamboohr.com/careers/list", "https://intel.wd1.myworkdayjobs.com/External"). |
includeDescription | boolean | When true, each item includes the full plain-text job description (not available for SmartRecruiters or BambooHR — see limitations). Off by default to keep runs fast and cheap when you only need title/location/link. |
maxJobsPerCompany | integer | Caps how many postings are pulled per company, per platform it's found on. Default 200. |
Example input — bare slugs (auto-detect across 8 platforms)
{"companies": ["stripe", "plaid", "huggingface", "helloprint"],"includeDescription": false,"maxJobsPerCompany": 100}
Example input — mixed, including a Workday board URL
{"companies": ["stripe","https://jobs.lever.co/palantir","https://jobs.ashbyhq.com/ramp","https://jobs.smartrecruiters.com/Visa","https://intel.wd1.myworkdayjobs.com/External","https://personio.jobs.personio.de","https://flyio.bamboohr.com/careers/list"],"includeDescription": true,"maxJobsPerCompany": 100}
Output — normalized schema
Every item in the dataset — regardless of which platform it came from — has this shape:
| Field | Type | Notes |
|---|---|---|
platform | string | "greenhouse", "lever", "ashby", "workday", "smartrecruiters", "workable", "recruitee", "personio", or "bamboohr" |
companySlug | string | The slug used to query the ATS ({tenant}/{site} for Workday) |
companyName | string | null | Human-readable company name, when the platform exposes it (Greenhouse and Workable do; the others generally don't) |
jobId | string | Platform-native job/posting ID |
title | string | Job title |
department | string | null | Department/function, when available |
team | string | null | Sub-team, when available |
locations | array of strings | All listed locations for the posting |
isRemote | boolean | null | Best-effort: platform's own remote flag when present, else inferred from location text (e.g. contains "Remote") |
employmentType | string | null | e.g. "Full-time", "FullTime", when the platform reports it |
applyUrl | string | Absolute URL to the live application page |
postedAt | string | null | ISO timestamp, when the platform reports it (always null for Workday and BambooHR — see limitations) |
updatedAt | string | null | ISO timestamp, when the platform reports it |
description | string | null | Full plain-text description, only when includeDescription: true (never populated for SmartRecruiters or BambooHR) |
scrapedAt | string | ISO timestamp of when this actor fetched the item |
Sample output item
{"platform": "ashby","companySlug": "ramp","companyName": null,"jobId": "34413f8d-26bf-4bbc-8ade-eb309a0e2245","title": "Security Engineer, Cloud","department": "Engineering","team": "Backend","locations": ["New York, NY (HQ)", "Remote (Canada)", "Remote (US)", "Miami, FL"],"isRemote": true,"employmentType": "FullTime","applyUrl": "https://jobs.ashbyhq.com/ramp/34413f8d-26bf-4bbc-8ade-eb309a0e2245/application","postedAt": "2026-04-07T17:12:35.753+00:00","updatedAt": null,"description": null,"scrapedAt": "2026-07-29T07:49:48.333Z"}
Run summary
At the end of a run, a per-company status summary is written to the
default key-value store under the key COMPANY_SUMMARY — which platform(s)
each company was found on, how many jobs came back from each, and a clear
message when a company wasn't found on any platform (e.g.
Company not found on greenhouse (tried slug "plaid")Use cases
- Recruiting intel / competitive hiring analysis — track which teams and roles a set of companies is actively hiring for, and how that shifts over time, across a company list that spans more than one region's dominant ATS (Workday and SmartRecruiters skew toward enterprise/EU employers that Greenhouse/Lever/Ashby lists usually miss).
- Job aggregation — build a niche job board or newsletter by pulling live postings from a curated list of companies without maintaining scrapers for each ATS separately.
- Sales signals ("who's hiring") — open roles are a well-known proxy for growth, new initiatives, and budget — useful firmographic signal for sales and market research.
Pricing
This actor is billed pay-per-result: one dataset item = one job
posting. Enabling includeDescription does not change pricing, only the
amount of data returned per item.
Limitations (please read)
- Only the nine platforms listed above are supported. Teamtailor, JazzHR, and iCIMS are deliberately excluded (see above — none of them expose a genuinely public, unauthenticated JSON/XML endpoint at a guessable URL). LinkedIn-only postings and custom in-house career pages are also out of scope and will simply come back "not found."
- Bare-slug auto-detection relies on the slug matching across platforms. A company's slug is sometimes a different string on different platforms. If auto-detection misses a platform you know a company uses, pass the direct board URL instead.
- Workday requires a board URL, not a bare slug. See above for why.
companyNameis oftennull. Only Greenhouse and Workable reliably return a company display name; the others' public APIs generally don't expose one, so those items fall back to the slug you supplied.- Workday multi-location jobs report a placeholder, not the locations.
When a Workday posting spans more than one site, Workday's list endpoint
returns its own summary string (for example
"2 Locations") instead of the individual cities, so that's what lands inlocations. Single-location Workday jobs are exact. If you need the individual sites for those postings, say so in the Issues tab — resolving them means an extra request per job, which costs you more per run, so it isn't on by default. isRemoteis best-effort. It uses the platform's own remote flag when present, otherwise infers from location text — this can misclassify unusual location strings.- An empty result for a platform doesn't always mean "not on that platform." Several platforms (Lever, Ashby, SmartRecruiters) return an empty list both when a company isn't there and when a company is there but currently has zero open roles; this actor can't fully distinguish the two from an empty response.
postedAtis alwaysnullfor Workday. Workday's job feed only exposes a relative string ("Posted Yesterday", "Posted 30+ Days Ago"), never a real timestamp, so there's nothing reliable to normalize into ISO 8601.descriptionis never populated for SmartRecruiters or BambooHR, even withincludeDescription: true. Neither platform's list endpoint returns one, and fetching per-job detail would multiply request volume by job count.postedAtis alwaysnullfor BambooHR, for the same reason — BambooHR's list endpoint doesn't report a posting date; it's only present behind the same per-job detail call that also holds the description.- BambooHR locations can be sparse or missing. Its list endpoint
frequently reports
nullcity/state for jobs that do have a location set in the company's BambooHR account — this is a gap in what BambooHR's own public feed exposes, not something this actor can recover without the same per-job detail call mentioned above. - Data reflects a single point-in-time snapshot of each ATS's public feed at the moment the actor ran — postings can close or change immediately after.