Company Jobs Scraper — 12 ATS, $0.002/job
Pricing
from $2.00 / 1,000 job results
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
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
12 days ago
Last modified
Categories
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 scrapers | Job databases | This Actor | |
|---|---|---|---|
| Breaks on site redesign | Yes | No | No — the API contract is stable |
| Blocked by bot protection | Often | No | No — the endpoints are public and unauthenticated |
| Needs proxies | Usually | No | No |
| Data freshness | Live | Hours to a day behind | Live |
| Company missing entirely | — | Possible — if it is not in their index | Not possible — it reads the company's own board |
| Cost per company | Browser compute | Subscription | One 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.
| Field | Type | Default | What it does |
|---|---|---|---|
companies | array of strings | ["stripe.com", "openai.com"] | Company domains, careers-page URLs, or explicit provider:slug |
keyword | string | — | Keep only jobs matching any of these words (title, department, team, description) |
location | string | — | Keep only jobs whose location contains any of these words |
remoteOnly | boolean | false | Keep only jobs the ATS flags as remote |
includeDescription | boolean | false | Include the full job description as plain text |
countOnly | boolean | false | Count matching jobs and return nothing — free |
onlyNewSinceLastRun | boolean | false | Return only jobs not seen in an earlier run |
maxJobsPerCompany | integer | 0 (no limit) | Cap results per company |
maxTotalJobs | integer | 0 (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.
| Field | Type | Always present | Description |
|---|---|---|---|
company | string | yes | The value you passed in |
companySlug | string | yes | The board identifier this company uses on its ATS |
ats | string | yes | greenhouse | workday | ashby | lever | smartrecruiters | rippling | workable | recruitee | bamboohr | breezy | teamtailor | personio |
atsLabel | string | yes | The same, formatted for display |
jobId | string | yes | The ATS's own identifier for the posting |
title | string | yes | Job title |
url | string | yes | Public link to the posting |
location | string | yes | As published by the company; formatting varies by ATS |
remote | boolean | null | yes | true when the ATS flags it remote or the location says so; null when the ATS does not report it |
department | string | yes | Empty on platforms that do not publish it |
team | string | yes | Empty on platforms that do not publish it |
employmentType | string | yes | Full-time, contract, etc. Not published by every ATS |
salary | string | yes | As published, e.g. 4500–6000 EUR/month. Empty on the platforms that do not publish it |
publishedAt | string | null | yes | ISO date, when the ATS publishes one |
updatedAt | string | null | yes | ISO date, when the ATS publishes one |
postedLabel | string | Workday only | Workday's relative label, e.g. Posted Yesterday |
description | string | with includeDescription | Plain-text job description |
scrapedAt | string | yes | When 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
| ATS | Endpoint used | Description text | Departments |
|---|---|---|---|
| Greenhouse | boards-api.greenhouse.io/v1/boards/{slug}/jobs | with includeDescription | yes |
| Workday | {tenant}.wd{n}.myworkdayjobs.com/wday/cxs/…/jobs | with includeDescription | — |
| Ashby | api.ashbyhq.com/posting-api/job-board/{slug} | yes | yes |
| Lever | api.lever.co/v0/postings/{slug} | yes | yes |
| SmartRecruiters | api.smartrecruiters.com/v1/companies/{slug}/postings | — | yes |
| Rippling | api.rippling.com/platform/api/ats/v1/board/{slug}/jobs | — | yes |
| Workable | apply.workable.com/api/v1/widget/accounts/{slug} | yes | yes |
| Recruitee | {slug}.recruitee.com/api/offers/ | yes | yes |
| BambooHR | {slug}.bamboohr.com/careers/list | — | yes |
| Breezy HR | {slug}.breezy.hr/json | — | yes |
| Teamtailor | {slug}.teamtailor.com/jobs.json | yes | — |
| Personio | {slug}.jobs.personio.de/xml | yes | yes |
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. countOnlyruns — 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.com→acme, plus a few variants such as stripping a trailinghq). It is right for most companies; when it is not, passprovider:slugexplicitly — the slug is visible in the URL of the company's careers page. - SmartRecruiters does not include description text in its public listing endpoint.
locationis free text exactly as the employer wrote it. It is not normalised.