ATS Jobs Scraper — Greenhouse, Lever, Ashby & Job Tracker avatar

ATS Jobs Scraper — Greenhouse, Lever, Ashby & Job Tracker

Pricing

from $1.45 / 1,000 results

Go to Apify Store
ATS Jobs Scraper — Greenhouse, Lever, Ashby & Job Tracker

ATS Jobs Scraper — Greenhouse, Lever, Ashby & Job Tracker

Scrape open job postings from company career sites across 6 ATS platforms: Greenhouse, Lever, Ashby, Recruitee, Personio and Breezy. Auto-detects the platform, returns clean normalized JSON, and can output only new postings since the last run. No browser, no login. ~$1.5 per 1,000 results.

Pricing

from $1.45 / 1,000 results

Rating

0.0

(0)

Developer

Joinner Medina

Joinner Medina

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

9 days ago

Last modified

Share

Scrape open job postings from company career sites across six ATS platforms — Greenhouse, Lever, Ashby, Recruitee, Personio and Breezy — with no browser, no proxies and no login. Companies publish their openings through public job-board endpoints; this Actor reads them, auto-detects the platform, and turns everything into one clean, normalized dataset you can filter, export and monitor.

Built for recruiters, job boards, lead-generation teams and AI agents that need fresh hiring data, it also works great as a new-jobs monitor: run it on a schedule and it will only output postings that appeared since the previous run.

What does the ATS Jobs Scraper do?

  • 🎯 Auto-detects the ATS platform from a company slug (stripe), a domain (stripe.com) or a full career-site URL — no configuration needed. You can also force a platform when you know it.
  • 🧩 Returns one schema for every platform — 25+ fields per posting (title, locations, workplace type, department, seniority, salary, dates, URLs, descriptions, a stable global_id for deduplication), ready for a database, spreadsheet or diff.
  • 🔄 Detects new postings — with the Only new jobs option, each run outputs only jobs you haven't seen before. State persists between runs, so scheduled monitors just work.
  • 🔍 Filters before you pay — include/exclude title keywords, location keywords and remote-only mode; only matching postings are saved to the dataset.
  • 🛡️ Robust by design — retries with exponential backoff, per-request timeouts, response size caps, per-company error isolation (one bad board never kills a run) and runtime validation of every record before it reaches your dataset.
  • ⚡ Fast and light — pure HTTP against single-purpose endpoints: scraping three companies (~960 postings) takes about ten seconds, at a fraction of the cost of browser-based scrapers.

When should you use this Actor?

  • "I need open job postings from companies that use Greenhouse, Lever or Ashby." — pass company slugs (stripe), domains (stripe.com) or career-site URLs and get every live opening back.
  • "I want a daily alert with only the jobs posted since yesterday." — enable Only new jobs and schedule the run; each run returns just the postings that appeared since the previous one.
  • "I'm building a job board or a hiring dataset and need one clean format from many companies." — every platform is normalized into the same 25+ fields.
  • "I need to filter jobs by title, location or remote before downloading anything." — filters run before results are saved, so you only pay for what matches.
  • "I want to track hiring trends, salaries or remote-work ratios across companies over time." — run on a schedule and diff the datasets.

Why this Actor

  • One schema, six ATS platforms — Greenhouse, Lever, Ashby, Recruitee, Personio and Breezy, auto-detected from a plain company name or URL. No per-platform configuration.
  • Built-in new-jobs tracking — persistent state between runs turns it into a monitoring pipeline without any external database.
  • Pay per result — around $1.5 per 1,000 job postings; filtering happens before saving and filtered-out or empty results are not charged.
  • No browser, no proxies, no login — it reads the public job-board endpoints companies already publish: fast runs at a fraction of the cost of browser-based scrapers.
  • Production-hardened — retries with exponential backoff, per-request timeouts, response size caps, per-company error isolation and runtime validation of every record.

Which job platforms are supported?

