ATS Hiring-Signal Scraper (Greenhouse, Lever, Ashby) avatar

ATS Hiring-Signal Scraper (Greenhouse, Lever, Ashby)

Pricing

from $3.00 / 1,000 job results

Go to Apify Store
ATS Hiring-Signal Scraper (Greenhouse, Lever, Ashby)

ATS Hiring-Signal Scraper (Greenhouse, Lever, Ashby)

Scrape jobs and hiring signals from Greenhouse, Lever and Ashby public JSON APIs into one uniform, deduplicated schema.

Pricing

from $3.00 / 1,000 job results

Rating

0.0

(0)

Developer

Daan Hoeven

Daan Hoeven

Maintained by Community

Actor stats

0

Bookmarked

1

Total users

0

Monthly active users

2 days ago

Last modified

Share

ATS Hiring-Signal Scraper — Greenhouse, Lever & Ashby Jobs API

Scrape job postings and hiring signals from the three biggest modern Applicant Tracking Systems — Greenhouse, Lever and Ashby — straight from their public, auth-free JSON APIs. Every job from every source is returned in one clean, uniform, deduplicated schema, with normalised locations, parsed salaries and optional change-detection (new / existing / closed roles) that turns raw vacancies into intent data.

No browser, no Playwright, no fragile HTML scraping. Pure HTTP + JSON → fast, cheap and maintenance-light. These endpoints are embedded in the career pages of thousands of companies, so the ATS vendors can't quietly break them without breaking all their customers — which makes this Actor far more stable than typical job scrapers.

Keywords: Greenhouse scraper, Lever scraper, Ashby scraper, ATS scraper, jobs API, hiring signals, intent data, sales intelligence, recruitment sourcing, job board aggregator, vacancy data, salary benchmarking.


What it does

  • 🟢 Greenhouseboards-api.greenhouse.io/v1/boards/{token}/jobs
  • 🔵 Leverapi.lever.co/v0/postings/{token}
  • 🟣 Ashbyapi.ashbyhq.com/posting-api/job-board/{token}

For each company you list (by ATS + token, or just by career-page URL), the Actor fetches every open posting, normalises it, deduplicates across sources, applies your filters, and pushes the results to the dataset.

Key features

  • Uniform cross-ATS schema — one record shape no matter the source. Fields a source doesn't provide are null, never missing.
  • Hiring-signal / change detection — run on a schedule and each job is tagged new, existing or closed. A company that just opened five sales-engineer roles is a growth signal; a closed role is a different signal. This is the intent data layer.
  • Normalised data — locations parsed into {city, country, remote}, salaries parsed into {min, max, currency, interval}, HTML descriptions cleaned to plain text.
  • Cross-ATS deduplication — stable jobId of the form {ats}_{token}_{id}; first occurrence wins.
  • Powerful filtering — by keyword (title/description), location (incl. remote), and department/team, plus a hard maxItems cost cap.
  • Robust by design — per-company error isolation (one failure never kills the run), retries with exponential backoff, 429/Retry-After respected, and SCHEMA_CHANGED warnings if an ATS ever changes its response shape.

Who it's for — use cases

  • Sales intelligence / intent data — detect company growth by department. "Acme is hiring 5 Sales Engineers" is a buying signal for B2B sellers.
  • Recruitment & sourcing — a live, structured feed of open roles across your target companies, with salary and location already parsed.
  • Job-board aggregators — pull thousands of clean, deduplicated postings from many companies into one schema, ready to publish.
  • Market & salary benchmarking — aggregate compensation data across companies, roles and regions.
  • Competitive monitoring — watch which roles competitors open and close over time.

Input

Provide at least one of companies or companyUrls. Everything else is optional.

FieldTypeDescriptionDefault
companiesarray<object>Explicit { ats: "greenhouse" | "lever" | "ashby", token: string }.
companyUrlsarray<string>Career-page / job-board URLs; ATS + token are derived automatically.[]
keywordsarray<string>Keep jobs whose title/description match any term (case-insensitive).[]
locationsarray<string>Keep jobs matching location/city/country (use remote for remote roles).[]
departmentsarray<string>Keep jobs matching department/team.[]
includeDescriptionbooleanInclude cleaned plain-text + HTML description.true
includeCompensationbooleanInclude normalised compensation where available.true
detectChangesbooleanHiring-signal mode: tag jobs new/existing/closed vs. the previous run.false
maxItemsintegerHard result cap for cost control (0 = unlimited).0
proxyConfigurationobjectApify proxy settings (datacenter is enough).{ "useApifyProxy": true }

