Company Jobs Scraper — 12 ATS, $0.002/job avatar

Company Jobs Scraper — 12 ATS, $0.002/job

Pricing

from $2.00 / 1,000 job results

Go to Apify Store
Company Jobs Scraper — 12 ATS, $0.002/job

Company Jobs Scraper — 12 ATS, $0.002/job

You pay only for jobs actually returned. Nothing found, nothing charged. Every open role from a company's own board across 12 ATS — Greenhouse, Workday, Ashby, Lever, SmartRecruiters, Workable, BambooHR and more. Reads the official public API each careers page already calls.

Pricing

from $2.00 / 1,000 job results

Rating

0.0

(0)

Developer

Shun Furu

Shun Furu

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

12 days ago

Last modified

Share

ATS Jobs Scraper — Greenhouse, Workday, Ashby, Lever and 8 more

Get every open job from any company's careers page. Give it a domain — stripe.com — and it works out which applicant tracking system (ATS) that company uses, reads the official public job-board API, and returns clean, normalised job data.

Live, not a database. Every run reads the company's own job board at that moment. There is no index to be out of date and no "sorry, that company isn't in our data" — if the company has a public board, you get today's jobs.

No browser. No cookies. No proxies. No blocking.

Input: ["stripe.com", "openai.com", "monzo.com"]
Output: one row per open job — title, location, department, remote flag, URL, dates

Not affiliated. This Actor is an independent tool. It is not affiliated with, endorsed by, or sponsored by Greenhouse, Workday, Ashby, Lever, SmartRecruiters, Rippling, Workable, Recruitee, BambooHR, Breezy HR, Teamtailor, Personio, or any of the companies whose job boards it reads. All product names and trademarks belong to their respective owners and are used here only to describe which public job boards this Actor can read.


Two things no other job Actor does

1. Find out the cost before you pay — countOnly

Set countOnly: true and the Actor resolves every company, counts the jobs that match your filters, and writes nothing. No rows means no charge, so the run is free. The count and the estimated cost land in the run's SUMMARY record and in the log:

Done. 2577 jobs match across 3/3 companies.
Running this for real would cost about $5.15. This run was free — nothing was written.

Use it to size a company list before committing to it. It answers the one question pay-per-event pricing normally cannot: what is this run going to cost me?

2. Pay for new jobs only — onlyNewSinceLastRun

Built for scheduled runs. The Actor remembers the job IDs it has already returned to you, and on the next run gives you only the postings that appeared since. A daily run over 50 companies stops being 50 full boards every day and becomes just the new openings.

The first run has nothing to compare against, so it returns everything; the saving starts on the second run. The memory is per-account and per-board, and lives in a named key-value store on your own account.


Why not just scrape the careers page?

Most job scrapers render a careers page in a headless browser and parse the HTML. That breaks every time a company redesigns its site, it gets blocked, and it is slow.

This Actor never touches HTML for job data. Every company on a supported ATS has a public JSON (or XML) endpoint — the very same one its own careers page calls to draw the job list. This Actor reads that endpoint directly.

HTML scrapersJob databasesThis Actor
Breaks on site redesignYesNoNo — the API contract is stable
Blocked by bot protectionOftenNoNo — the endpoints are public and unauthenticated
Needs proxiesUsuallyNoNo
Data freshnessLiveHours to a day behindLive
Company missing entirelyPossible — if it is not in their indexNot possible — it reads the company's own board
Cost per companyBrowser computeSubscriptionOne HTTP request

You do not need to know which ATS a company uses. Pass the company domain and all twelve providers are probed in parallel; whichever answers wins. If none do, the Actor reads the company's careers page and looks for the job board embedded in it.


Input

Every field is optional. Run it with the defaults and it works.

FieldTypeDefaultWhat it does
companiesarray of strings["stripe.com", "openai.com"]Company domains, careers-page URLs, or explicit provider:slug
keywordstringKeep only jobs matching any of these words (title, department, team, description)
locationstringKeep only jobs whose location contains any of these words
remoteOnlybooleanfalseKeep only jobs the ATS flags as remote
includeDescriptionbooleanfalseInclude the full job description as plain text
countOnlybooleanfalseCount matching jobs and return nothing — free
onlyNewSinceLastRunbooleanfalseReturn only jobs not seen in an earlier run
maxJobsPerCompanyinteger0 (no limit)Cap results per company
maxTotalJobsinteger0 (no limit)Cap results for the whole run — use this to cap cost

