ATS Jobs Scraper — 7 Platforms + 5,000-Company Directory
Pricing
from $1.00 / 1,000 job items
ATS Jobs Scraper — 7 Platforms + 5,000-Company Directory
5,000 validated company boards, seven public ATS platforms, normalized jobs, and only-new-jobs delta mode for $0.001 per job.
Pricing
from $1.00 / 1,000 job items
Rating
0.0
(0)
Developer
My Excel Solutions
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
7 days ago
Last modified
Categories
Share
5,000 validated company job boards built in — run it against the whole directory, filter by platform or keyword, or bring your own slugs. Seven ATS platforms, one normalized schema, $0.001 per job, and an only-new-jobs delta mode.
ATS Jobs Scraper is a directory-first Apify Actor for collecting public job postings from Greenhouse, Lever, Ashby, Workable, SmartRecruiters, Recruitee, and Personio. It is designed for job aggregators, career-search tools, recruiting research, internal talent intelligence, and scheduled data exports that need reliable source records at a predictable price.
The built-in directory is the main difference. You do not need to arrive with thousands of company slugs or maintain a separate board-discovery database. You can run all 5,000 validated boards, select one or more ATS platforms, apply a title keyword, request explicitly remote roles, or combine directory mode with your own board URLs. For a narrow workflow, pass one company. For a market-wide feed, schedule the directory with delta mode and pay only when new postings appear.
There is no AI enrichment, email discovery, company enrichment, or inferred salary layer. The Actor reads intentionally public career-site endpoints, validates each platform's response tolerantly, and maps source fields into one deterministic contract. Missing data remains null.
What you get
- A committed directory of exactly 5,000 company job boards, discovered through Common Crawl and validated against live public ATS endpoints.
- Seven ATS integrations behind a stable output schema.
- Direct input as
{ platform, slug }objects or pasted career-board URLs. - Case-insensitive title filtering and an explicit-remote filter applied before billing.
- Delta mode that establishes a baseline and then emits and charges only unseen jobs.
- Within-run deduplication on
(platform, companySlug, jobId). - Polite fetching: no more than five boards in parallel, a static identifying User-Agent, bounded retry/backoff, and non-fatal invalid-slug handling.
- Pay-per-event pricing of $0.001 per returned unique job, plus Apify's $0.0005 Actor-start event.
Supported platforms and normalized fields
| Platform | Public source | Job identity and title | Location / remote | Employment / compensation | Posted date |
|---|---|---|---|---|---|
| Greenhouse | Board API | Native id, title | Free-text location; no reliable remote flag, so remote: null | Departments; published pay-transparency ranges when exposed | updated_at |
| Lever | Postings API, global and EU | Native id, text | Category location, country, workplaceType | Commitment and published salaryRange | Epoch createdAt |
| Ashby | Posting API | Stable job URL segment, title | Location, postal city/country, isRemote | Employment type and published salary component | publishedAt |
| Workable | Public widget API | shortcode, title | City/state/country, telecommuting | Employment type; compensation not exposed | published_on |
| SmartRecruiters | Company postings API | Native id, name | Structured city/region/country and remote flag | Department and employment labels; compensation not exposed | releasedDate |
| Recruitee | Careers Site Offers API | Native id, title | Location or locations and source remote flag | Department and employment type when present; compensation not exposed | published_at, then created_at |
| Personio | Public XML syndication feed | Native id, name | Office; remote is not reliably structured, so remote: null | Department and employment type; compensation always null | createdAt |
Null-over-guessing is a contract, not a limitation to hide. The Actor does not infer remote work from “work from home” in a title, split a free-text location into a guessed city, treat a bonus as base salary, or manufacture an employment type. If a platform does not expose a dependable field, the normalized field is null.
Quick start: bring your own boards
Pass board objects, supported URLs, or both. URL auto-detection extracts the correct company identifier.
{"boards": [{ "platform": "greenhouse", "slug": "openai" },"https://jobs.lever.co/netflix","https://jobs.ashbyhq.com/linear","https://jobs.recruitee.com/o/account-manager","https://personio.jobs.personio.de/job/1834171"],"keywordFilter": "engineer","remoteOnly": true,"maxJobsPerBoard": 100}
Direct boards and seed-directory boards are deduplicated before fetching. A malformed URL is rejected at input validation. A syntactically valid but nonexistent company identifier produces an invalid_slug board outcome and does not crash the rest of the run.
Quick start: use the 5,000-board directory
Run the directory without maintaining a slug list:
{"useSeedDirectory": true,"seedFilterPlatforms": ["greenhouse", "lever", "recruitee", "personio"],"keywordFilter": "data","remoteOnly": false,"onlyNewSinceLastRun": true}
The committed seven-platform distribution is:
| Platform | Validated company boards |
|---|---|
| Greenhouse | 907 |
| Lever | 500 |
| Ashby | 1,250 |
| Workable | 700 |
| SmartRecruiters | 1,043 |
| Recruitee | 300 |
| Personio | 300 |
| Total | 5,000 |
Directory entries are not assumed valid merely because a URL appeared in a web index. The build process extracts candidate hostnames or path slugs, requests each platform's public endpoint at a maximum of two request starts per second, checks the expected JSON or XML shape, and only commits successful boards. A resumable local progress cache prevents unnecessary repeat probes.
Use seedFilterPlatforms to control both runtime and selection identity. Running only Personio and Recruitee is a different delta baseline from running all seven platforms.
Input reference
| Field | Type | Default | Meaning |
|---|---|---|---|
boards | array | [] | { platform, slug } objects or supported ATS board URLs; required unless directory mode is enabled |
useSeedDirectory | boolean | false | Include the bundled 5,000-board directory |
seedFilterPlatforms | array | all seven | Restrict directory mode to selected ATS platforms |
keywordFilter | string | — | Case-insensitive substring match against job title |
remoteOnly | boolean | false | Keep only jobs whose source explicitly marks them remote |
maxJobsPerBoard | integer | 0 | Cap returned jobs per board; zero is unlimited; in delta mode this caps new jobs after old jobs are removed |
onlyNewSinceLastRun | boolean | false | Establish a selection-specific baseline, then emit and charge only unseen jobs |
deltaStateReset | boolean | false | Delete saved state for the current selection and make the next delta result a fresh baseline |
Stable output schema
One dataset item is returned per unique (platform, companySlug, jobId). This example is generated byte-for-byte from the recorded Workable fixture used by the normalization tests:
{"platform": "workable","companySlug": "acme","jobId": "WK101","title": "Data Analyst","department": "Analytics","location": {"raw": "Boston, Massachusetts, United States","city": "Boston","country": "United States"},"remote": true,"employmentType": "full_time","compensation": null,"postedAt": "2026-07-02T00:00:00.000Z","applyUrl": "https://apply.workable.com/acme/j/WK101/apply/","sourceUrl": "https://apply.workable.com/acme/j/WK101/","fetchedAt": "2026-07-21T12:00:00.000Z"}
Filters, normalization, and deduplication run before publication. Only published records reach Actor.pushData(job, 'job-item'), so a filtered or duplicate record is not billed.
Only-new-jobs delta mode
Set onlyNewSinceLastRun: true for recurring collection. The first successful run for a selection is its baseline: every matching unique job is returned and charged, then the job identities are saved. Later runs remove saved identities before the publisher, so only new records are written and charged.
Selection state is isolated in the caller's named Apify key-value store, ats-jobs-delta-state. Its key is a SHA-256 hash of the canonical board selection, directory platform filters, title keyword, and remote-only setting. Board order and platform-filter order do not matter. Changing a board, keyword, remote setting, directory mode, or directory platform set intentionally creates a new baseline. The run log says when this happens.
The Phase 1.5 fixture-scale verification cycle is concrete:
| Run | Source fixture | Returned jobs | Charged job-item events |
|---|---|---|---|
| Baseline | jobs 1, 2, 3 | 3 | 3 |
| Next scheduled run | old jobs 1, 2, 3 plus new jobs 4, 5 | 2 | 2 |
| Unchanged run | jobs 1 through 5 | 0 | 0 |
For a practical cost example, suppose a weekly scheduled run covers 1,000 boards. The baseline returns 20,000 matching jobs and costs $20 in job-item events. If the following week has 650 new matching postings, that run returns 650 items and costs $0.65, rather than charging again for the 20,000 old postings. The Actor-start event still applies to every run.
State is saved only after publication. If the process crashes after pushing records but before its final state save, those records can be returned and charged again on the next run. This is the deliberate safe failure direction: duplicate delivery after a rare interrupted save is preferable to permanently losing new jobs. Downstream consumers that require exactly-once processing should also key on (platform, companySlug, jobId).
The saved seen-set is capped at the most recent 500,000 job hashes. If a selection exceeds that bound, the Actor logs a warning and drops the oldest hashes. Very old jobs could then reappear if an ATS republishes or retains them indefinitely. Use narrower platform or keyword selections if this matters. Set deltaStateReset: true together with delta mode when you intentionally want to repay for and rebuild a baseline.
Pricing, free evaluation, and graceful limits
The primary pay-per-event event is job-item at $0.001 per returned unique job, equivalent to $1 per 1,000 job records. The synthetic apify-actor-start event is $0.0005. Filtering and delta removal happen before charging.
Apify Free-plan users can evaluate up to 3 boards and 50 jobs per run. These limits compose with delta mode: old jobs are removed first, and up to 50 genuinely new records are published. A PPE budget limit also stops publication gracefully. The Actor checks eventChargeLimitReached, records an accurate status message, and exits successfully instead of relying on the SDK to stop it.
Integrate through MCP
When the Apify MCP server is connected to an AI client, call its Actor-running tool with the Actor ID and ordinary input. Tool names vary slightly by MCP client; the payload is equivalent to:
{"tool": "call-actor","arguments": {"actorId": "ats-jobs-scraper-greenhouse-lever-ashby","input": {"boards": ["https://jobs.lever.co/netflix"],"keywordFilter": "engineer","onlyNewSinceLastRun": true}}}
Read the completed run's default dataset for normalized jobs. The same PPE price, delta state, free limits, and run budget apply whether a run begins through MCP, Apify Console, the REST API, or a schedule.
Schedule with an Apify Task
- Open the published Actor in Apify Console and choose Create task.
- Save a stable input. For a recurring feed, enable
onlyNewSinceLastRun; keep filters fixed so every scheduled run uses the same selection identity. - Open Schedules, create a weekly or daily cron schedule, and attach the saved Task.
- Set a sensible maximum run charge. Remember that the first run is a full baseline.
- Use the Task's dataset or a webhook to hand the new records to your database, automation, or notification layer.
If you edit a Task's board list or filters, expect one new baseline. If you only change schedule timing, the delta identity is unchanged.
n8n workflow
There are two simple patterns. For polling, use a Schedule Trigger, then an HTTP Request node that starts the saved Apify Task through the Apify API. Wait for completion, request the run's default dataset items, and feed each item into your database, spreadsheet, Slack, or email nodes. Store the Apify token in n8n credentials, not in workflow text.
For push delivery, configure an Apify Actor-run-succeeded webhook aimed at an n8n Webhook node. The webhook payload identifies the completed run. Use an authenticated HTTP Request node to fetch defaultDatasetId/items, then branch on fields such as platform, remote, or department. Delta mode keeps this workflow quiet: an unchanged run produces no job items.
Make workflow
Create a scheduled scenario with HTTP → Make a request to start the saved Apify Task. Add a sleep/poll loop or use an Apify completion webhook to resume the scenario. Fetch the completed default dataset, iterate the returned array, and map normalized fields into Airtable, Google Sheets, a CRM, or a notification module. Keep the scheduling input stable and enable delta mode so Make operations are spent on new postings instead of processing the same historical board repeatedly.
Neither n8n nor Make needs platform-specific parsing. Every route consumes the same normalized schema.
Honest product comparison
| Product | Choose it when | Coverage and differentiation | Published result price | AI enrichment |
|---|---|---|---|---|
| ATS Jobs Scraper | You want a budget source feed, a built-in validated company directory, and delta runs | 7 public ATS platforms; 5,000 validated boards; deterministic normalized output | $0.001/job | No |
| fantastic-jobs | You need much broader ATS coverage or enriched job intelligence | 54-platform positioning and AI-enriched output | $0.004–$0.012/job | Yes |
This Actor is not a substitute for fantastic-jobs when enrichment or broad long-tail ATS coverage is the requirement. Conversely, users who only need normalized public source records should not have model costs baked into every item.
Zero AI by design
Zero AI is a reliability and pricing choice. Normalization is deterministic, the same input shape maps to the same fields, and no model can hallucinate a salary, seniority, department, or location that the ATS did not publish. There are no token costs hidden inside the $0.001 event price and no model outage in the fetch path. Source records can still contain employer-written mistakes, but the Actor does not amplify them through inference.
Reliability and source etiquette
The Actor uses the static User-Agent ats-jobs-actor/1.0, processes no more than five boards concurrently, times requests out, and retries only network errors, HTTP 429, and HTTP 5xx with bounded backoff. A 404 is treated as invalid board data. One malformed job is skipped with a warning while valid siblings continue. Personio XML and the six JSON APIs are parsed through platform-specific tolerant schemas.
The endpoints are public because they power employer career sites and syndication feeds. The Actor does not bypass authentication, scrape applicant data, or access private recruiting systems.
Rebuild the directory
The committed directory is ready to use. Maintainers can refresh it with:
npm run seed:build
Target one platform while preserving all enforced minimums:
npm run seed:build -- --platform recruitee --min-platform 300 --collection-count 100 --candidate-cap 10000npm run seed:build -- --platform personio --min-platform 300 --collection-count 100 --candidate-cap 10000
The builder exits nonzero when it cannot meet the relevant minimum. Current enforced minimums are Greenhouse 900, Lever 500, Ashby 900, Workable 700, SmartRecruiters 900, Recruitee 300, and Personio 300. Fixed-size rebalancing reserves those entries before filling the remaining directory slots.
Local development
Node.js 22 is required.
npm installnpm run checknpm run smoke:fixturesnpm run build
Run locally with the Apify CLI:
apify run -i '{"boards":[{"platform":"greenhouse","slug":"openai"}]}'
Set ACTOR_TEST_PAY_PER_EVENT=true for local PPE testing. After authenticating, use apify push to deploy.
Relevant API searches
Greenhouse job board API · Lever postings API · Ashby job board API · Workable jobs API · SmartRecruiters postings API · Recruitee offers API · Personio XML jobs feed · ATS job scraper · scrape Greenhouse jobs · normalized job postings API · only new jobs scraper