PlatformExample career siteNotes
Greenhouseboards.greenhouse.io/stripeDescription HTML, departments, offices
Leverjobs.lever.co/spotifyWorkplace type, salary ranges when published
Ashbyjobs.ashbyhq.com/rampStructured compensation included
Recruiteebunq.recruitee.comFull offers API
Personiopersonio.jobs.personio.deXML board feed
Breezy HRbreezy.breezy.hrFull positions feed

More platforms are on the roadmap (SmartRecruiters, Workable). Need a specific one? Open an issue on the Actor's page.

How to scrape jobs from company career sites

  1. Open the Actor in Apify Console and paste your companies into the Companies field — one slug, domain or career-site URL per line. The Actor figures out the rest.
  2. (Optional) Narrow the output with title, location or remote filters so the dataset contains only what you need.
  3. (Optional) Enable Only new jobs and save the input as a task with a schedule: each run will output only the postings that are new since the last run.
  4. Run the Actor, then export the dataset in JSON, CSV, Excel or XML — or pull it through the Apify API.

How much does it cost to scrape job postings?

The Actor uses pay-per-result pricing: you only pay for the job postings saved to the dataset — no monthly fee and no charge for empty results. The exact price per result is always shown in the Pricing tab of this page; as a rule of thumb, 1,000 job postings cost around $1.5, and the Apify free plan includes a monthly usage credit that covers small runs and tests.

Since filtering happens before results are saved, narrow inputs (a few companies, specific keywords) translate directly into lower costs. For a daily new-jobs monitor with change detection, most runs return only a handful of fresh postings.

Input example

{
"companies": ["stripe", "jobs.lever.co/spotify", "ramp", "bunq.recruitee.com"],
"titleKeywords": ["engineer"],
"excludeTitleKeywords": ["intern"],
"remoteOnly": false,
"onlyNewJobs": true,
"includeDescriptions": true,
"maxJobsPerCompany": 1000,
"concurrency": 4
}
FieldTypeDefaultDescription
companiesarray of strings—Companies to scrape. Slug, domain or career-site URL. Required. Max 200 per run.
atsstringautoForce a platform (greenhouse, lever, ashby, recruitee, personio, breezy).
titleKeywordsarray of strings[]Only jobs whose title contains one of these keywords (case-insensitive).
excludeTitleKeywordsarray of strings[]Skip jobs whose title contains any of these keywords.
locationKeywordsarray of strings[]Only jobs whose location contains one of these keywords.
remoteOnlybooleanfalseOutput only jobs detected as remote.
onlyNewJobsbooleanfalseOutput only jobs not seen in previous runs (change detection).
includeDescriptionsbooleantrueFull descriptions (text + HTML where available). Disable for lighter, faster output.
maxJobsPerCompanyinteger1000Safety cap per company.
concurrencyinteger4Companies processed in parallel (1–10).

Output example

One dataset record per job posting (trimmed example):

{
"ats": "greenhouse",
"company": "stripe",
"company_name": "Stripe",
"title": "Abuse Investigator",
"location": "Dublin",
"locations": ["Ireland Locations", "Dublin"],
"remote": false,
"remote_type": "unknown",
"department": "8611 Security Analytics",
"seniority": "unknown",
"salary_text": null,
"salary_min": null,
"salary_max": null,
"salary_currency": null,
"salary_interval": null,
"posted_at": "2026-09-03T17:30:34.000Z",
"url": "https://stripe.com/jobs/search?gh_jid=8172487",
"job_id": "8172487",
"description_text": "…",
"global_id": "greenhouse:stripe:8172487",
"scraped_at": "2026-10-08T00:00:04.342Z"
}
FieldNotes
global_idStable unique id: {ats}:{company}:{job_id}. Use it as your dedupe key across runs and platforms.
remote_typeremote / hybrid / onsite / unknown (derived from provider fields, then location, then description).
seniorityHeuristic: intern / entry / senior / lead / manager / executive / unknown.
salary_*Best effort: structured provider compensation when available, otherwise parsed from the salary text; left null when nothing reliable is found.
posted_at / updated_atISO-8601 UTC when the provider exposes them.

