Multi-ATS Jobs Scraper (Greenhouse, Lever, Ashby)
Pricing
$3.00 / 1,000 job returneds
Multi-ATS Jobs Scraper (Greenhouse, Lever, Ashby)
Returns open job postings from Greenhouse, Lever, and Ashby ATS boards (title, location, department, apply link) via their public JSON APIs. Pass companies as "ats:token" (e.g. greenhouse:stripe). Top use: tracking a target company's full job board.
Pricing
$3.00 / 1,000 job returneds
Rating
5.0
(2)
Developer
Dami's Studio
Maintained by CommunityActor stats
0
Bookmarked
3
Total users
1
Monthly active users
15 days 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}