Greenhouse & Lever Jobs API — ATS Job Aggregator avatar

Greenhouse & Lever Jobs API — ATS Job Aggregator

Pricing

from $2.00 / 1,000 job posting results

Go to Apify Store
Greenhouse & Lever Jobs API — ATS Job Aggregator

Greenhouse & Lever Jobs API — ATS Job Aggregator

Job scraper API for Greenhouse & Lever ATS: structured JSON, deduplicated results, parsed salary ranges, first_published dates. No LinkedIn, no bans. Pay-per-event: $0.002 per result.

Pricing

from $2.00 / 1,000 job posting results

Rating

0.0

(0)

Developer

Farside

Farside

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

4 days ago

Last modified

Categories

Share

ATS Job Aggregator (Greenhouse & Lever)

ATS Job Aggregator pulls structured job postings straight from company Applicant Tracking Systems — Greenhouse and Lever — through their public JSON APIs. No LinkedIn, no scraping of rendered pages, no account bans, no duplicate jobs. Every record comes out as clean JSON with the fields LinkedIn-based scrapers usually can't give you: first_published timestamps, parsed salary ranges, and the company → ATS mapping.

Why use ATS Job Aggregator?

LinkedIn scrapers have three chronic problems: jobs duplicated across scrapes, accounts getting banned, and missing salary data. Because this Actor reads the companies' own ATS APIs, none of those apply — you get the same data the company publishes, in structured JSON, with no anti-bot risk. The APIs return the job's true posting date and (where published) its salary range, which is exactly what you need to detect new jobs and compare pay across companies.

Who is this for?

  • Recruiting agencies / headhunters — batch-monitor all your client companies' open roles. Point the Actor at a list of Greenhouse board tokens and Lever slugs, run it on a schedule, and diff first_published between runs to see what's new this week.
  • Job seekers with a target company list — keep a personal feed of every opening at your 20 dream companies, with salary ranges and apply links, without browsing 20 career pages daily.
  • Market researchers / analysts — count open roles per company, per department, per location; track hiring velocity over time; compare salary bands across the industry (USD/EUR/SGD/MYR/etc. parsed automatically).

Why this beats the LinkedIn-based scrapers

LinkedIn scrapersThis Actor
Data sourceRendered LinkedIn pagesCompany ATS public APIs
Duplicate jobs within a runCommonDeduplicated via stable dedupe_key
Account bansFrequentNo accounts involved — nothing to ban
first_publishedNot reliably availableDirect from Greenhouse / Lever
Structured salaryMissing or free-textsalary_min / salary_max / salary_currency / salary_period parsed
Run on schedule via ApifyYesYes, with API access and integrations

How it works

The Actor takes two input lists and fetches everything:

  1. GreenhouseGET https://boards-api.greenhouse.io/v1/boards/{token}/jobs lists every open job; the Actor then fetches each job's detail endpoint (skippable) to capture first_published, department, and the compensation block.
  2. LeverGET https://api.lever.co/v0/postings/{slug}?mode=json returns the full posting list including createdAt and salary text, in a single request.

Salary text is parsed with a currency-aware regex engine — "$60,000 - $97,000/year", "€2,400 - €5,600/month", "RM 3,000 - RM 5,000", "110,000 - 200,000/year SGD" all become structured salary_min / salary_max / salary_currency plus a salary_period (year / month / hour / week) so hourly rates never masquerade as annual pay. When a range can't be parsed, the raw text is kept in salary_text and the numbers are set to null — nothing is guessed.

Failures are isolated per company (60s timeout, 2 retries). One dead token never kills the run: the failure is reported in a clearly-flagged record and in the run log.

How much does it cost?

Pay-per-event pricing: $0.002 per job result (the result-item event). No per-run or startup fees unless you enable additional synthetic events in Apify Console.

  • 1,000 job records ≈ $2
  • A 100-company monitor producing 5,000 records ≈ $10

Compare: Amazon product data on Apify ≈ $3/1K, Google Maps ≈ $1.5–4/1K. You're paying for structured, deduplicated, salary-enriched data — not proxy traffic.

How to run it

  1. Collect your company list. Greenhouse: open the company's jobs board at boards.greenhouse.io/{token} — the {token} in the URL is what you paste (e.g. airtable, anthropic). Lever: open jobs.lever.co/{slug} — the {slug} is what you paste (e.g. palantir).
  2. Fill in the input (see below) — either or both lists, empty ones are fine.
  3. Hit Start. Data lands in the dataset — download as JSON, CSV, or Excel, or pull it through the API.