The companies field accepts three shapes

[
"stripe.com", // a domain — the ATS is detected
"https://jobs.ashbyhq.com/ramp", // a careers-page URL
"greenhouse:airbnb" // an explicit board
]

For Workday, pass the careers-site URL — https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite. A Workday tenant cannot be derived from a company domain, so this Actor asks for the URL rather than pretending to guess.

Example 1 — remote engineering jobs at a shortlist of companies

{
"companies": ["stripe.com", "openai.com", "monzo.com", "linear.app"],
"keyword": "engineer, scientist",
"remoteOnly": true,
"includeDescription": true
}

Example 2 — check the cost of a large list before paying for it

{
"companies": ["stripe.com", "cloudflare.com", "figma.com", "notion.so", "airtable.com"],
"countOnly": true
}

Returns nothing, charges nothing, and tells you what the real run would cost.

Example 3 — a daily watch that only bills you for new postings

{
"companies": ["stripe.com", "anthropic.com", "vercel.com"],
"onlyNewSinceLastRun": true,
"maxTotalJobs": 500
}

Schedule it daily. Day one returns every open job; from day two you get — and pay for — only the jobs that have appeared since.


Output

One row per job. The fields are identical across all twelve platforms, so results from different companies can be pooled with no post-processing.

FieldTypeAlways presentDescription
companystringyesThe value you passed in
companySlugstringyesThe board identifier this company uses on its ATS
atsstringyesgreenhouse | workday | ashby | lever | smartrecruiters | rippling | workable | recruitee | bamboohr | breezy | teamtailor | personio
atsLabelstringyesThe same, formatted for display
jobIdstringyesThe ATS's own identifier for the posting
titlestringyesJob title
urlstringyesPublic link to the posting
locationstringyesAs published by the company; formatting varies by ATS
remoteboolean | nullyestrue when the ATS flags it remote or the location says so; null when the ATS does not report it
departmentstringyesEmpty on platforms that do not publish it
teamstringyesEmpty on platforms that do not publish it
employmentTypestringyesFull-time, contract, etc. Not published by every ATS
salarystringyesAs published, e.g. 4500–6000 EUR/month. Empty on the platforms that do not publish it
publishedAtstring | nullyesISO date, when the ATS publishes one
updatedAtstring | nullyesISO date, when the ATS publishes one
postedLabelstringWorkday onlyWorkday's relative label, e.g. Posted Yesterday
descriptionstringwith includeDescriptionPlain-text job description
scrapedAtstringyesWhen the row was produced (ISO 8601, UTC)
{
"company": "stripe.com",
"companySlug": "stripe",
"ats": "greenhouse",
"atsLabel": "Greenhouse",
"jobId": "8130725",
"title": "Account Executive, AI Startups",
"url": "https://stripe.com/jobs/search?gh_jid=8130725",
"location": "San Francisco",
"remote": null,
"department": "Startups - Account Executives (NA)",
"team": "",
"employmentType": "",
"publishedAt": "2026-08-19T14:02:07-04:00",
"updatedAt": "2026-08-19T14:02:07-04:00",
"scrapedAt": "2026-08-22T09:48:35.371Z"
}

The run summary

Every run writes a SUMMARY record to the key-value store: which ATS each company resolved to, how many jobs matched your filters, and which companies could not be resolved, with the list of boards that were tried. Failures are never written to the dataset, so a company that cannot be read costs you nothing and still tells you why.


Supported platforms

ATSEndpoint usedDescription textDepartments
Greenhouseboards-api.greenhouse.io/v1/boards/{slug}/jobswith includeDescriptionyes
Workday{tenant}.wd{n}.myworkdayjobs.com/wday/cxs/…/jobswith includeDescription
Ashbyapi.ashbyhq.com/posting-api/job-board/{slug}yesyes
Leverapi.lever.co/v0/postings/{slug}yesyes
SmartRecruitersapi.smartrecruiters.com/v1/companies/{slug}/postingsyes
Ripplingapi.rippling.com/platform/api/ats/v1/board/{slug}/jobsyes
Workableapply.workable.com/api/v1/widget/accounts/{slug}yesyes
Recruitee{slug}.recruitee.com/api/offers/yesyes
BambooHR{slug}.bamboohr.com/careers/listyes
Breezy HR{slug}.breezy.hr/jsonyes
Teamtailor{slug}.teamtailor.com/jobs.jsonyes
Personio{slug}.jobs.personio.de/xmlyesyes

