ATS Jobs Scraper — Greenhouse, Lever, Ashby, Workday & more avatar

ATS Jobs Scraper — Greenhouse, Lever, Ashby, Workday & more

Pricing

from $1.30 / 1,000 jobs

Go to Apify Store
ATS Jobs Scraper — Greenhouse, Lever, Ashby, Workday & more

ATS Jobs Scraper — Greenhouse, Lever, Ashby, Workday & more

[💵 $2.00 / 1K] Scrape open jobs across 11 ATS platforms — Greenhouse, Lever, Ashby, Workday & more — in one unified schema. Auto-detects the ATS from a plain company domain. Monitoring mode returns only new jobs since your last run. For n8n, Make, Zapier & MCP. Official public APIs only.

Pricing

from $1.30 / 1,000 jobs

Rating

0.0

(0)

Developer

Dataloft Studio

Dataloft Studio

Maintained by Community

Actor stats

0

Bookmarked

3

Total users

2

Monthly active users

5 days ago

Last modified

Share

ATS jobs scraper for Greenhouse, Lever, Ashby, Workday, SmartRecruiters, Workable, Recruitee, Personio, Teamtailor, BambooHR and Rippling — every posting pulled through the platform's own public API, normalised into one schema. Give it a board URL, or just a company website and it finds the board for you. Works with n8n, Make, Zapier, the Apify API and as an MCP tool for AI agents.

What is the ATS Jobs Scraper?

Most job data on the internet is second-hand: aggregated, re-listed, days stale. This scraper goes to the source. Every posting comes from the applicant tracking system the company actually publishes to, which means you get the role when the source API exposes it, with the real requisition ID and the real apply link.

It reads 11 ATS platforms through their official public endpoints. No login, no cookies, no browser automation, no proxies needed, no personal data — job postings only.

What data can you extract from Greenhouse, Lever, Ashby and Workday?

Every posting is normalised into one flat schema, so a Greenhouse role and a Workday role look identical to your code.

Output fields

FieldNotes
title, company, companySlug
atsPlatformwhich system it came from
department, team, employmentType, senioritywhen the platform exposes them
location, locations[], country, isRemoteisRemote is inferred when the ATS has no explicit flag
compensation{min, max, currency, interval} — structured where published, parsed from text otherwise
descriptionHtml, descriptionTextfull posting, both formats
applyUrl, jobUrl, companyCareerUrl
postedAt, updatedAt, scrapedAtISO 8601
source, sourceUrlexact endpoint the row came from

Example output JSON

{
"title": "Senior / Staff Fullstack Engineer",
"company": "Linear",
"atsPlatform": "ashby",
"department": "Product",
"team": "Engineering",
"employmentType": "FullTime",
"location": "Europe",
"isRemote": true,
"compensation": null,
"descriptionText": "At Linear, we're building the product development system for teams…",
"applyUrl": "https://jobs.ashbyhq.com/linear/d3bc1ced-…/application",
"postedAt": "2026-04-27T20:13:45.158Z",
"scrapedAt": "2026-08-25T19:14:15.176Z"
}

Which ATS platforms are supported?

PlatformBoard looks likeNotes
Greenhousejob-boards.greenhouse.io/acmefull description included
Leverjobs.lever.co/acmestructured salary when published
Ashbyjobs.ashbyhq.com/acmestructured compensation tiers
Workdayacme.wd5.myworkdayjobs.com/SiteNamesite ID optional — it is probed for you
SmartRecruiterscareers.smartrecruiters.com/Acmecompany ID is case-sensitive
Workableapply.workable.com/acme
Recruiteeacme.recruitee.com
Personioacme.jobs.personio.de.de and .com both handled
Teamtailoracme.teamtailor.comRSS feed, capped at 100 by the platform
BambooHRacme.bamboohr.com/careers
Ripplingats.rippling.com/acme/jobs

A valid board with no open roles returns zero rows rather than an error — that is the platform's answer, not a failure.

Why JazzHR is not supported

JazzHR's feed endpoint returns HTTP 200 with a "page not found" HTML body, and its sibling feed paths answer 410 Gone. A scraper that trusts the status code turns that into rows of nothing. It was tested across 180 tenants and removed rather than shipped as a silent data leak. If you need JazzHR, it has to be HTML parsing with real maintenance — open an issue and say so.

How to scrape jobs from a company domain (no board URL needed)

The part people usually have to do by hand is finding the board. You do not.

Put stripe.com in Company websites and the scraper reads the careers page, spots the ATS behind it, verifies the board actually serves jobs, and pulls everything. If no link is visible it falls back to probing each platform's public API with the obvious slug. In testing, plain domains resolved for 8 of 10 well-known companies — including one that had been acquired and moved onto its parent's board. A target list of 200 companies is a single run instead of 200 manual lookups.

