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

ATS Jobs API — Greenhouse, Lever & Ashby Job Scraper

Pricing

from $1.05 / 1,000 job returneds

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

ATS Jobs API — Greenhouse, Lever & Ashby Job Scraper

Open jobs from Greenhouse, Lever and Ashby boards through their official public APIs, with a per-company hiring summary by department and location, and a watch mode that reports jobs opened and closed since the last run.

Pricing

from $1.05 / 1,000 job returneds

Rating

0.0

(0)

Developer

Kittiwake Data

Kittiwake Data

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

6 days ago

Last modified

Categories

Share

A Greenhouse jobs API, a Lever jobs scraper and Ashby jobs in one Actor — plus the hiring signals on top. Give it a list of companies. For each one it reads every open job from the company's Greenhouse, Lever or Ashby job board through that ATS's official public posting API, and returns clean job rows and a per-company summary: open roles by department and by location, and how many jobs opened and closed since the last run.

Schedule it weekly in watch mode and you get only what changed — the jobs a company just opened and the ones it just closed. That is the hiring-surge signal: a team that opens ten sales roles in a month is buying, growing, or both.

What it is for

  • B2B sales and account-based marketing — hiring signals for your target accounts: who is opening roles in the department you sell to, and where.
  • Recruiters and talent teams — a clean feed of new openings at the companies you track.
  • Investors and analysts — headcount intent by department and location, week over week.
  • Job boards and aggregators — structured job rows from three ATSs through one input format.

What makes it different

a raw job scraperthis Actor
inputone ATS, one URL formatGreenhouse, Lever and Ashby URLs, ats:token, or just a company domain
outputjob rowsjob rows and a per-company summary: open jobs by department and top 10 locations
changere-download everythingmode: "watch" returns only jobs opened or closed since the last run
personal datawhatever the page hasno person fields at all; descriptions (which can name a hiring manager) are opt-in
sourceHTML scrapingeach ATS's official public job-posting API

Quick start

All open jobs at three companies:

{
"companies": [
"https://boards.greenhouse.io/airbnb",
"https://jobs.lever.co/palantir",
"https://jobs.ashbyhq.com/ashby"
]
}

Weekly hiring-signal watch, from domains and tokens:

{
"companies": ["palantir.com", "greenhouse:airbnb", "ashby:notion"],
"mode": "watch"
}

The first watch run of a board returns its open jobs as a baseline (changeType: "baseline"); every run after that returns only opened and closed rows, plus the summaries.

Input

fieldtypedefaultwhat it does
companiesstring[]requiredOne per company: a board URL (boards.greenhouse.io/…, job-boards.greenhouse.io/…, jobs.lever.co/…, jobs.ashbyhq.com/…), a token with its ATS (greenhouse:airbnb, lever:palantir, ashby:ashby), or a company domain (palantir.com)
modescan | watchscanscan: every open job. watch: only jobs opened or closed since the last run
includeDescriptionbooleanfalseAdd each job's description as plain text
maxJobsPerCompanyinteger50Cap on job rows per company in scan mode and on a first watch run; watch-mode opened/closed rows are never capped; the summary still counts every job
stateStoreNamestringats-jobs-api-stateNamed key-value store that remembers each board's open jobs between runs
requestDelayMsinteger1000Pause between requests. Lever calls are always at least 1 s apart

Domains. For a bare domain the Actor checks its robots.txt, then reads /careers (and /jobs only if /careers has no board link) once, and looks for a Greenhouse, Lever or Ashby board link. If none is found the company is reported as ats-not-detected and not charged. A bare token without an ATS (airbnb) is not guessed — the same token can be a different company on each ATS — so write greenhouse:airbnb.

Output

Two row types in one dataset, with a Jobs view and a Company summaries view.

A job row (type: "job"):

{
"type": "job",
"company": "palantir",
"ats": "lever",
"jobId": "6ed76ce8-4156-4b60-b120-403538bd66cd",
"title": "Administrative Business Partner",
"department": null,
"team": "Administrative",
"location": "Singapore, Singapore",
"workplaceType": "hybrid",
"remote": false,
"employmentType": "full-time",
"publishedAt": "2026-08-11T17:38:11.368Z",
"updatedAt": null,
"url": "https://jobs.lever.co/palantir/6ed76ce8-4156-4b60-b120-403538bd66cd",
"changeType": "baseline",
"checkedAt": "2026-09-26T07:20:46.388Z"
}

