ATS Jobs: Greenhouse, SmartRecruiters, Personio | $1.49/1k
Pricing
from $1.49 / 1,000 job postings
ATS Jobs: Greenhouse, SmartRecruiters, Personio | $1.49/1k
Open jobs from company career boards on Greenhouse, Lever, Ashby, SmartRecruiters, Workable, Recruitee, Personio and Teamtailor. Give board URLs or company names; get one normalized row per job: title, location, remote flag, salary, dates, apply link, advert text. No API keys, no login.
Pricing
from $1.49 / 1,000 job postings
Rating
0.0
(0)
Developer
Raffy
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
2 days ago
Last modified
Categories
Share
What does ATS Jobs Scraper do?
ATS Jobs Scraper reads the public job boards that companies host on eight applicant-tracking systems (ATS) - Greenhouse, Lever, Ashby, SmartRecruiters, Workable, Recruitee, Personio and Teamtailor - and returns one normalized row per open job. You give it a list of companies; it gives you every posting they have open right now, in the same columns whichever system the company uses: title, department, location, remote flag, employment type, salary range where the ATS publishes one, dates, posting and apply links, and the full advert as plain text.
It works as a Greenhouse API, Lever API and Ashby API alternative in one call: no API key, no account on any of the systems, no browser and no proxy. Each company is one plain HTTP request to the endpoint the company's own careers page uses, so a run over dozens of companies finishes in seconds and the data is first-hand - never a repost from an aggregator.
It does not log in, does not read private applicant data and does not return recruiter or hiring-manager names, e-mail addresses or phone numbers.
Use cases for this job board API
- Hiring-signal lead generation - feed in your target-account list and see which accounts are hiring, for which teams and where. Ten open "Data Engineer" roles is a buying signal for data tooling; a first "Head of Security" role is one for security vendors. Filter with
titleKeywordsand you pay only for the roles that matter to you. - Job board and newsletter aggregation - pull the openings of the companies you cover straight from the source, with stable
jobIds for de-duplication and directapplyUrls. - Recruiting and talent-market research - track which departments grow at competitors, how much of their hiring is remote, and what pay ranges they publish.
- Sales and investor research - headcount plans show up on a careers page long before they show up anywhere else.
- Automation - schedule a daily run and send new rows to Google Sheets, Airtable, Supabase or a CRM through Apify integrations, webhooks, n8n, Make or Zapier. Diff on
ats+companySlug+jobIdto get "new jobs today".
Supported applicant-tracking systems
| ATS | Example entry | Also accepted | Notes |
|---|---|---|---|
| Greenhouse | https://boards.greenhouse.io/stripe | https://job-boards.greenhouse.io/stripe, greenhouse:stripe | Pay range when the company publishes pay transparency ranges. |
| Lever | https://jobs.lever.co/palantir | lever:palantir, https://jobs.eu.lever.co/<slug>, lever-eu:<slug> | Workplace type, team and country on every posting. |
| Ashby | https://jobs.ashbyhq.com/ramp | ashby:ramp | Structured salary (min, max, currency, period) when the company shows compensation. |
| SmartRecruiters | https://jobs.smartrecruiters.com/BoschGroup | smartrecruiters:BoschGroup | Needs the company id from the board URL. The advert text takes one extra request per job. |
| Workable | https://apply.workable.com/blueground | workable:blueground | Company boards on apply.workable.com. |
| Recruitee | https://bunq.recruitee.com | recruitee:bunq | Use the <company>.recruitee.com address, not the company's own careers domain. |
| Personio | https://holidu.jobs.personio.de | personio:holidu, .jobs.personio.com | Read in English; monthly or yearly salary when published. |
| Teamtailor | https://career.teamtailor.com | teamtailor:career.teamtailor.com | Host-based: give the address of the careers site. |
You can also type just the company name (stripe, ramp). The Actor then tries the eight systems in the order of the table above until one lists jobs for that name, and records which one matched in the ats column. That costs up to 8 small requests per spelling of the name, so a board URL or ats:slug is faster and exact. When no system knows the name you get one free not_found row for that company - an entry never disappears silently.
What data can ATS Jobs Scraper extract?
Every ok row has the same columns. A value the ATS does not publish is null; nothing is guessed from the advert text.
| Field | Type | Description |
|---|---|---|
status | string | ok, not_found or error (see below) |
company | string | Company display name as the ATS publishes it; derived from the slug for Lever and Ashby, which publish none |
companySlug | string | The company's identifier on the ATS (the careers host name for Teamtailor) |
ats | string | greenhouse, lever, ashby, smartrecruiters, workable, recruitee, personio or teamtailor |
jobId | string | The posting's id on that ATS; unique together with ats and companySlug |
title | string | Job title |
department | string | Department, when published |
team | string | Team within the department (Lever, Ashby) |
location | string | Primary location as displayed on the board |
locations | array | Every location listed for the posting, primary first |
country | string | ISO code (Lever, SmartRecruiters, Recruitee, Workable, Teamtailor) or country name (Ashby); null for Greenhouse and Personio |
workplaceType | string | remote, hybrid or onsite, only when the ATS has an explicit workplace field (Lever, Ashby, SmartRecruiters, Recruitee) |
isRemote | boolean | The ATS's remote flag; for Greenhouse, Personio and Teamtailor, whether the location text says Remote |
employmentType | string | Full-time, Part-time, Contract, Temporary, Internship, Permanent, or the ATS's own wording |
salaryMin, salaryMax | number | Pay range from the ATS's structured pay fields only |
salaryCurrency | string | ISO 4217 code, e.g. USD |
salaryPeriod | string | year, month, week, day or hour, when stated |
postedAt | string | When the posting was published, ISO 8601 UTC |
updatedAt | string | When it was last updated (Greenhouse, Recruitee) |
url | string | Public posting URL |
applyUrl | string | Direct link to the application form, when published |
descriptionText | string | Full advert as plain text; null when includeDescription is off |
sourceEndpoint | string | The public ATS endpoint the row was read from |
scrapedAt | string | ISO 8601 time of extraction |
error | string | Reason, on not_found and error rows only |
SmartRecruiters rows can also carry descriptionError: the reason why the advert text could not be fetched. The rest of that row is still complete.
Result status (tri-state output)
status | Meaning | Billed? |
|---|---|---|
ok | One open job posting. | Yes |
not_found | The company has no board on the checked system(s), its board lists no open jobs, or no job matched your filters. One row per company, with the reason in error. | No |
error | The board could not be read after retries (network error, block, or a changed response format). error says why. | No |
How to scrape Greenhouse, Lever, Ashby and other ATS job boards
- Open the Actor in Apify Console and click Try for free.
- Put one company per line into Companies or job board URLs: a board URL, an
ats:slugpair, or a company name. - Optionally narrow the run with Title keywords, Location contains, Remote jobs only or Posted within (days). Filters run before a row is written, so you only pay for matches.
- Set Maximum results (the default 20 keeps a first run cheap) and, if one large employer should not use up the run, Maximum results per company.
- Click Start. Records appear in the dataset in real time; open the Output tab or Export them as JSON, CSV, Excel, XML or HTML.
To automate it, use the API tab (Node.js, Python, curl examples) or add a Schedule.
Input
See the Input tab for the full schema. The main options:
| Field | Type | Default | Description |
|---|---|---|---|
companies | array of strings | three sample boards | Board URLs, ats:slug pairs or company names, one per entry |
titleKeywords | array of strings | - | Keep a job when its title contains any of these (case-insensitive) |
locationContains | string | - | Keep a job when its location, extra locations or country contains this text |
remoteOnly | boolean | false | Keep only remote postings |
postedWithinDays | integer | - | Keep only jobs published in the last N days (falls back to the updated date) |
maxItems | integer | 20 | Stop after this many rows |
maxItemsPerCompany | integer | - (prefilled 7) | Cap per company |
includeDescription | boolean | true | Add descriptionText; turn off for lighter, faster runs |
startUrls | array of strings | the same three boards | Alias of companies for tools that always send startUrls |
proxyConfiguration | object | Apify Proxy off | Not needed; the boards answer datacenter IPs |
Example input (the prefilled default):
{"companies": ["greenhouse:duolingo", "ashby:linear", "lever:spotify"],"maxItems": 20,"maxItemsPerCompany": 7,"includeDescription": true}
A hiring-signal run over your own account list:
{"companies": ["https://boards.greenhouse.io/stripe", "https://jobs.ashbyhq.com/ramp", "palantir", "smartrecruiters:BoschGroup"],"titleKeywords": ["data engineer", "analytics", "machine learning"],"postedWithinDays": 30,"includeDescription": false,"maxItems": 500}
Output
You can download the dataset in various formats such as JSON, HTML, CSV or Excel. Real rows from a run with the default input on 2026-09-20 (descriptionText shortened here), followed by the real not_found row a run gave for a company name that no system knows:
[{"status": "ok","company": "Duolingo","companySlug": "duolingo","ats": "greenhouse","jobId": "8705196002","title": "Ad Sales Lead - West","department": "Business","team": null,"location": "Remote - California","locations": ["Remote - California"],"country": null,"workplaceType": null,"isRemote": true,"employmentType": null,"salaryMin": 152000,"salaryMax": 228000,"salaryCurrency": "USD","salaryPeriod": null,"postedAt": "2026-08-13T13:22:34.000Z","updatedAt": "2026-09-03T16:23:10.000Z","url": "https://careers.duolingo.com/jobs/8705196002?gh_jid=8705196002","applyUrl": "https://careers.duolingo.com/jobs/8705196002?gh_jid=8705196002","descriptionText": "Our mission at Duolingo is to develop the best education in the world and make it universally available. ...","sourceEndpoint": "https://boards-api.greenhouse.io/v1/boards/duolingo/jobs?content=true&pay_transparency=true","scrapedAt": "2026-09-20T19:16:13.558Z"},{"status": "ok","company": "Spotify","companySlug": "spotify","ats": "lever","jobId": "2193db3f-77c5-43b8-b030-8f92c9882bf1","title": "Android Engineer - Experience","department": "Engineering","team": "Experience","location": "London","locations": ["London", "Stockholm"],"country": "GB","workplaceType": "hybrid","isRemote": false,"employmentType": "Permanent","salaryMin": null,"salaryMax": null,"salaryCurrency": null,"salaryPeriod": null,"postedAt": "2026-06-23T11:29:45.805Z","updatedAt": null,"url": "https://jobs.lever.co/spotify/2193db3f-77c5-43b8-b030-8f92c9882bf1","applyUrl": "https://jobs.lever.co/spotify/2193db3f-77c5-43b8-b030-8f92c9882bf1/apply","descriptionText": "We design Spotify’s consumer experience—end to end, moment to moment, across every screen, platform, and partner integration. ...","sourceEndpoint": "https://api.lever.co/v0/postings/spotify?mode=json","scrapedAt": "2026-09-20T19:16:16.121Z"},{"url": "https://apify.com/glasswing/ats-jobs-scraper","status": "not_found","error": "No public job board was found for \"zzqq no such company 123\" on any of the supported systems (greenhouse, lever, ashby, smartrecruiters, workable, recruitee, personio, teamtailor). Give the board URL or \"ats:slug\" (for example greenhouse:stripe) if you know where the company posts its jobs.","scrapedAt": "2026-09-20T19:19:11.702Z","company": "zzqq no such company 123","companySlug": "zzqqnosuchcompany123","ats": null,"sourceEndpoint": "https://api.smartrecruiters.com/v1/companies/zzqqnosuchcompany123/postings?limit=1"}]
How much does it cost to scrape ATS job boards?
This Actor uses pay-per-event pricing:
| Event | Price | Charged when |
|---|---|---|
| Actor start | $0.005 per run | Once when the run starts. The platform charges one event per GB of memory and the Actor runs on 1 GB, so this is one event. |
| Job posting | $0.00149 per job | One job posting with status: ok was saved to the dataset. |
1,000 jobs cost about $1.50 ($0.005 to start plus 1,000 x $0.00149 = $1.495); 100 jobs cost about $0.15 ($0.154), and the default 20-job run about $0.03 ($0.0348). You are never charged for a row with status not_found or error, never for a job your filters excluded, and never twice for the same posting in one run. The free Apify plan's $5 of monthly credit covers about 3,350 jobs in a single run.
You can cap spending per run with Maximum results and with the run's Max total charge option; the Actor stops gracefully when either limit is reached and keeps everything it has already saved.
Integrations, exports and API access
Results export to CSV, JSON, Excel, XML and HTML from the Output tab or the dataset API. From there, Apify integrations and webhooks push the same rows into Google Sheets, Airtable or a CRM, plus Slack, Amazon S3 or your own endpoint - directly, or through Zapier, Make and n8n.
The Actor can also be called from the Apify API (Node.js, Python and curl snippets are on the API tab), from the Apify MCP server, or by any AI agent that can call a tool: inputs are plain strings, numbers and booleans, a call with no arguments runs the three sample boards, and every row carries status and error, so an agent can tell "this company is not hiring" apart from "this board could not be read" without parsing prose.
Tips
- Prefer a board URL or
ats:slugover a bare name: it is exact and saves the auto-detection requests. - Put all your companies in one run instead of many single-company runs; you pay the start fee once and the boards are read in parallel.
- Set
includeDescriptiontofalsewhen you only need the hiring signal (company, title, department, location). Greenhouse and Workable responses get much smaller, SmartRecruiters needs no per-job request, and the price per row is unchanged. - Use
maxItemsPerCompanywhen one employer in the list has thousands of openings. - Schedule a daily run with
postedWithinDays: 1for a "new jobs today" feed.
Limitations
- It needs the company list. The Actor reads the boards you name; it is not a keyword search across all companies on an ATS. Finding out which ATS a company uses is on you, or on the auto-detection, which only tries the company name as a slug.
- Auto-detection is a best effort. It matches a name only when the company's slug on the ATS is the name itself (
stripe) or its compact or hyphenated form (bosch group->boschgroup,bosch-group), and a different company can own the same slug. At most 100 bare names are auto-detected per run. When it matters, give the board URL. - Salary only where the ATS publishes structured pay. Ashby compensation, Greenhouse pay ranges, and the salary fields of Lever, Recruitee, Personio and Teamtailor are read; pay that an employer only mentions inside the advert text stays in
descriptionText. Workable and SmartRecruiters publish no structured pay. - Fields differ by system.
department,team,country,workplaceType,employmentTypeandupdatedAtarenullwhere the ATS has no such field; the field table above says which. Lever and Ashby publish no company display name, socompanyis derived from the slug there. - SmartRecruiters: about 5,000 postings per company per run at most, and the advert text costs one extra request per job.
- Personio is read in English (
language=en); a tenant that writes its adverts only in another language can come back with an emptydescriptionText. - Recruitee boards must be given as
<company>.recruitee.com; a company's own careers domain does not serve the public endpoint. - A board URL with a typo gives a
not_foundrow that carries the endpoint URL andHTTP 404rather than the company columns. - Only published, public postings are visible. If a system changes its response format, rows come back as
errorrather than silently wrong; report it in the Issues tab and the Actor is updated quickly.
FAQ
Do I need an API key for Greenhouse, Lever, Ashby or the other systems? No. The Actor reads the public, keyless endpoints that the companies' own careers pages use. All you need is an Apify account.
How do I know which ATS a company uses? Open its careers page and look at the address of a job or of the "Apply" button: boards.greenhouse.io, jobs.lever.co, jobs.ashbyhq.com, jobs.smartrecruiters.com, apply.workable.com, *.recruitee.com, *.jobs.personio.de and *.teamtailor.com are the giveaways. Paste that URL as it is. Or type the company name and let the Actor try.
Can I search all companies for a keyword? No. This Actor goes deep on the companies you list rather than wide across a whole system. titleKeywords filters the jobs of those companies.
Does it return recruiter names, e-mails or phone numbers? No. Some of these systems include the posting's creator, owner or a mailbox address in their responses; the Actor never copies those fields, and it masks e-mail addresses and phone numbers that an employer typed into the advert text.
Why is salaryMin empty for most rows? Most employers do not publish pay as a structured field. The Actor does not guess numbers out of prose, so an empty value means "not published in a structured way", never "unknown to us".
Why did I get fewer rows than maxItems? The companies had fewer open jobs, the filters excluded the rest, maxItemsPerCompany capped them, or the run hit your Max total charge limit. Check the run log for "Stopping gracefully".
What happens when a company has no open jobs or nothing matches my filters? You get one free not_found row for that company with the reason, so your pipeline can tell "not hiring" from "not checked".
Can I use this Actor from an AI agent or MCP client? Yes. Inputs are plain values, every row is self-describing (status plus error), and results can be fetched via the dataset API or the Apify MCP server.
Legal and data-protection notice
This Actor extracts only data that companies publish on their public job boards, and only business data about jobs and hiring companies: job titles, adverts, departments, locations, workplace and employment types, pay ranges, dates, company names and links.
It does not emit recruiter, hiring-manager, creator or owner names, e-mail addresses or phone numbers. Where a system's public response contains such fields the Actor does not copy them, and as an extra safeguard e-mail addresses and phone numbers that an employer typed into the advert text are replaced with [email removed] and [phone removed] before the row is saved. The Actor does not log in, does not solve captchas and does not circumvent access controls.
Job adverts can nevertheless mention individuals. Personal data is protected by the GDPR in the European Union and by other regulations around the world; you should not process personal data without a legitimate reason. If you are unsure whether your reason is legitimate, consult your lawyers. You are responsible for complying with the terms of service of the systems and companies whose boards you read, and with applicable law, when you use the extracted data.
This Actor is an independent tool. It is not affiliated with, endorsed by or sponsored by Greenhouse, Lever, Ashby, SmartRecruiters, Workable, Recruitee, Personio, Teamtailor or any company whose jobs it lists. All trademarks belong to their respective owners.