Step-by-step

  1. Paste board URLs into Job board URLs, company websites into Company websites, or both.
  2. Add filters if you want a slice rather than everything — title, location, department, keyword, posted-after, remote-only.
  3. Set Max jobs in total on your first run so you can see the shape of the output before scraping at scale.
  4. Run it, then export as JSON, CSV or Excel, or pull it from the API.

API target requirement

API callers must supply boards or companyDomains (or their supported aliases). The Console prefilled form is an example, not an implicit target list for an empty API request. A raw POST with no target is rejected with a clear input error and no job-result event; the small Actor-start fee may still apply. In the 2026-09-05 cloud check, the runtime did not substitute the saved exampleRunInput for a missing request body. Copy the complete JSON example when integrating.

Example input JSON

{
"boards": [
"https://job-boards.greenhouse.io/airbnb",
"https://jobs.ashbyhq.com/openai",
"https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite"
],
"companyDomains": ["stripe.com", "figma.com"],
"titleIncludes": ["engineer", "developer"],
"locationIncludes": ["remote", "berlin"],
"postedAfter": "2026-08-01",
"maxTotalJobs": 500
}

Board URLs work in whatever form you copied them — job-boards.greenhouse.io/acme, boards.greenhouse.io/embed/job_board?for=acme, jobs.lever.co/acme, acme.recruitee.com, acme.jobs.personio.de, a Workday URL with or without the site ID. You can also write greenhouse:acme if you already know the slug.

Input aliases from other ATS scrapers

Input copied from another ATS actor runs here unchanged: companies, companyUrls, startUrls and queries are accepted as aliases and merged into the board and domain lists. Switching costs you nothing.

Monitoring mode: get only new jobs since your last run

The first successful run delivers a baseline of the matching postings within your scan limits. Later runs with Only new jobs since last run skip previously delivered identities. This is an unseen-record feed, not proof that a job was first published yesterday, changed, or closed.

Use a distinct Seen-jobs store name per watchlist and run only one task at a time against that store. Named key-value storage has no atomic cross-run lock; do not overlap schedules. Scan/output caps can limit coverage. If history cannot be read or persisted, or a previous delivery is unresolved, monitoring fails visibly instead of silently treating everything as new. Inspect RUN_SUMMARY and the saved pending-delivery record before retrying; do not delete history to bypass a reconciliation error.

How much does it cost to scrape ATS job boards?

$2.00 per 1,000 jobs. You are charged per job actually saved to your dataset, so filtered-out postings, duplicates and previously-seen postings in monitoring mode cost nothing. A run that scans 5,000 listings and matches 40 charges for 40. No matching result incurs a job event, but the configured Actor-start fee is $0.00005 per GB of memory, including on an empty run (one event at the 1 GB default).

Cost examples

Illustrative result-event charges at the base tier; not measured demand or recurring revenue. Add the small Actor-start fee described above. Actual available matches vary.

What you runRows chargedCost
20 companies, everything open, ~40 roles each800$1.60
Same 20 companies daily in monitoring mode, ~2% churn~16 per day~$0.03 per day, ~$1.00 per month
Engineering roles only across 200 companies, ~10 matches each2,000$4.00
Full inventory of one large Workday board with descriptions1,500$3.00

Two ways to keep a first run cheap: set Max jobs in total, and turn Include full job descriptions off — descriptions are the slow part on Workday, which needs one extra request per posting.

Integrations: n8n, Make, Zapier, MCP and the API

Use with AI agents (MCP server)

Add the scraper as a tool in Claude Desktop, Cursor, or any MCP client through Apify's hosted MCP server:

$npx -y @apify/actors-mcp-server --actors dataloft/ats-jobs-scraper

Or point your client at https://mcp.apify.com with your Apify token and enable this actor. An agent can then answer "which of these 30 companies opened a senior data role this week?" directly.

n8n, Make and Zapier

Use the official Apify node (n8n), the Apify app (Make) or the Apify integration (Zapier): pick Run Actor, choose dataloft/ats-jobs-scraper, paste the input JSON above, and read the dataset items in the next step. Combined with monitoring mode this gives you a "new roles at my watchlist → Slack / Sheets / CRM" workflow in three nodes.

JavaScript, Python and REST