changeType is baseline (first run of that board), opened, closed (watch mode), or null (a job already there last run, in scan mode). workplaceType is remote, hybrid, onsite or null; employmentType is normalised to full-time, part-time, contract, intern or temporary where the ATS says so. Greenhouse publishes no employment type or workplace field, so those are null for Greenhouse jobs (remote is true only when the location says "Remote"). description appears only with includeDescription: true.

A company summary row (type: "company-summary"), one per company per run:

{
"type": "company-summary",
"company": "ashby",
"ats": "ashby",
"input": "https://jobs.ashbyhq.com/ashby",
"status": "ok",
"openJobs": 66,
"byDepartment": { "Engineering": 24, "Customer Success": 23, "Sales": 13, "Marketing": 3, "Design": 2, "People & Talent": 1 },
"byLocation": { "Remote - US": 36, "United Kingdom": 8, "Remote - European Union": 7, "Remote - Canada": 5, "Australia": 4 },
"opened": null,
"closed": null,
"source": "https://api.ashbyhq.com/posting-api/job-board/ashby",
"message": null,
"checkedAt": "2026-09-26T07:20:47.638Z"
}

status is ok, not-found (the ATS has no public board by that name), ats-not-detected or fetch-failed. byLocation holds the top 10 locations. byDepartment uses the team where a board publishes no department (common on Lever). opened and closed are null on a board's first run. A run summary is saved as RUN_SUMMARY in the run's key-value store.

What you pay for

eventpricewhen
Job returned$0.0015one job row written — every open job in scan mode, and a board's baseline on its first watch run
Job opened or closed$0.003one opened or closed row in watch mode. Never for a baseline
Company summary$0.005one company's board read and summarised successfully

Each row is charged once — a watch-mode change is never also charged as a job. Charges are made only after the board was read, the rows written and the state saved. Nothing is charged for a board that does not exist, a company with no board detected, or a failed request. The run stops at the spending limit you set and writes no more job rows than that limit covers.

Apify Store discounts apply by subscription plan: the prices above are the Free-plan prices; Starter pays 10% less, Scale 20% less and Business 30% less on every event.

Example: a weekly watch of 50 companies that together open or close 40 jobs a week costs 50 × $0.005 + 40 × $0.003 = $0.37 a week after the first run.

This Actor calls only each ATS's official, documented, public job-posting API, with GET requests and one identifying User-Agent. It never touches an application or apply endpoint.

ATSendpointwhat the publisher saysrobots.txt
Greenhouseboards-api.greenhouse.io/v1/boards/{token}/jobsJob Board API docs: "Job Board data is publicly available, so authentication is not required for any GET endpoints"disallows only /embed/
Leverapi.lever.co/v0/postings/{company}?mode=jsonpostings-api README: "all job postings in the published state are publicly viewable"Allow: /, Crawl-delay: 1 — honoured: Lever calls are at least 1 s apart
Ashbyapi.ashbyhq.com/posting-api/job-board/{name}publishes a Job Postings API for public job boards; no key needednone

SmartRecruiters is deliberately not supported: its API host's robots.txt disallows every crawler except LinkedInBot. Workday, iCIMS and others without a documented public posting API are out of scope too.

A company domain's careers page is fetched only if its robots.txt allows it; an unreadable robots.txt is treated as "no". Private, local and IP-address hosts are refused. A 403 or 429 from any host is not retried, and that host is not asked again in the same run.

Personal data

No row carries a person field — no recruiter, hiring manager, email or phone. Greenhouse's metadata and data_compliance blocks are never read. Job descriptions are free text written by the company and can name a hiring manager, so they are off by default; switch includeDescription on only if you need them, and handle them under your own data-protection terms.

FAQ

Which ATSs are supported? Greenhouse, Lever and Ashby. Paste any board URL, job URL, ats:token or company domain.

How do I find a company's board token? It is the first path segment of its board URL: boards.greenhouse.io/airbnb, jobs.lever.co/palantir, jobs.ashbyhq.com/ashby. Or give the company domain and let the Actor find it.

How do I get only new jobs? Use mode: "watch" on a schedule. Keep the same stateStoreName between runs; use a different one per list if you run several.

Why are some fields null? The ATS does not publish them. Nothing is guessed or inferred beyond the remote flag for Greenhouse locations that say "Remote".

Can I export to CSV or Excel? Yes. The dataset downloads as JSON, CSV, Excel or XML, or read it over the Apify API. Use the Jobs view for a flat table.

Is this a LinkedIn or Indeed scraper? No. It reads companies' own job boards, which is where LinkedIn and Indeed listings usually come from.

Support

Use the Issues tab on this Actor. Include the run id and the input you used.