Change detection needs a previous snapshot, so run the Actor on a schedule to get meaningful new/closed signals.

Example input

{
"companies": [
{ "ats": "greenhouse", "token": "stripe" },
{ "ats": "lever", "token": "netflix" },
{ "ats": "ashby", "token": "ramp" }
],
"companyUrls": [
"https://boards.greenhouse.io/airbnb"
],
"keywords": ["sales engineer", "account executive"],
"locations": ["Amsterdam", "remote"],
"departments": ["Sales"],
"includeDescription": true,
"includeCompensation": true,
"detectChanges": false,
"maxItems": 0,
"proxyConfiguration": { "useApifyProxy": true }
}

You can also point the Actor at plain career-page URLs and let it figure out the ATS:

{
"companyUrls": [
"https://boards.greenhouse.io/stripe",
"https://jobs.lever.co/netflix",
"https://jobs.ashbyhq.com/ramp"
]
}

Output

One record per job, in a uniform schema:

{
"jobId": "greenhouse_acme_4012345",
"source": "greenhouse",
"companyName": "Acme Inc.",
"companyToken": "acme",
"title": "Senior Sales Engineer",
"department": "Sales",
"team": "Revenue",
"location": "Amsterdam, NL",
"city": "Amsterdam",
"country": "NL",
"remote": true,
"employmentType": "Full-time",
"description": "Plain-text description…",
"descriptionHtml": "<p>…</p>",
"compensation": { "min": 80000, "max": 110000, "currency": "EUR", "interval": "year" },
"applyUrl": "https://boards.greenhouse.io/acme/jobs/4012345",
"postedAt": "2026-06-01T00:00:00Z",
"updatedAt": "2026-06-03T00:00:00Z",
"scrapedAt": "2026-06-04T10:00:00Z",
"changeStatus": "new"
}

changeStatus is only present in detectChanges mode. Any field a source doesn't expose is null, so the schema stays consistent across all three ATS providers.

At the end of every run a small summary is written to the key-value store (RUN_SUMMARY): { companiesOk, companiesFailed, totalJobs }.


Finding a company's token

The token is the company slug in its public career-page URL:

ATSCareer-page URLToken
Greenhousehttps://boards.greenhouse.io/stripestripe
Leverhttps://jobs.lever.co/netflixnetflix
Ashbyhttps://jobs.ashbyhq.com/rampramp

If you're not sure, just drop the full URL into companyUrls and the Actor resolves the ATS and token for you. Unresolvable URLs are skipped with a warning instead of failing the run.


Pricing

This Actor uses the pay-per-result model: you're charged per job pushed to the dataset. Use maxItems to set a hard cost ceiling. Because everything runs over plain HTTP JSON (no headless browser), compute cost is minimal.


FAQ

Does this need an API key or login for Greenhouse / Lever / Ashby? No. All three endpoints are public, auth-free JSON APIs embedded in companies' career pages.

How stable is it? Very. These endpoints power thousands of live career pages, so the vendors can't break them without breaking their own customers. The only realistic maintenance signal is a SCHEMA_CHANGED warning, which the Actor logs automatically.

What are "hiring signals"? Turn on detectChanges and run on a schedule. The Actor compares each run to the previous one and tags jobs as new, existing or closed — letting you spot when a company starts (or stops) hiring for a role or department. That's intent data for sales and market research.

Does it scrape applicant or candidate data? No. Only public job postings and company metadata. No personal/applicant data (GDPR by design).

Can I get salaries? Where the ATS exposes them (notably Ashby and some Lever boards), yes — normalised into {min, max, currency, interval}. Set includeCompensation: true (the default).

Which other ATS providers are supported? v1 covers Greenhouse, Lever and Ashby. The adapter pattern makes adding Workable, Recruitee or Personio straightforward in a future version.


Running locally

npm install
npm run check # typecheck + lint + tests
apify run # runs with .actor/ input

Built with Node.js 20 + TypeScript (strict), Apify SDK v3, Crawlee HttpCrawler and got-scraping. Pure API-first — no browser.