Job Postings Scraper — Greenhouse, Lever, Ashby & Workday
Pricing
from $0.95 / 1,000 job posting returneds
Job Postings Scraper — Greenhouse, Lever, Ashby & Workday
Scrape open roles from companies' own job boards across six applicant tracking systems: Greenhouse, Lever, Ashby, SmartRecruiters, Workable and Personio. One normalised row per role — title, department, location, apply URL and full description. Auto-detects which system each company uses.
Pricing
from $0.95 / 1,000 job posting returneds
Rating
0.0
(0)
Developer
Yaniv van der Stigchel
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
18 hours ago
Last modified
Categories
Share
Job Postings Scraper — with the apply link aggregators lost
Open roles from the applicant tracking system each company actually runs, not from an aggregator's copy. Pass a list of companies; get one normalised row per role across six different systems.
Every row carries a working applyUrl. LinkedIn removed the external apply
URL from what aggregators can see, permanently — the largest LinkedIn jobs Actor
on this Store told a user in a public review reply that they are removing the
field entirely because it is "only possible post-login". Company job boards
never had that problem: the apply link is native there, always present, always
correct.
A job posting you cannot apply to is a row, not a lead.
What it does
- Scrape job postings from Greenhouse, Lever, Ashby, SmartRecruiters, Workable and Personio
- Get a working apply link on every role
- Get every open role at a list of companies in one dataset
- Track hiring activity as a buying signal
- Monitor competitor headcount growth on a schedule
- Export job postings to JSON or CSV
Three things aggregators cannot give you
The apply URL. Covered above. It is native on a company's own board.
Deduplication. One row per role, keyed on the board's own job id, so the same role does not arrive three times across three sources — and you are not charged three times for it.
The company name, on every row. Obvious, and routinely missing from aggregator output.
Why this is not one more job scraper
Six systems publish the same concept — an open role — in six different shapes,
at six different URLs, using different names for the same field. Greenhouse
nests locations under offices; Lever puts everything in categories; Ashby
alone states whether a role is remote; Personio returns XML. A buyer wanting
every open role at 500 companies has to reconcile all of it.
Auto-detection is the point. You will not know which system each company on your list uses, and finding out by hand is the work you are paying to avoid. Pass a bare slug and each board is tried in turn until one answers.
Every row carries atsPlatform, so you can see where it came from — and it is
useful in itself, because knowing a company runs Greenhouse tells you how to
apply programmatically.
You only pay for roles
errorCode | Meaning | Charged |
|---|---|---|
— (success: true) | A role | Yes |
no-open-roles | Board found and empty — not hiring publicly | No |
no-board-found | No public board on any supported system | No |
unparseable-company | Input was not a slug or careers URL | No |
Those two failures are different answers and the distinction matters. No roles means the company was located and is not hiring. No board means we could not find where they publish — they may use an unsupported system, or host roles on their own site.
Input
| Field | Required | Description |
|---|---|---|
companies | yes | Slugs (stripe) or careers URLs (https://jobs.lever.co/spotify) |
platform | no | Default auto. Set it only if every company uses the same system |
maxJobsPerCompany | no | Default 1000. Your cost ceiling |
maxConcurrency | no | 1–15, default 5 |
{"companies": ["stripe", "https://jobs.lever.co/spotify", "https://jobs.ashbyhq.com/ramp"],"platform": "auto","maxJobsPerCompany": 500}
A careers URL skips detection, so it is both faster and cheaper than a bare slug when you already know the board.
Output
Every row has the same fields whether it succeeded or failed, so you can select columns without branching.
| Field | Type | Description |
|---|---|---|
success | boolean | True when the row carries a role |
company | string | Company slug |
atsPlatform | string | Which system the role came from |
jobId | string | Board's id — stable, deduplicates across runs |
title | string | Role title |
jobUrl | string | Canonical page |
applyUrl | string | Where an application is submitted |
department | string | As the board states it |
team | string | Where published |
location | string | Primary location |
allLocations | array | Every location, where the board says so |
isRemote | boolean | Only when stated; null when not |
employmentType | string | Full-time, contract, internship |
publishedAt | string | ISO 8601 |
updatedAt | string | ISO 8601, where published |
descriptionText | string | Full description as plain text |
errorCode | string | Null on success |
errorMessage | string | Human-readable explanation |
{"success": true,"company": "ramp","atsPlatform": "ashby","jobId": "34413f8d-26bf-4bbc-8ade-eb309a0e2245","title": "Security Engineer, Cloud","jobUrl": "https://jobs.ashbyhq.com/ramp/34413f8d","applyUrl": "https://jobs.ashbyhq.com/ramp/34413f8d/application","department": "Engineering","team": "Backend","location": "New York, NY (HQ)","allLocations": ["New York, NY (HQ)", "Remote (US)"],"isRemote": false,"employmentType": "FullTime","publishedAt": "2026-08-14T09:12:00Z","updatedAt": null,"descriptionText": "About the role\n\nYou will secure our cloud estate...","errorCode": null,"errorMessage": null}
Use it from an AI agent (MCP)
This Actor is callable as a tool through the Apify MCP server, so Claude, ChatGPT, Cursor and VS Code can run it directly.
Add the server to your MCP client:
{"mcpServers": {"apify": {"url": "https://mcp.apify.com","headers": {"Authorization": "Bearer <YOUR_APIFY_TOKEN>"}}}}
Then ask for what you want in plain language — for example “list every open role at this company” — and the agent calls cleanfeed/ats-job-postings-scraper with the right input. Every output field is described in the dataset schema, so the agent knows what it is getting back before it runs anything.
Call it from code
Python
from apify_client import ApifyClientclient = ApifyClient("<YOUR_APIFY_TOKEN>")run = client.actor("cleanfeed/ats-job-postings-scraper").call(run_input={"companies": ["stripe"],})for item in client.dataset(run["defaultDatasetId"]).iterate_items():if item["success"]:print(item)
JavaScript
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });const run = await client.actor('cleanfeed/ats-job-postings-scraper').call({companies: ["stripe"],});const { items } = await client.dataset(run.defaultDatasetId).listItems();console.log(items.filter((i) => i.success));
cURL
curl -X POST "https://api.apify.com/v2/acts/cleanfeed~ats-job-postings-scraper/run-sync-get-dataset-items?token=<YOUR_APIFY_TOKEN>" \-H 'Content-Type: application/json' \-d '{"companies": ["stripe"]}'
Related actors
| If you need | Use |
|---|---|
| Contact details for those same companies | Verified Website Contacts |
| What those companies say on video | YouTube Channel Transcript Scraper |
Limitations
isRemoteis null unless the board states it. Only Ashby, Lever and Workable publish it. Inferring it from the word "remote" in a location gives confident wrong answers on "Remote-friendly office, Berlin", so the gap is reported instead.- Not every company is on a supported board. Workday and Taleo are widely used in large enterprises and are not covered; many companies host roles on their own site. Those return
errorCode: no-board-found. - Slugs differ per system. A company can be
stripeon Greenhouse andstripe-incon Lever. Pass the careers URL when auto-detection misses. - Salary is not included. Most of these boards do not publish it, and the few that do use inconsistent formats. A field that is null most of the time and wrong some of the rest is worse than absent.
- SmartRecruiters' list endpoint carries no description, so
descriptionTextis null for those rows. - Unlisted Ashby roles are excluded —
isListed: falsemeans the company chose not to publish them.
FAQ
Which applicant tracking systems are supported?
Greenhouse, Lever, Ashby, SmartRecruiters, Workable and Personio. Pass a bare company slug and each is tried in turn until one answers.
How do I find a company's slug?
Open their careers page — the slug is in the URL, so boards.greenhouse.io/stripe gives stripe. Pasting the whole URL also works and skips detection, which is faster.
What if a company has no open roles?
You get one row with errorCode: no-open-roles and are not charged. That is a real finding: a company that has stopped hiring is a signal in itself, and it is a different answer from no-board-found.
Can I get roles from LinkedIn or Indeed?
No, and deliberately. Those are aggregators with their own terms. This reads the boards companies publish themselves, which is also fresher — a role appears here before it propagates to an aggregator.
Why is salary missing?
Most of these boards do not publish it in their API, and the few that do use inconsistent formats. A field that is null most of the time and wrong some of the rest is worse than no field at all.
Does it need a proxy?
No. These are public job boards on ordinary infrastructure.
Notes
Only public job boards that companies publish themselves are read. No aggregators, no login, no scraping of a careers page behind an application flow. A company with no public board is reported as such rather than guessed at.