Salary is returned where the platform publishes it — currently Recruitee and Breezy HR.


Pricing

You pay for jobs, not for attempts.

Price
Starting a run$0.00001 — one US cent per 1,000 runs
Each job returned$0.002 — $2 per 1,000 jobs

What you are not charged for

  • Companies that could not be resolved — no rows are produced, so there is nothing to bill.
  • Error messages and diagnostics — they go to the log and to SUMMARY, never to the dataset.
  • countOnly runs — they write nothing at all.
  • With onlyNewSinceLastRun, jobs you have already been given.

Cap a run with maxTotalJobs, or with Apify's own per-run charge limit.


Reliability

The Actor ships with a live smoke test covering all seven platforms plus the domain-detection and careers-page-discovery paths, checked against known-good companies and failing if the number of returned jobs drops below a floor. It runs against the real APIs, so upstream changes are caught before users hit them — which is the point of building on stable public APIs rather than on HTML.

✓ greenhouse 576 jobs 601ms
✓ ashby 754 jobs 263ms
✓ lever 383 jobs 1155ms
✓ rippling 734 jobs 664ms
✓ smartrecruiters 136 jobs 605ms
✓ workday 1,182 jobs 4486ms
✓ workable 38 jobs 358ms
✓ recruitee 15 jobs 355ms
✓ bamboohr 4 jobs 1141ms
✓ breezy 3 jobs 188ms
✓ teamtailor 38 jobs 689ms
✓ domain detection 576 jobs 4014ms
✓ site discovery 1 job 8550ms

FAQ

Do I need to know which ATS a company uses? No. Pass the domain. All seven are probed in parallel and the one that answers is used. If none answer, the Actor reads the company's careers page and looks for an embedded job board. The one exception is Workday — see below.

Why does Workday need a URL when the others don't? A Workday board lives at {tenant}.wd{n}.myworkdayjobs.com, and neither the tenant nor the pod number can be derived from a company domain. Every way of deriving it was tested and none worked, so the Actor asks for the URL instead of failing silently.

What happens if a company can't be resolved? It is reported in SUMMARY with the list of boards that were tried, and it produces no rows — so it costs you nothing.

How do I keep the cost down? Three ways: run with countOnly: true first to see the price; set maxTotalJobs; or use onlyNewSinceLastRun for scheduled runs so you are billed only for new postings.

How fresh is the data? It is read at the moment you run it. There is no cache and no index.

Can it handle very large boards? Yes. Large Workday employers publish thousands of jobs — NVIDIA about 2,000, Salesforce about 1,530. The Actor reads the total up front and fetches the remaining pages in parallel, bringing a 2,000-job board down to roughly 16 seconds.

Does it collect any personal data? No. Job postings are company business information. No candidate or employee data is touched, and nothing behind a login is accessed.

Is reading these boards allowed? The Actor only calls public, unauthenticated endpoints that the companies' own careers pages call. No bot protection is bypassed, no rate limits are circumvented, and it backs off on 429.

Why is department empty for some companies? Not every ATS publishes it, and some boards leave it blank. For Greenhouse the Actor makes an extra call to fill departments in where the listing endpoint omits them.

Why is publishedAt empty on Workday? Workday's listing endpoint returns a relative label (Posted Yesterday, exposed as postedLabel) rather than a date. Enable includeDescription and the Actor reads each job's detail page, which carries a real date, the employment type and the description — at the cost of one extra request per job.

Can I get salary data? Where the platform publishes it, yes — the salary field is filled in for Recruitee and Breezy HR boards. Most other ATS platforms do not expose salary publicly, so the field is empty for them.

Can I feed it a spreadsheet of companies? Not yet — pass the list in companies. If this matters to you, open an issue on the Actor. Issues decide what gets built next.


Limitations — stated up front

  • Only companies on one of the twelve supported platforms are found. Taleo, iCIMS, Jobvite, SuccessFactors, Gem and bespoke careers pages are not covered.
  • Slug detection from a bare domain is a heuristic (acme.comacme, plus a few variants such as stripping a trailing hq). It is right for most companies; when it is not, pass provider:slug explicitly — the slug is visible in the URL of the company's careers page.
  • SmartRecruiters does not include description text in its public listing endpoint.
  • location is free text exactly as the employer wrote it. It is not normalised.