ATS Jobs Aggregator: Greenhouse, Lever, Ashby, SmartRecruiters
Pricing
Pay per event
ATS Jobs Aggregator: Greenhouse, Lever, Ashby, SmartRecruiters
Pricing
Pay per event
Rating
0.0
(0)
Developer
David-Solal Chalmeau
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
16 days ago
Last modified
Categories
Features
Share
ATS Jobs Aggregator: Greenhouse, Lever, Ashby & SmartRecruiters
Get up to maxJobsPerCompany open jobs per company (default 200), in one flat table, from each company's own public job-board API — totalJobs and truncated on every row tell you when a board was bigger than the cap. Give it a list of companies (ashby:ashby, greenhouse:stripe, lever:ro, smartrecruiters:smartrecruiters) and get one row per job with title, department, location, remote flag, employment type, posting dates, apply URL and a plain-text description. Built for job aggregators, market research, competitor hiring tracking and LLM agents that need clean structured rows rather than scraped HTML.
What you get per job
| Field | Notes |
|---|---|
company, ats | The company you asked for and the ATS it came from |
jobId | The board's own job id (stable across runs) |
title, department, location | Department and location are only as precise as the ATS provides |
remote | true / false / null. null means the ATS does not publish remote status (Greenhouse) — it is never guessed |
employmentType | e.g. Full-time, FullTime. Only some ATS provide it (null otherwise) |
postedAt, updatedAt | ISO 8601, or null when the ATS does not publish the field (Lever/Ashby/SmartRecruiters have no updatedAt; Greenhouse has both) |
applyUrl, jobUrl | jobUrl is always the board job page. applyUrl is a separate direct application link where the ATS publishes one (Lever, Ashby, SmartRecruiters); it is null for Greenhouse, whose public API has no separate apply endpoint — no invented links |
totalJobs, truncated | totalJobs is how many postings the ATS reports for that board; truncated: true means maxJobsPerCompany cut the board short — including when the cap stopped the SmartRecruiters fetch before your filters ran — so this run did not cover all of it |
descriptionText | Plain text: HTML stripped, entities decoded (Greenhouse double-escapes part of its content, e.g.  ), max 2000 characters. null for SmartRecruiters (its public list endpoint carries no description) |
sourceUrl, fetchedAt | The exact API URL that produced the row, and when it was fetched |
Example input
{"companies": ["ashby:ashby", "greenhouse:stripe", "lever:ro", "smartrecruiters:smartrecruiters"],"keywords": ["engineer"],"locations": ["Berlin", "Remote"],"remoteOnly": false,"maxJobsPerCompany": 200,"maxCompanies": 20}
companies accepts "ats:slug" strings or {"ats": "greenhouse", "slug": "stripe"} objects. Slugs are validated (letters/digits, ., _, -, max 64 chars) and deduplicated case-insensitively — the board APIs themselves are case-insensitive, so greenhouse:stripe and greenhouse:Stripe are one board, fetched and billed once. Keywords match the title or department (case-insensitive), locations match as substrings, and remoteOnly: true keeps only jobs the ATS explicitly marks remote.
Example output
Real rows from a run of the input above (descriptions truncated here for readability):
{"company": "ashby","ats": "ashby","jobId": "7458d4e9-da2e-47bd-98cb-adfda43d42b2","title": "Engineering Manager - EU","department": "Engineering","location": "Remote - European Union","remote": true,"employmentType": "FullTime","postedAt": "2024-03-04T14:29:08.532Z","updatedAt": null,"applyUrl": "https://jobs.ashbyhq.com/ashby/7458d4e9-da2e-47bd-98cb-adfda43d42b2/application","jobUrl": "https://jobs.ashbyhq.com/ashby/7458d4e9-da2e-47bd-98cb-adfda43d42b2","descriptionText": "Hi 👋 I'm Colin https://uk.linkedin.com/in/colinhoweuk, Director of Engineering, Europe. How do you feel about software e...","sourceUrl": "https://api.ashbyhq.com/posting-api/job-board/ashby?includeCompensation=false","fetchedAt": "2026-10-28T05:26:19.043Z"}
{"company": "ro","ats": "lever","jobId": "3910baa9-f961-4380-ad0a-01deec07f7a2","title": "Analytics Engineer, Conversational Analytics","department": "Data","location": "New York, NY","remote": false,"employmentType": "Full-time","postedAt": "2026-09-16T17:43:56.068Z","updatedAt": null,"applyUrl": "https://jobs.lever.co/ro/3910baa9-f961-4380-ad0a-01deec07f7a2/apply","jobUrl": "https://jobs.lever.co/ro/3910baa9-f961-4380-ad0a-01deec07f7a2","descriptionText": "Join Tech @ Ro to build the future of healthcare, from the g...","sourceUrl": "https://api.lever.co/v0/postings/ro?mode=json","fetchedAt": "2026-10-28T05:26:19.065Z"}
Row-level totalJobs / truncated are shown on every delivered row but omitted from the two samples above for readability.
A company that cannot be fetched yields one row instead of jobs, and the run continues:
{"company": "this-board-does-not-exist-xyz123","ats": "greenhouse","error": "BOARD_NOT_FOUND","message": "Greenhouse board not found: this-board-does-not-exist-xyz123"}
error is BOARD_NOT_FOUND (no such board), UPSTREAM_ERROR (ATS returned an error or unusable payload), INVALID_COMPANY (unknown ATS name, malformed or traversal-looking slug) or SKIPPED_MAX_COMPANIES (valid company beyond your maxCompanies — one free row per board so you can see exactly what was left out).
How to find a company's slug
Open the company's careers page and read the URL: boards.greenhouse.io/<slug>, jobs.lever.co/<slug>, jobs.ashbyhq.com/<slug>, jobs.smartrecruiters.com/<slug>. For companies that host the board on their own domain (e.g. Stripe's stripe.com/jobs), the slug is the Greenhouse/Lever identifier, usually visible in the embedded job-board iframe URL.
Pricing
Pay per event: a small start fee per run plus $0.0018 per delivered job row. Companies whose board is missing or fails, invalid company inputs, and jobs removed by your filters are free — you are only charged for rows you actually receive. If your spending limit is reached the Actor stops cleanly instead of continuing unpaid. Current prices are shown on this page.
Limits and honest notes
- Sources: the official public job-board APIs of Greenhouse, Lever, Ashby and SmartRecruiters — the endpoints each vendor publishes for embedding a company's board on its own site. No login, no CAPTCHA or anti-bot bypass, no proxies.
- Companies are user-supplied. The Actor only reads boards you name; it does not discover or crawl companies on its own.
- No personal data beyond the posting. Rows contain only what the job posting publishes. No recruiter names, emails or phone numbers are extracted, even when a description mentions them.
- Hard caps: at most 100 companies and 1000 jobs per company per run (defaults 20 and 200). For Greenhouse, Lever and Ashby the whole board arrives in one response, so the cap is applied after your filters and
maxJobsPerCompany: 200means at most 200 matching jobs. For SmartRecruiters the same cap also bounds how many postings are fetched while it paginates (100 per request), so a highly selective filter on a large SmartRecruiters board can return fewer rows than the cap — raisemaxJobsPerCompanythere if you need deeper coverage. - Truncation is visible, never silent. Every job row carries
totalJobs(postings the ATS reports for the board) andtruncated(truewhen the cap cut that board short), and the run's status message names how many boards were truncated. A live Stripe Greenhouse board is ~720 jobs, so with the defaultmaxJobsPerCompany: 200you get 200 rows flaggedtruncated: true— raise the cap to fetch the rest. Filtered boards are covered too: if the cap stopped the SmartRecruiters fetch, the row(s) that did match are still stampedtruncated: true(a 5025-posting board filtered down to 21 rows says so on every row) —truncated: falsemeans your run genuinely covered the whole board. - Companies beyond
maxCompaniesare reported, not dropped silently. Each one yields a single free row witherror: "SKIPPED_MAX_COMPANIES", and the count appears in the status message. Reordercompaniesor raisemaxCompanies(hard limit 100) to include them. - Field coverage differs by ATS. Greenhouse publishes no remote flag and no employment type, so those are
null, and it has no separate direct-apply URL (applyUrlisnull,jobUrlis the board page). Lever publishes no description HTML update time; Ashby and Lever publish noupdatedAt. SmartRecruiters' public list endpoint has no description, sodescriptionTextisnullthere. remoteOnly: truecannot match Greenhouse rows — Greenhouse never publishes remote status, so every Greenhouse job is excluded. That is counted and reported in the run status message (it is not a silent empty result); dropremoteOnlyor use keywords/locations for those boards.- SmartRecruiters paginates 100 postings per request, up to your
maxJobsPerCompany. Its unknown-company response is an empty list rather than an error, so a typo in a SmartRecruiters slug yields no rows and no error row. - Descriptions are truncated at 2000 characters to keep rows small; the full posting is always at
jobUrl. - Politeness: boards are fetched with concurrency 3 and retried with exponential backoff (500 ms, then 1 s) on HTTP 429/5xx. SmartRecruiters pagination (100 postings per request, up to your
maxJobsPerCompany) waits 250 ms between pages instead of hitting the API back-to-back. Each board is fetched at most a handful of times per run. - Listings change constantly;
fetchedAttells you exactly when a row was read.