Jobs
Multi-ATS Jobs Scraper (Greenhouse, Lever, Ashby)
One normalized jobs feed across Greenhouse, Lever and Ashby — hand it a bare company slug or careers URL and we auto-detect the ATS, then return a unified schema with department, team, remote flag, and salary range (where the ATS exposes it) that today's single-ATS actors leave raw or don't normalize at all.
Free Apify credit covers a first run. No credit card to try.
What this Actor scrapes
Greenhouse, Lever, and Ashby together power the hiring pipelines of thousands of tech companies — but each platform speaks its own API dialect, and you don't always know upfront which one a given company runs on. This Actor figures that out for you: point it at a company, it auto-detects the platform, normalises the fields into one consistent schema, and writes the results to a typed Apify dataset.
Give it a bare board slug ("stripe"), a full careers URL ("https://jobs.lever.co/palantir"), or an explicit {atsType, companySlug} object if you already know the platform — mix all three forms freely in a single run. That auto-detection is the part the single-ATS wrappers on the Store don't do.
What we handle for you
- 🛡️ Browser fingerprint rotation — requests impersonate real browser TLS handshakes so the target sees a browser, not a bot.
- 🌐 Residential proxy rotation via Apify Proxy — fresh session and exit IP when upstream pushes back.
- 🔁 Retries with exponential backoff on
408 / 429 / 5xx— up to 5 attempts,Retry-Afterhonoured. - 🧱 Rate-limit-aware pacing — we slow down rather than get banned; partial successes surface via a clear status message.
- 🧊 Normalised, typed dataset rows — Pydantic-validated, ISO-8601 timestamps, stable IDs across all three ATS platforms.
- 💰 Pay-Per-Event pricing — you pay only for job postings that land in your dataset.
Use cases
- Talent intelligence pipelines — track hiring velocity at target companies; diff successive runs to spot new headcount or sudden freezes.
- Competitive analysis — monitor which roles a competitor is hiring for and in which locations.
- Job aggregators — power a niche job board (e.g. "remote fintech jobs") without scraping fragile HTML pages.
- Recruitment automation — feed a CRM or ATS of your own with normalised job data from multiple sources in one shot.
- Market research — identify which departments are growing fastest across an industry by counting roles by department field.
- Targeted talent mapping — set
departmentFilter,locationFilter, orremoteOnlyon the run itself so you only pay for the postings you actually want, instead of filtering the whole dataset afterward. - No-code workflows (n8n / Make / Zapier) — connect via the Apify integration node; pipe new postings to a Slack channel or Google Sheet without writing code.
Input
Paste this into the Apify Console, or send it as the run input over the API. Proxy settings are on by default; you rarely need to touch them.
| Field | Type | Required | What it does |
|---|---|---|---|
companies | array | yes | List of companies to scrape. Each entry may be: (1) a bare board slug, e.g. "stripe" — auto-detected via a concurrent probe of all three ATSes; (2) a full careers/ATS URL, e.g. "https://jobs.lever.co/palantir" — the… |
maxItemsPerCompany | integer | no | Maximum number of job postings to collect per company, applied after departmentFilter/locationFilter/remoteOnly. Set to 0 (default) to collect all available postings. |
includeDescription | boolean | no | When enabled, the full job description (HTML/text) is fetched and stored in the description_html field. Disable to reduce dataset size and run time. |
departmentFilter | string | no | Case-insensitive substring match against the normalised department or team field. Leave empty to skip. |
locationFilter | string | no | Case-insensitive substring match against the normalised location field. Leave empty to skip. |
remoteOnly | boolean | no | When enabled, keep only postings where remote == true. Postings with an unknown remote status are excluded. |
{
"companies": [
{
"atsType": "greenhouse",
"companySlug": "stripe"
},
"https://jobs.lever.co/palantir",
"https://jobs.ashbyhq.com/ramp"
],
"maxItemsPerCompany": 50,
"includeDescription": true,
"departmentFilter": null,
"locationFilter": null,
"remoteOnly": false,
"proxyConfiguration": {
"useApifyProxy": true
}
} Output
One row per result, schema-validated before it is written. Export JSON, CSV, Excel or XML from the run, or read it over the API.
companyatsjob_idtitledepartmentteamlocationremotesalary_minsalary_maxsalary_currencyurlposted_atupdated_atdescription_html
{
"company": "ramp",
"ats": "ashby",
"job_id": "3b1c2e9a-0000-0000-0000-000000000000",
"title": "Senior Software Engineer, Payments",
"department": "Engineering",
"team": "Payments",
"location": "New York City",
"remote": false,
"salary_min": 211400,
"salary_max": 290600,
"salary_currency": "USD",
"url": "https://jobs.ashbyhq.com/ramp/3b1c2e9a-0000-0000-0000-000000000000",
"posted_at": "2026-05-02T18:11:00.000Z",
"updated_at": null,
"description_html": "<p>About the role...</p>"
} Pricing
| Event | Price | When |
|---|---|---|
| Actor start | $0.20 | Once per run, covers warm-up and proxy session setup. |
| Result emitted | $0.0015 | Per result written to the dataset. |
You pay only for results that land. Cap any run with maxTotalChargeUsd. See pricing & billing for worked examples.
Limitations
- Salary is Ashby-only, and we won't fake the rest. Only Ashby's public API exposes a structured salary field. Greenhouse and Lever don't publish comp data structurally — pulling a number out of their free-text descriptions would mean unreliable regex guesses, so
salary_min/salary_max/salary_currencyare honestlynullfor those two platforms instead. - Company slugs or URLs must exist on the chosen ATS. Passing an incorrect one returns zero results for that company (logged as a warning, not a fatal error).
- Auto-detect tie-breaks on ambiguous slugs. If the same bare slug happens to resolve on more than one platform (rare — not observed in practice), we keep the one with more open postings and log a warning. Pass an explicit
{atsType, companySlug}object if you need to force a specific platform. - Ashby's job-board schema may evolve without notice — we track public API changes but cannot guarantee instant compatibility if Ashby renames or drops fields.
description_htmlfield can contain raw HTML. Strip tags on your end if plain text is required.- Pagination is handled automatically, but very large boards (thousands of postings) may produce long run times at the default proxy tier.
- The Actor does not support authenticated ATS boards (private job listings require employer credentials).
FAQ
Can I scrape multiple companies in one run?
companies array can hold up to 1 000 entries, mixing bare slugs, careers URLs, and explicit {atsType, companySlug} objects. They run sequentially; use maxItemsPerCompany to cap each one.Do I need to know which ATS a company uses before I start?
{atsType, companySlug} form skips detection entirely and runs slightly faster.Does every job posting include a salary range?
salary_min / salary_max / salary_currency come back null for Greenhouse and Lever postings. If salary data is a hard requirement, point the Actor at Ashby-hosted employers.Does this work with private / internal job boards?
Can I schedule this to run daily?
Ready to run it?
Open the listing on Apify, paste the input above, and watch rows land. If it ever breaks, it is our problem before it is yours.
Related Actors