const { ApifyClient } = require('apify-client');
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('dataloft/ats-jobs-scraper').call({
companyDomains: ['stripe.com', 'figma.com'],
titleIncludes: ['engineer'],
maxTotalJobs: 200,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
from apify_client import ApifyClient
client = ApifyClient(token=os.environ["APIFY_TOKEN"])
run = client.actor("dataloft/ats-jobs-scraper").call(run_input={
"companyDomains": ["stripe.com", "figma.com"],
"maxTotalJobs": 200,
})
items = client.dataset(run["defaultDatasetId"]).list_items().items

REST: POST https://api.apify.com/v2/acts/dataloft~ats-jobs-scraper/run-sync-get-dataset-items with an Authorization: Bearer <APIFY_TOKEN> header with the input JSON as the body.

What this Actor does and does not do

DoesDoes not
Pull live postings from 11 ATS platforms via their public APIsScrape LinkedIn, Indeed or any site that forbids it
Find a company's board from a plain domainLog in, solve CAPTCHAs, or bypass anti-bot systems
Normalise 11 response shapes into one schemaCollect candidate, recruiter or any personal data
Parse structured salary where publishedGuess salaries that are not published
Return only new postings in monitoring modeApply to jobs on your behalf

Limits and known caveats

  • Teamtailor publishes an RSS feed capped at 100 postings per board by the platform.
  • SmartRecruiters company IDs are case-sensitive; a wrong case returns an empty board, not an error.
  • Workday reports the board total only on the first page; this scraper holds that total, but a Workday tenant with a fully custom site ID may need the full URL pasted in.
  • Domain detection resolves most well-known companies (8 of 10 in testing). When it cannot, the run names the input it could not resolve instead of skipping it silently.
  • Descriptions on Workday cost one extra request per posting — expect longer runs when Include full job descriptions is on for large boards.
  • Rate limits are the platforms' own; the scraper retries with backoff and does not need a proxy for normal volumes.

Example tasks

Ready-made configurations — competitor hiring watchlists, daily new-role alerts, remote engineering roles from Greenhouse and Lever — are on the Example tasks tab of this actor: https://apify.com/dataloft/ats-jobs-scraper/examples

This actor only calls endpoints the ATS platforms publish for public consumption — the same JSON your browser fetches when you open a company's careers page, and in several cases documented integration APIs. It collects job postings, which are company information, not personal data: no candidate records, no recruiter contact details, no email harvesting.

You are still responsible for how you use the output, including under GDPR and each platform's terms. If you plan to redistribute the data commercially, read those terms first.

Frequently asked questions

Can I scrape a company if I do not know its ATS? Yes — put the plain website in Company websites. If detection fails the run tells you which inputs it could not resolve instead of quietly skipping them.

Why did a board return zero jobs? Almost always because the company genuinely has no openings. Every supported platform answers 200 with an empty list for a valid-but-dormant board.

Does it work with Workday if I only have the company name? Yes. Workday site IDs like NVIDIAExternalCareerSite are probed from a list of naming patterns, so nvidia.wd5.myworkdayjobs.com alone is enough.

How fresh is the data? As fresh as the company's own careers page — you are reading the same source it renders from.

Can I run this on a schedule? Yes, use Apify Schedules. With Only new jobs since last run on, each run returns only what appeared since the previous one; with it off, deduplication still gives you a clean current snapshot.

Does monitoring mode cost extra? No. Previously-seen postings are skipped before anything is charged, so a stable watchlist costs a few cents a day.

Can I use it from n8n or as an MCP tool? Yes — see the Integrations section above. No code is needed for n8n, Make or Zapier.

What happens when a filter matches nothing? The log reports scanned N → matched 0 for that board, so you can tell the difference between "no matches" and "something broke".

Other job scrapers by Dataloft Studio

  • Workday Jobs Scraper — the same Workday support, but deeper: search terms passed to Workday's own engine, site-ID discovery from a plain company website.
  • Remote Jobs Aggregator — 7 remote-only job boards merged into one deduplicated feed.

Changelog

  • 0.3 — 2026-09-05: monitoring fails visibly on history/delivery errors; budget-aware result charging and delivery reconciliation; published examples corrected and bounded.

  • 0.2 — 2026-09-02: monitoring mode (only new jobs since last run, named seen-jobs store); input aliases companies / companyUrls / startUrls / queries; memory capped at 1 GB (lower per-run platform cost); dataset schema and output schema aligned; changelog added.

  • 0.1.4 — 2026-08-25: fixed a concurrency race where Max jobs in total could be exceeded across parallel boards.

  • 0.1.2 — 2026-08-25: output schema added; first-run prefill capped so daily automated tests finish under 5 minutes.

  • 0.1 — 2026-08-25: initial release, 11 ATS platforms, domain-to-board detection.

Support and feedback

Issues and feature requests go in the Issues tab. Bug reports that include the input JSON get fixed fastest.