ATS Job Scraper: Greenhouse, Lever & Workday
Pricing
from $3.00 / 1,000 jobs
ATS Job Scraper: Greenhouse, Lever & Workday
Scrape company jobs from 8 ATS platforms. Track new, updated and closed vacancies, filter remote jobs, and export data. Use company domains or job board URLs.
Pricing
from $3.00 / 1,000 jobs
Rating
0.0
(0)
Developer
Tom J
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
19 hours ago
Last modified
Categories
Share
ATS Job Scraper & Company Hiring Monitor
Extract jobs from Greenhouse, Lever, Workday, Ashby, SmartRecruiters, Recruitee, Teamtailor and Personio into one structured dataset. This ATS job scraper reads the public career feeds of companies you choose, then tracks new, updated and closed vacancies across recurring runs.
Start with company domains, official job board URLs, or an existing Apify dataset. Filter by job title, country and fully remote work, then export results as JSON, CSV or Excel through Apify. No employer login or source API key is required for the standard routes.
Pricing: $3 per 1,000 delivered job rows, plus $0.002 per successfully checked company and a $0.00005 start event at the default 1 GB. In changes mode, unchanged jobs produce no job-row charge. See examples and billing details.
Automatic discovery combines website links, a small reverified catalog and a bounded browser fallback. A supported official board URL is the most predictable starting point. Some company websites cannot be recognized automatically; check the company statuses in OUTPUT.
Supported ATS job boards
| ATS | Recommended input | Common use |
|---|---|---|
| Greenhouse | https://job-boards.greenhouse.io/company | Greenhouse job listings and hiring changes |
| Lever | https://jobs.lever.co/company | Lever vacancies, teams and locations |
| Workday | Employer's myworkdayjobs.com board URL | Workday career feeds and paginated vacancies |
| Ashby | https://jobs.ashbyhq.com/company | Ashby jobs and structured salary data where published |
| SmartRecruiters | https://careers.smartrecruiters.com/Company | Public SmartRecruiters company jobs |
| Recruitee | https://company.recruitee.com | Recruitee career-site vacancies |
| Teamtailor | Official company career-site URL | Teamtailor jobs, including identifiable custom sites |
| Personio | https://company.jobs.personio.de | Personio vacancies; supported .com boards also work |
These URL patterns are illustrative; replace them with the employer's actual public board. Provider coverage does not guarantee that every company using that provider is discoverable or accessible.
Who is this job scraper for?
- Recruitment teams: maintain a target-company vacancy feed and spot newly posted roles.
- Sales research: use hiring activity as a company signal alongside your CRM data.
- Market research: compare job titles, locations and published salary fields across a chosen company list.
- Automation workflows: feed hiring changes into your own dashboards, webhooks or Apify integrations.
What you can do
- Read public Greenhouse, Lever, Ashby, SmartRecruiters, Workday, Recruitee, Teamtailor and Personio job feeds.
- Discover supported ATS links from a company website; override detection with an explicit board URL.
- Export job titles, departments, application links, source dates, locations and published structured salary data where available.
- Run a full snapshot or return only NEW, UPDATED and CLOSED jobs since the previous successful check.
- Filter by title, country, fully remote work and publication date.
- Send one optional webhook containing changes, or use Apify's run-finished integrations.
How to get company jobs in 3 steps
- Enter company domains, select an input dataset, or paste official ATS board URLs.
- Choose
snapshotfor matching jobs, orchangeswith a uniquestateNamefor a recurring watchlist. - Run the Actor. Download the job dataset and inspect the company-level statuses in OUTPUT.
The sample input intentionally caps each company at three jobs to make your first trial inexpensive. Increase maxJobsPerCompany for a fuller feed; a capped scan is explicitly marked partial.
{"domains": ["collibra.com", "deliverect.com", "attendi.nl"],"mode": "snapshot","maxJobsPerCompany": 3,"includeDescription": "text"}
Monitor company hiring changes
{"boardUrls": ["https://brenger.recruitee.com"],"mode": "changes","stateName": "my-company-watchlist","maxJobsPerCompany": 500,"includeDescription": "none"}
The first run delivers matching jobs as NEW and establishes a baseline. Later runs deliver changed rows only. Failed checks preserve the previous baseline. Incomplete feeds can report new or updated jobs but never infer closed jobs.
Read company domains from a dataset
Select an Apify dataset using the resource picker, then set domainField to its website field, for example website or company.website. The Actor scans at most 10,000 source rows and accepts at most 2,000 unique company/board inputs.
Input
| Field | Default | Meaning |
|---|---|---|
domains | — | Company domains or public career-page URLs; up to 2,000 combined inputs. |
boardUrls | — | Direct official board URLs. A custom Teamtailor site must identify its provider in public HTML. |
datasetId, domainField | —, website | Selected input dataset and dot-separated website field. |
atsOverride | [] | Objects with domain and boardUrl, or domain, ats and token. Workday needs its board URL. Custom Teamtailor domains accept ats: teamtailor and boardUrl. |
mode | snapshot | snapshot or changes. |
stateName, stateStoreId | — | Named watchlist, or a selected read/write key-value store. Required for changes mode. |
titleKeywords | [] | Include titles containing any phrase, case-insensitively. |
excludeTitleKeywords | [] | Exclude titles containing any phrase. |
countries | [] | ISO-2 country codes, such as BE, NL, DE, FR, GB or US. Unknown countries do not match. |
remoteOnly | false | Only explicitly fully remote roles; excludes hybrid and unknown. |
postedAfter | — | ISO date, inclusive. Jobs without a usable source publication date are excluded. |
includeDescription | text | none, cleaned text, or reconstructed safe paragraph html. |
maxJobsPerCompany | 500 | 1–5,000; applied before filters. Sample input uses 3. |
maxJobsTotal | 10,000 | 1–100,000 delivered rows; stops cleanly at the limit. |
maxCompanySeconds | 120 | 15–300 seconds per company. Large feeds may be partial before this deadline. |
maxRunSeconds | 1,800 | 30–7,200 seconds for company processing, followed by bounded cleanup/webhook handling. |
useCatalog, browserFallback | true, true | Reverify catalog candidates; inspect dynamic career pages when needed. |
maxBrowserCompanies | 10 | Maximum browser fallbacks per run; requires at least 1 GB. |
braveSearchApiKey, maxSearchQueries | —, 10 | Optional paid search provider using your own secret key; capped queries. |
webhookUrl | — | Optional public destination. One POST, no redirects or retries. Keep URLs with secrets out of shared input files. |
Output
Each job row includes:
company,companyDomain,ats,boardUrl,jobId,titleandapplyUrl.department,team,employmentType,workplaceTypeandlocations[]with city, region, ISO-2 country and a source label.salarywith min, max, currency and period only when structured source data supports it; otherwise null. No salary estimates or AI enrichment.postedAt,updatedAt,languageand the chosen description field, when available.changeType,changedFields,firstSeenAt,lastSeenAtand, for closed rows,closedAt.
A direct board URL does not establish the employer's corporate website: companyDomain is null in that case. A board token can serve as the company label when the source does not publish a company name. Missing dates and locations remain null rather than being invented. Language detection is heuristic.
OUTPUT includes a record for every attempted company: input, detected ATS, board URL, jobs found/matched/delivered, completeness, warnings and status. Statuses include ok, partial, no_ats_found, unreachable, blocked, failed, duplicate and stopped. A paginated source may not expose a total; then jobsFound is a lower bound from retrieved records. OUTPUT also records charged events, unprocessed companies, stop reasons, HTTP request count, connection attempts, recovery events and revision. A green run does not mean every company was read successfully.
How much does Company Jobs Feed cost?
Pay per event: the company check and delivered job rows are separate events. The current Apify pricing table is authoritative. There is no additional automatic dataset-row charge.
| Event | Price (USD) |
|---|---|
| Successfully checked company feed | $0.002 |
| Delivered job row | $0.003 |
| Standard Actor start, per allocated GB rounded up, minimum one | $0.00005 |
At the default 1,024 MB, 200 successful company checks and 3,000 delivered jobs cost $9.40005. A later changes run with those 200 companies and 250 changed rows costs $1.15005. A successful check can be charged even when it has no matching jobs or no changes. A clearly marked capped feed also counts as a successful check.
Job scraper pricing examples
At the default 1 GB memory:
| Run | Company checks | Delivered rows | Event total (USD) |
|---|---|---|---|
| Small first trial | 1 | 3 | $0.01105 |
| Company watchlist, first snapshot | 200 | 3,000 | $9.40005 |
| Later watchlist run with changes | 200 | 250 | $1.15005 |
| Later watchlist run without changes | 200 | 0 | $0.40005 |
These examples assume every listed company check succeeds. A failed or undetected company has no company-check or job event, but the start event still applies. An optional Brave Search subscription is separate and uses your own key. Changes runs need a stable stateName and complete enough feeds to infer closed jobs.
What you are not charged for
No company-check event for undetected, blocked or unreachable boards, or failed source parsing. No job event for filtered jobs or unchanged jobs in changes mode. Failed company entries appear in OUTPUT, not as paid job rows. The standard start event still applies to failed runs. There must be no automatic apify-default-dataset-item charge alongside these explicit events.
A company result is saved before charging its check. A job row is saved before its job event is charged. The Actor checks the remaining budget and stops when it cannot afford the next event. Incomplete delivery preserves that company's old watch baseline. Storage, charging and baseline updates are separate operations: after an uncertain billing/storage interruption, inspect OUTPUT before retrying. Retrying an unfinished company can repeat already delivered rows; cross-service exactly-once delivery is not promised.
Automate & schedule
Create a task with a stable stateName and schedule it at your chosen interval. Never overlap runs sharing a watchlist. Each baseline is scoped by board, company domain and filter/description settings. Changing those settings creates a separate baseline. Snapshot mode can update a baseline when you provide a state name; without one, it only exports a snapshot.
CLOSED means a previously observed job is absent from a successfully completed source feed. It does not prove that a person was hired. A cap, source error or incomplete JSON-LD scan never triggers closures. Jobs first observed during a partial scan remain tracked for later complete runs.
The optional webhook sends one JSON payload with changes, jobsDelivered, stopped and revision. It includes NEW/UPDATED/CLOSED summary rows, capped at 1,000 entries with changesCapped; use the dataset for the full result. Delivery failure is reported without retrying or discarding the dataset. Use an Apify run-finished webhook if you want to hand downstream systems the dataset ID directly.
Integrate with any app
The Apify API requires your Apify token; source websites do not require a separate API key. Optional Brave Search discovery requires your own Brave key and may incur separate search-provider charges.
Python:
from apify_client import ApifyClientclient = ApifyClient("YOUR_APIFY_TOKEN")run = client.actor("olliolli/ats-job-feed").call(run_input={"domains": ["attendi.nl"], "maxJobsPerCompany": 100})for job in client.dataset(run["defaultDatasetId"]).iterate_items():print(job["title"], job["applyUrl"])
JavaScript:
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: process.env.APIFY_TOKEN });const run = await client.actor('olliolli/ats-job-feed').call({domains: ['attendi.nl'], maxJobsPerCompany: 100});const { items } = await client.dataset(run.defaultDatasetId).listItems();console.log(items);
MCP
Once this Actor is available to your account, add it through Apify's MCP server to an MCP-compatible client. Use the same inputs and inspect both the dataset and OUTPUT. MCP does not bypass source access restrictions.
Good to know
- Discovery reads at most six public HTML pages per company and respects robots.txt. It deduplicates redirected/trailing-slash pages and prioritizes vacancy lists and details over career stories, including external career sites explicitly linked by the company, and can inspect one bounded sitemap. If needed, it tries the hostname-derived board name and at most one explicit company name from the homepage metadata on Greenhouse, Lever and Ashby (at most six board-feed probes, at most three in parallel, plus one content request for small Greenhouse boards (up to 50 jobs), or at most three individual job descriptions for larger boards per candidate name). A guessed board is accepted only when at least two distinct jobs link back to the employer domain through canonical job URLs or corporate/legal/career links, including dedicated jobs/careers subdomains. Greenhouse verification reads lightweight job metadata first; large boards need only a few individual descriptions. Name similarity alone is insufficient. OUTPUT records the discovery method, evidence URL and cache use. These checks reduce mistaken matches but cannot guarantee perfect attribution. A bounded browser fallback can inspect dynamic pages, but hidden boards and unsupported systems may still be missed; provide the official board URL or an override when known.
- Positive ATS detections are cached for seven days. A cached board returning 404/410 is evicted for the next run. A board that still responds but is outdated can require an override until cache expiry.
- Requests use a direct public connection, with HTTP feed requests paced to at most two per second per host and five in flight. Browser asset requests use Chromium scheduling and a separate 100-request cap per company. Company processing is sequential. No residential proxies, browser evasion or login sessions are used.
- Conditional ETag/Last-Modified requests are used for supported single-document feeds when a complete normalized cache exists. Raw responses are not stored. Paginated/detail feeds are fetched afresh.
- Feed completeness is based on what the public source exposes. SmartRecruiters and Workday can need an extra request per job; large companies may hit the time/job cap. Raise the limits cautiously and inspect
complete. - JSON-LD fallback reads up to five same-host job/career pages, excludes expired listings when a validThrough date is present, and is always marked incomplete. It never emits CLOSED rows.
- No CAPTCHA solving, employer-account credentials, imported cookies, applicant APIs, recruiter directory extraction, LinkedIn/Indeed scraping or unlimited IP rotation. Rate limits and access blocks stop that host for the run.
- Recruitee documents a future authentication change for its Careers Site API on 10 February 2027. This version uses the currently public endpoint. If it becomes restricted, the Actor reports blocked; it does not obtain or bypass credentials. Re-verify this adapter before that date.
- HTTP(S) only, ports 80/443, public DNS/IPs only. Redirects are revalidated and DNS is pinned to public addresses. Response sizes are bounded; an oversized response is a reported failure.
Privacy
Only whitelisted public job fields are copied. Recruiter, hiring-manager, creator and applicant fields are excluded. Descriptions remove email/phone patterns and contact-bearing paragraphs; contact sections are omitted. HTML is reconstructed as safe paragraphs, without scripts, forms, tracking images or links. Cleaning is conservative and may omit useful text; it is not a general-purpose anonymization guarantee for arbitrary source prose. Set includeDescription: none when you only need job metadata.
Raw ATS responses, cookies, headers and raw exception messages are not stored or logged. Company inputs and application URLs are part of your results; do not include private URLs or secrets. State stores contain cleaned job snapshots and public board/cache information until you delete them. The optional webhook transmits the selected change summaries to your supplied public destination.
FAQ
Can I scrape Greenhouse, Lever and Workday jobs together?
Yes. Put their official board URLs in one boardUrls list. Results share a common schema, so you can filter and export them together. Workday requires the employer's full board URL, including its site name.
Does this replace a global job search API?
It provides a company-specific job feed, not a searchable worldwide index. You choose the employers; the Actor reads their supported public sources. It does not scrape LinkedIn or Indeed.
Can I schedule daily or weekly job monitoring?
Yes. Save a task with mode: changes and a unique stateName, then create an Apify schedule for that task. Avoid overlapping runs against the same state. The first successful scan establishes the baseline; later scans return matching changes.
Can I search all jobs worldwide by keyword?
No. Supply the companies or boards you want to follow. Title keywords filter those companies' jobs; this Actor has no independent global index.
Why does my company show no_ats_found?
The configured discovery routes did not identify a supported public feed within their limits. The company may use another ATS, hide its board behind a dynamic application, block requests or require more time. Try its official board URL or atsOverride. This status does not mean the company has no jobs.
Why is a company marked partial?
A job/time limit was reached, or the source was a limited JSON-LD scan. Delivered rows remain useful, but the feed cannot establish that missing jobs closed. Increase limits when appropriate.
Does the first changes run charge for every matching job?
It delivers current matching jobs as NEW, so the job event applies once per delivered row. Subsequent unchanged jobs are not delivered or charged, although the successful company check still applies.
Can filters create false closed-job alerts?
The Actor compares the unfiltered source snapshot before deciding which change rows match your filters. A title moving outside your filter is not treated as a source closure. Changing filter settings establishes a separate baseline.
Can I get salaries, remote jobs and multiple locations?
Yes when the source supplies usable fields. Unknown salary/country/date fields remain null. Fully remote is distinct from hybrid. The Actor does not estimate salaries or infer remote status from every mention in a description.
Can I use my CRM or a Google Maps dataset?
Yes. Select its dataset and website field. Only company websites are read; the Actor does not need or copy CRM contact people.
Disclaimer
Independent tool, not affiliated with Greenhouse, Lever, Ashby, SmartRecruiters, Workday, Recruitee, Teamtailor, Personio or employers. A screening/research aid, not a certified account of hiring activity. Respect source terms and your rights to process the content.
Need help?
Open an Apify issue with the run ID, public board URL, relevant settings and safe OUTPUT reason. Do not share tokens, private datasets or personal information.
Automatic ATS recognition: catalog, browser and optional search
The Actor first tries a small bundled catalog of previously verified public boards. Each catalog candidate must again pass the two-job employer-domain check; entries expire after 30 days. This is a limited convenience directory, not a comprehensive company database. Set useCatalog: false to measure discovery without this assistance. Cached successful mappings last seven days.
If normal website discovery fails, browserFallback: true opens at most two public career pages in a fresh anonymous browser context. It can inspect rendered links and up to eight first-party JSON responses. The browser accepts explicit supported ATS URLs found on an official career page or in its first-party JSON responses. The career page must have been discovered through the company website or a same-domain career path. This uses the same first-party link evidence as ordinary HTML discovery and supports all eight ATS providers. Guessed catalog/search candidates still require two-job verification. No forms are submitted. Images, fonts, media, WebSockets and service workers are blocked. Access blocks and robots exclusions are respected.
The browser has a 25-second limit and 100-request cap per company, with at most maxBrowserCompanies (default 10) fallbacks per run. Use at least 1,024 MB. Low memory or insufficient remaining company time skips the fallback. Company discovery has a maximum 65-second budget plus a bounded catalog check, within maxCompanySeconds; feed retrieval also needs time. The default 120 seconds is recommended.
An optional braveSearchApiKey enables one search request per unresolved company, up to maxSearchQueries (default 10) per run. The company domain is sent to Brave Search. Search ranking and snippets are never accepted as proof: supported board candidates need two distinct employer-domain links. Search errors disable further queries for the run. The key is secret input, is not forwarded across redirects, and is never written to OUTPUT. No key or paid search subscription is bundled.
OUTPUT company records include discoveryDiagnostics for attempted catalog, search and browser routes. discoveryMethod distinguishes verified-catalog, verified-search, company-browser-link and normal website discovery. A catalog-assisted success rate must not be described as discovery accuracy on unseen companies. None of these routes guarantees universal recognition; an official board URL remains the most predictable input.