ATS Jobs Scraper — 7 Platforms + 5,000-Company Directory avatar

ATS Jobs Scraper — 7 Platforms + 5,000-Company Directory

Pricing

from $1.00 / 1,000 job items

Go to Apify Store
ATS Jobs Scraper — 7 Platforms + 5,000-Company Directory

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

My Excel Solutions

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

7 days ago

Last modified

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

PlatformPublic sourceJob identity and titleLocation / remoteEmployment / compensationPosted date
GreenhouseBoard APINative id, titleFree-text location; no reliable remote flag, so remote: nullDepartments; published pay-transparency ranges when exposedupdated_at
LeverPostings API, global and EUNative id, textCategory location, country, workplaceTypeCommitment and published salaryRangeEpoch createdAt
AshbyPosting APIStable job URL segment, titleLocation, postal city/country, isRemoteEmployment type and published salary componentpublishedAt
WorkablePublic widget APIshortcode, titleCity/state/country, telecommutingEmployment type; compensation not exposedpublished_on
SmartRecruitersCompany postings APINative id, nameStructured city/region/country and remote flagDepartment and employment labels; compensation not exposedreleasedDate
RecruiteeCareers Site Offers APINative id, titleLocation or locations and source remote flagDepartment and employment type when present; compensation not exposedpublished_at, then created_at
PersonioPublic XML syndication feedNative id, nameOffice; remote is not reliably structured, so remote: nullDepartment and employment type; compensation always nullcreatedAt

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:

PlatformValidated company boards
Greenhouse907
Lever500
Ashby1,250
Workable700
SmartRecruiters1,043
Recruitee300
Personio300
Total5,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

FieldTypeDefaultMeaning
boardsarray[]{ platform, slug } objects or supported ATS board URLs; required unless directory mode is enabled
useSeedDirectorybooleanfalseInclude the bundled 5,000-board directory
seedFilterPlatformsarrayall sevenRestrict directory mode to selected ATS platforms
keywordFilterstringCase-insensitive substring match against job title
remoteOnlybooleanfalseKeep only jobs whose source explicitly marks them remote
maxJobsPerBoardinteger0Cap returned jobs per board; zero is unlimited; in delta mode this caps new jobs after old jobs are removed
onlyNewSinceLastRunbooleanfalseEstablish a selection-specific baseline, then emit and charge only unseen jobs
deltaStateResetbooleanfalseDelete 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:

RunSource fixtureReturned jobsCharged job-item events
Baselinejobs 1, 2, 333
Next scheduled runold jobs 1, 2, 3 plus new jobs 4, 522
Unchanged runjobs 1 through 500

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

  1. Open the published Actor in Apify Console and choose Create task.
  2. Save a stable input. For a recurring feed, enable onlyNewSinceLastRun; keep filters fixed so every scheduled run uses the same selection identity.
  3. Open Schedules, create a weekly or daily cron schedule, and attach the saved Task.
  4. Set a sensible maximum run charge. Remember that the first run is a full baseline.
  5. 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

ProductChoose it whenCoverage and differentiationPublished result priceAI enrichment
ATS Jobs ScraperYou want a budget source feed, a built-in validated company directory, and delta runs7 public ATS platforms; 5,000 validated boards; deterministic normalized output$0.001/jobNo
fantastic-jobsYou need much broader ATS coverage or enriched job intelligence54-platform positioning and AI-enriched output$0.004–$0.012/jobYes

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 10000
npm 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 install
npm run check
npm run smoke:fixtures
npm 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