Input example

{
"greenhouse_tokens": ["airtable", "anthropic", "datadog"],
"lever_slugs": ["palantir", "canva"],
"enrich_details": true,
"max_results": 0
}
  • greenhouse_tokens — Greenhouse board tokens (optional, default [])
  • lever_slugs — Lever company slugs (optional, default [])
  • enrich_detailstrue (default) fetches every Greenhouse job's detail endpoint for first_published, department and salary; false skips detail requests and only returns list-level fields (faster, but those three fields stay null for Greenhouse jobs). Lever data is always complete — its single request includes everything.
  • max_results — stop after N job records across all companies; 0 = unlimited

Output example

{
"company": "Airtable",
"source_ats": "greenhouse",
"job_id": "8403127002",
"title": "Account Executive, Strategic Accounts",
"location": "Remote - US",
"department": "Sales",
"salary_min": 272000,
"salary_max": 354000,
"salary_currency": "USD",
"salary_text": "$272,000 — $354,000",
"salary_period": "year",
"first_published": "2026-02-28T14:04:24.000Z",
"last_updated": "2026-03-27T15:33:08-04:00",
"apply_url": "https://job-boards.greenhouse.io/airtable/jobs/8403127002",
"scraped_at": "2026-08-31T03:40:00.000Z",
"dedupe_key": "5c23f0aeefa61d9a",
"errors": []
}

Fields

FieldTypeDescription
companystringCompany name (Greenhouse) or slug (Lever). Falls back to the board token for Greenhouse when details are not fetched
source_atsstringgreenhouse or lever
job_idstringATS-internal job ID
titlestringJob title
locationstringLocation as published
departmentstringDepartment / team
salary_min / salary_maxnumberParsed range bounds (null when not published or unparseable)
salary_currencystringISO 4217 code (USD, EUR, MYR, SGD, ...)
salary_textstringRaw salary fragment as published
salary_periodstringyear / month / hour / week — compare only values sharing the same period
first_publishedstringFirst posting date (Greenhouse first_published, Lever createdAt), normalized to UTC; null for Greenhouse when enrich_details: false
last_updatedstringLast modification in the ATS (Greenhouse only; always null for Lever)
apply_urlstringDirect posting/application URL
scraped_atstringWhen this record was scraped (UTC)
dedupe_keystringsha256 of company identifier + normalized title + normalized location + job_id (first 16 hex chars) — stable across runs, so you can diff two scrapes without re-fetching everything
errorsarrayAlways [] on normal records; a failed company produces one flagged record with the error message here

All fields are nullable. You can download the dataset in JSON, HTML, CSV, or Excel format from the Storage tab.

Tips

  • Diff two runs for new jobs: dedupe_key is deterministic — join runs on it and flag records whose first_published is newer than your last scrape. (Each run pushes the full current posting list again; the stable key is what makes the diff cheap.)
  • Keep costs flat: with enrich_details: false, Greenhouse runs make 1 request per company instead of 1 per job — use it when you only need the posting list.
  • Mix both ATSs freely: companies don't all use the same ATS; combine greenhouse_tokens + lever_slugs in one run for a single unified dataset.
  • Schedule it: use Apify's Scheduler with weekly runs and email/API delivery for a zero-touch monitoring pipeline.

FAQ & support

Will my IP get banned? No. The Actor only calls public, undocumented-but-stable JSON endpoints of the ATS providers themselves — the same endpoints the companies' own career pages use. There is no account and no anti-bot wall to trip.

Why is salary_min null sometimes? Many companies simply don't publish pay ranges. If the range exists but is in an exotic format, the raw text stays available in salary_text.

Do I need proxies or login credentials? No. No proxies, no cookies, no credentials.

Our Actors only extract what companies have chosen to publish publicly. The data may include personal data (e.g. names inside job descriptions), which is protected by the GDPR in the EU and similar laws worldwide. Only scrape for legitimate purposes and consult your lawyers if unsure. Use the Issues tab for feedback and the API tab for programmatic access.

Local development

pip install -r requirements.txt
# put your input in storage/key_value_stores/default/INPUT.json, then:
apify run
# unit tests:
python -m unittest discover -s tests