Multi-ATS Jobs Scraper (Greenhouse, Lever, Ashby)
Pricing
$3.00 / 1,000 job returneds
Multi-ATS Jobs Scraper (Greenhouse, Lever, Ashby)
Scrape open job listings from the three big ATS job boards — Greenhouse, Lever and Ashby — straight from their public JSON APIs. No key, no login. Pass companies as "ats:token" (e.g. greenhouse:stripe) and get every posting normalized to one clean schema.
Pricing
$3.00 / 1,000 job returneds
Rating
5.0
(1)
Developer
Dami's Studio
Maintained by CommunityActor stats
0
Bookmarked
3
Total users
1
Monthly active users
a day ago
Last modified
Categories
Share
Multi-ATS Jobs Scraper (Greenhouse · Lever · Ashby)
Scrape open job listings straight from the public JSON APIs of the three most common applicant-tracking systems — Greenhouse, Lever and Ashby. No API key, no login, no anti-bot to fight. Point it at a list of company boards and get every posting back in one normalized schema.
Thousands of companies post their jobs through exactly these three boards. Instead of writing a scraper per company, give this actor a list like ["greenhouse:stripe","lever:spotify","ashby:ramp"] and it figures out the right endpoint for each and merges the results.
Input
| Field | Notes |
|---|---|
companies | Array of "ats:token" strings. ats is greenhouse, lever, or ashby; token is the company's board slug. e.g. greenhouse:stripe, lever:spotify, ashby:openai. |
ats + company | Convenience pair for a single board (e.g. ats: "greenhouse", company: "stripe"). Ignored when companies is set. |
maxItems | Max postings per company. 0 (default) = all open postings. |
proxyConfiguration | Optional, off by default. These public APIs have no anti-bot, so no proxy is needed. Only enable one if you hit IP-based rate limits across many boards. |
Where do I find the token?
It's the slug in the public careers URL:
- Greenhouse →
boards.greenhouse.io/<token>(also<token>.greenhouse.io) - Lever →
jobs.lever.co/<company> - Ashby →
jobs.ashbyhq.com/<name>
Output
One dataset row per job posting, deduped by url:
{"ok": true,"ats": "greenhouse","company": "stripe","title": "Account Executive, AI Sales","location": "San Francisco, CA","department": "1650 AI GTM Strategy & Solutions","employmentType": "FullTime","url": "https://stripe.com/jobs/search?gh_jid=7964697","postedAt": "2026-06-05T15:44:04-04:00","description": "Who we are… (plain text, HTML stripped)","salary": "$211.4K – $290.6K • Offers Equity"}
Field availability differs by ATS, so several fields can be null even on a successful row:
| Field | Greenhouse | Lever | Ashby |
|---|---|---|---|
employmentType | null (not exposed) | ✓ (commitment) | ✓ |
salary | null (not exposed) | best-effort (often null) | ✓ (structured, may be null) |
department | ✓ | ✓ (dept / team) | ✓ (dept / team) |
location / postedAt / url | ✓ when present, else null | ✓ when present, else null | ✓ when present, else null |
title and company are always present on a success row. Every other field above is null when the board doesn't supply it — this is honest data, not a failed scrape.
Greenhouse job descriptions come HTML-entity-encoded; the actor decodes and strips them to clean plain text.
Diagnostic rows (ok:false)
When a board has no open postings, uses a wrong/retired slug, or hits a network/blocked error, the actor pushes a single ok:false diagnostic row instead of job rows, for example:
{ "ok": false, "errorCode": "NO_RESULTS", "error": "The request succeeded but returned no results for this input.", "ats": "greenhouse", "company": "acme", "note": "No open postings for greenhouse:acme." }
An ok:false row means that one board had nothing to return — not that the run failed. These rows are never charged. Filter on ok:true to get only billable job postings. Empty input produces an ok:false row with errorCode: "BAD_INPUT" instead of crashing the run.
Billing & empty boards
Billed per job posting returned (job), and only for genuine ok:true rows. A board with no open postings (or a wrong/retired slug) produces a single ok:false diagnostic row explaining why — and is not charged. Network/blocked errors and empty/invalid input likewise return a coded ok:false diagnostic row instead of failing the whole run, and are never charged.
Example
{"companies": ["greenhouse:stripe", "greenhouse:airbnb", "lever:spotify", "ashby:ramp", "ashby:openai"],"maxItems": 0}