Job Postings Scraper — Greenhouse, Lever, Ashby & Workday avatar

Job Postings Scraper — Greenhouse, Lever, Ashby & Workday

Pricing

from $0.95 / 1,000 job posting returneds

Go to Apify Store
Job Postings Scraper — Greenhouse, Lever, Ashby & Workday

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

Yaniv van der Stigchel

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

18 hours ago

Last modified

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

errorCodeMeaningCharged
— (success: true)A roleYes
no-open-rolesBoard found and empty — not hiring publiclyNo
no-board-foundNo public board on any supported systemNo
unparseable-companyInput was not a slug or careers URLNo

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

FieldRequiredDescription
companiesyesSlugs (stripe) or careers URLs (https://jobs.lever.co/spotify)
platformnoDefault auto. Set it only if every company uses the same system
maxJobsPerCompanynoDefault 1000. Your cost ceiling
maxConcurrencyno1–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.

FieldTypeDescription
successbooleanTrue when the row carries a role
companystringCompany slug
atsPlatformstringWhich system the role came from
jobIdstringBoard's id — stable, deduplicates across runs
titlestringRole title
jobUrlstringCanonical page
applyUrlstringWhere an application is submitted
departmentstringAs the board states it
teamstringWhere published
locationstringPrimary location
allLocationsarrayEvery location, where the board says so
isRemotebooleanOnly when stated; null when not
employmentTypestringFull-time, contract, internship
publishedAtstringISO 8601
updatedAtstringISO 8601, where published
descriptionTextstringFull description as plain text
errorCodestringNull on success
errorMessagestringHuman-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 ApifyClient
client = 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"]}'
If you needUse
Contact details for those same companiesVerified Website Contacts
What those companies say on videoYouTube Channel Transcript Scraper

Limitations

  • isRemote is 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 stripe on Greenhouse and stripe-inc on 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 descriptionText is null for those rows.
  • Unlisted Ashby roles are excluded — isListed: false means 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.