Use cases

  • Job boards & aggregators — build a normalized feed of company-direct postings (no third-party job-board noise).
  • Recruiting & sourcing — find fresh openings at target companies filtered by title, location and seniority.
  • Job alerts & monitoring — schedule daily runs with Only new jobs and pipe new postings to Slack, email or your CRM.
  • Market research — track hiring trends, salary data and remote-work ratios across companies over time.
  • AI agents — feed fresh, structured hiring data into agent workflows via the Apify API or MCP server.

Integrations and automation

curl -X POST "https://api.apify.com/v2/acts/devjoi~ats-jobs-scraper/runs?token=$APIFY_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"companies": ["stripe", "ramp"], "maxJobsPerCompany": 50}'
  • Schedules — run hourly, daily or weekly; combined with Only new jobs that's a complete monitoring pipeline.
  • No-code — connect through Zapier, Make, n8n or the Apify integrations catalog.
  • AI agents — the Actor is described by full input/output schemas, so LLM agents can discover and chain it (Apify MCP server supported).
  • Exports — JSON, CSV, Excel, XML and direct API access; data retention is handled by the platform.

Limitations and honest scope

  • Only the six supported ATS platforms are covered. Companies on other platforms are reported as unresolvable in the run summary and skipped. SmartRecruiters and Workable are on the roadmap.
  • Salary fields are best-effort: structured provider data when published, otherwise parsed from salary text — left null when nothing reliable is found. seniority is derived heuristically from the job title.
  • The Actor reads only what each company publishes publicly on its job board. No login walls, no applicant or personal data.
  • One run processes up to 200 companies; use several runs or schedules for larger lists.

FAQ

Do I need proxies, a browser or a login?

No. The Actor talks to the public job-board endpoints that companies already expose (the same feeds that power their career pages). No browser, no proxies and no accounts — which keeps runs fast and cheap.

How do I get only new job postings?

Enable Only new jobs. The Actor remembers every posting it has seen (in a persistent state store) and each run outputs only postings that appear for the first time. Combine it with a schedule for automated new-jobs alerts.

Can I filter by location, title or remote?

Yes — use titleKeywords, excludeTitleKeywords, locationKeywords and remoteOnly. Filtering happens before results are saved, so you don't pay for postings you don't need.

Which companies can I scrape?

Any company using Greenhouse, Lever, Ashby, Recruitee, Personio or Breezy for their careers site — that includes thousands of tech and non-tech companies. If you pass a company that can't be resolved, the run continues with the rest and reports the failure in the summary.

The run reports a company couldn't be resolved. What should I check?

Most of the time it's the slug: use the exact career-site address (e.g. jobs.lever.co/acme → acme). If the company does use a supported platform, try forcing it with the Force ATS platform option.

How fresh is the data?

Completely fresh: every run queries the companies' live career sites at that moment. Nothing is cached between runs except the change-detection state (a list of already-seen posting ids).

The Actor only reads job-board endpoints that companies publish for public consumption, and it identifies itself normally. As with any scraping, you are responsible for how you use the data and for complying with the sites' terms — keep request volumes reasonable (defaults are already conservative).

Something went wrong — where do I look?

Open the run in Apify Console and check the SUMMARY record in the key-value store: it lists per-company results, error messages and statistics. Most issues are typos in company slugs. If a board is temporarily down, the run retries automatically and reports the company as failed without affecting the others.

Development

npm install
npm run local # runs against ./storage (INPUT.json is created on first run)
npm run local -- --fresh # clean slate (also clears change-detection state)
npm test # offline suite: unit + real-payload fixtures + e2e
npm run test:live # live integration tests against real career sites

Local runs emulate the platform storage layout (storage/datasets/default/, storage/key_value_stores/default/). The offline test suite runs against captured real API payloads in tests/fixtures/, and the opt-in live suite hits real endpoints — so upstream API changes are caught early.