ATS Job Listings API Scraper
Pricing
from $1.50 / 1,000 job records
ATS Job Listings API Scraper
Query an index of 1,639 verified company job boards — 60,983 open roles across Greenhouse, Lever, Ashby, SmartRecruiters, Recruitee and Workable — no company list, no login, no API key. Filter by title, location, remote and posting date: every remote senior backend role posted this week, in one run.
Pricing
from $1.50 / 1,000 job records
Rating
0.0
(0)
Developer
K O
Maintained by CommunityActor stats
0
Bookmarked
5
Total users
4
Monthly active users
a day ago
Last modified
Categories
Share
Job Postings Aggregator — Six ATS Platforms, One Schema
No company list needed. Pick a verified company bundle - top tech, AI/ML, devtools, fintech, YC-backed or remote-first - and hit Start. Every company in a bundle was checked against its live board, so you never have to research a board slug to get a result.
Covers six ATS platforms in one feed — Greenhouse, Lever, Ashby, SmartRecruiters, Recruitee and Workable — twice the board coverage of the nearest combo-ATS listing on the Store (which covers three: Greenhouse, Lever and Ashby). Filters run before you're charged: titleIncludes, locationIncludes and remoteOnly are applied inside the fetch loop, before any record reaches the dataset or a billable event, so you only pay for postings that actually match.
Pain points
- Each ATS has its own API, its own field names, its own quirks — Lever hands you epoch milliseconds, Recruitee a non-standard timestamp, SmartRecruiters pages by offset and nests every value behind
{label}. Wiring up even three or four of these yourself is real integration work. - Tracking a target-company list across more than one ATS means running several separate scrapers and merging their output into one schema yourself.
- Filtering after the fact — pulling everything, then throwing away what you didn't want — means paying for records you never use.
- Watching a list of companies for genuinely new roles across multiple platforms means storing what you already pulled, per platform, and diffing every run yourself.
What we solve
- One schema, six ATS platforms. Greenhouse, Lever, Ashby, SmartRecruiters, Recruitee and Workable all normalize to the same 12-field record — no per-platform integration work.
- Filters run before billing.
titleIncludes,locationIncludesandremoteOnlyare checked inside the fetch loop, before a record is normalized, pushed, or charged — postings that don't match never cost you anything. - Per-source failure isolation. A source that fails (bad slug, unreachable API) is skipped with a logged warning — the rest of your source list still comes back.
- Dedup built in, namespaced correctly.
onlyNewSinceLastRun(default on) tracks the last 20,000 ids seen, keyed byats:company:id— so the same numeric id from two different platforms, or two different companies, never collides or gets silently skipped.
Who uses it
Recruiters and sourcers tracking a list of target companies, niche and aggregator job boards, sales teams watching accounts for hiring signals, and labor-market/HR-tech products that need one consistent jobs schema instead of five or six different APIs.
Company bundles - no company list needed
You do not need to know a single board slug to run this. Pick a bundle in presetLists, hit Start, and you get live postings straight away.
| Bundle | Boards | What it covers |
|---|---|---|
top-tech | 83 | Large, well-known technology employers. |
ai-ml | 75 | AI labs, ML infrastructure and applied-AI companies. |
devtools | 65 | Developer tools, infrastructure, observability and security. |
yc-backed | 62 | Y Combinator alumni, from recent batches to public companies. |
fintech | 56 | Payments, banking, lending, crypto and financial infrastructure. |
remote-first | 54 | Companies that hire remote-first or distributed by default. |
Every slug in a bundle was checked against its live board on 2026-08-24 - a 200 response with at least one open role. Each bundle file carries its own verifiedAt date, and any board that stops answering is reported in the run's FAILED_TARGETS record instead of silently shrinking your results.
Bundles merge with anything you list yourself and are deduped, so you can start from a bundle and add your own companies to it.
Search every board we know - no company list at all
Turn on Search every board we know and you do not supply companies, slugs or bundles. The run queries our index of 1,639 verified boards - 60,983 open roles at the last check on 2026-08-24 - and applies your title, location and remote filters across all of them.
Ask it things a company list cannot answer:
- every remote senior backend role posted this week
- everything matching "machine learning" across every board in the index
- new postings only, across the whole index, on a daily schedule
| ATS | Boards |
|---|---|
| greenhouse | 617 |
| ashby | 615 |
| lever | 224 |
| workable | 66 |
| smartrecruiters | 59 |
| recruitee | 58 |
Every board in the index answered its ATS with at least one open role when the index was built. Boards are fetched most-active first and capped by Maximum boards to search (default 200), because each board is one request. The index is rebuilt on a refresh sweep; a board that stops answering three sweeps running is retired from it, and anything that fails during your run is reported in FAILED_TARGETS rather than silently shrinking your results.
Input
{"presetLists": ["top-tech"],"sources": ["greenhouse:stripe", "lever:ro", "ashby:Ramp"],"titleIncludes": ["engineer"],"locationIncludes": ["remote"],"remoteOnly": false}
| Field | What it does |
|---|---|
searchIndex | No company list at all. Query every board in the shipped index and apply your filters across all of them. See the section above. |
maxBoards | Only used with searchIndex. Each board is one request, so this caps run length. Boards are searched most-active first. |
presetLists | Start here if you have no company list. One or more verified company bundles; every board in them was checked against the live API. Merges with anything you list yourself, deduped. |
sources | List of "<ats>:<company>" entries. Supported ats: greenhouse, lever, ashby, smartrecruiters, recruitee, workable. The company is the slug from that ATS's careers URL. E.g. greenhouse:stripe, lever:ro, ashby:Ramp, workable:walter-careers. |
maxPerCompany | Stop after this many jobs from each board, so one large employer cannot fill the whole result. Recommended with bundles. Empty = no cap. |
postedWithinDays | Keep only postings published in the last N days — 7 gives you this week's roles. Postings on boards that publish no date are left out, because they cannot be shown to be recent. Applied before billing. |
titleIncludes | Keep only jobs whose title contains one of these keywords (case-insensitive). Empty = all. Applied before billing. |
locationIncludes | Keep only jobs whose location contains one of these keywords. Empty = all. Applied before billing. |
remoteOnly | Keep only remote-flagged roles (or locations mentioning remote). Applied before billing. |
onlyNewSinceLastRun | Recommended for schedules — skips jobs already returned by a previous run, so you're only charged for genuinely new postings. Default true. |
maxItems | Stop after this many normalized records. |
includeSalary | Fill the salary fields from whichever pay data each source ATS publishes. On by default; costs no extra requests. |
Output
| Field | Description |
|---|---|
jobId | The posting's id in its source ATS. |
ats | Which platform this came from: greenhouse, lever, ashby, smartrecruiters, recruitee, or workable. |
company | The company/account slug you queried. |
title | Job title. |
location | Primary location, mapped from that ATS's own location shape. |
department | Department/team, mapped from that ATS's own tagging. |
employmentType | Employment type, mapped from that ATS's own field. |
remote | Boolean where the source ATS exposes one; null for ATSs (like Greenhouse) with no explicit remote flag. |
postedAt | Posting date, normalized to ISO 8601 regardless of the source's native format. |
url | Public posting URL. |
applyUrl | Direct apply-form URL, where the ATS separates it from the posting URL. |
source | Always "Jobs Aggregator". |
workplaceType | The source ATS's own workplace label, where it publishes one. |
salaryMin / salaryMax | Pay range as plain numbers, so you can sort and filter on it. null when the posting does not state pay — never a guess. |
salaryCurrency | ISO code (USD, EUR, GBP, …) where the posting makes it clear. |
salaryInterval | YEAR, MONTH, WEEK, DAY or HOUR. |
salaryText | The pay exactly as the posting worded it, kept so you can audit the parse. |
salarySource | ats when the board published structured pay, ats-summary or description when it was read out of the posting text. null when no pay was found. |
cityDerived / regionDerived / countryDerived / countryCodeDerived | The location string split into parts, so "Austin, TX" is filterable by state and country instead of by substring. |
locationType | REMOTE, HYBRID or ONSITE. |
seniorityLevel | INTERN, JUNIOR, SENIOR, STAFF, PRINCIPAL, MANAGER, DIRECTOR, VP or EXECUTIVE, read from the title. null when the title carries no signal. |
employmentTypeNormalized | FULL_TIME, PART_TIME, CONTRACT, INTERN, APPRENTICESHIP or VOLUNTEER — one enum across every ATS, so records are comparable. |
daysSincePosted | Whole days since the posting went live, precomputed. |
organizationUrl | The public careers board this posting came from. |
Pay coverage. Pay comes from whichever data each source publishes: structured for Ashby and Recruitee, parsed from the posting body for Greenhouse, Lever and SmartRecruiters. Measured 08/2026 across two Ashby and Lever boards: 174 of 186 postings.
This is a real record, captured live from an Ashby source on 08/21/2026 by running this actor's own client and field-mapping code — not a placeholder:
{"jobId": "34413f8d-26bf-4bbc-8ade-eb309a0e2245","ats": "ashby","company": "Ramp","title": " Security Engineer, Cloud","location": "New York, NY (HQ)","department": "Engineering","employmentType": "FullTime","remote": true,"postedAt": "2026-04-07T17:12:35.753+00:00","url": "https://jobs.ashbyhq.com/Ramp/34413f8d-26bf-4bbc-8ade-eb309a0e2245","applyUrl": "https://jobs.ashbyhq.com/Ramp/34413f8d-26bf-4bbc-8ade-eb309a0e2245/application","source": "Jobs Aggregator","salaryMin": 211400,"salaryMax": 290600,"salaryCurrency": "USD","salaryInterval": "YEAR","salaryText": "$211.4K – $290.6K • Offers Equity","salarySource": "ats","cityDerived": "NY (HQ)","regionDerived": "NY","countryDerived": "United States","countryCodeDerived": "US","locationType": "HYBRID","seniorityLevel": null,"employmentTypeNormalized": "FULL_TIME","daysSincePosted": 136,"workplaceType": "Hybrid","organizationUrl": "https://jobs.ashbyhq.com/Ramp"}
Note what's missing next to the source-specific record: no compensationSummary (Ashby), no departments/offices array or requisitionId (Greenhouse), no workplaceType/allLocations/country (Lever), no requirementsHtml/benefitsHtml/countries (Workable). The unified schema trades platform-specific depth for one consistent shape across all six sources — see Which actor do I want? below if you need that depth back.
Which actor do I want?
- Tracking companies across more than one ATS, or don't want to run five separate scrapers? You're in the right place — this is the only actor in this family that covers all six platforms in one normalized feed.
- Need Greenhouse's multi-department/multi-office tagging or its internal requisition id? Use Greenhouse Jobs Scraper.
- Need Lever's remote/hybrid/onsite distinction, every location tag, or a country field? Use Lever Jobs Scraper.
- Need Ashby's published salary/compensation data? Use Ashby Jobs Scraper — this aggregator fetches Ashby's compensation payload too but drops the field to keep its schema consistent across all six sources.
- Need Workable's description/requirements/benefits split, or its deduplicated country array? Use Workable Jobs Scraper.
- Want the free CRM/Notion/HubSpot/Airtable/Supabase sync feature? It currently ships on the four single-board actors above (
syncEnabledinput), not yet on this one.
Pricing (Pay-Per-Event)
query— charged once per run for the poll across your sources.job-record— charged per normalized job pushed, after filtering. This is the primary event.apify-actor-start(Apify-managed) — covers baseline compute per run.
A daily monitor of a target-account list with a title filter returns just the handful of relevant new roles for pennies — and the filtered-out postings were never billed in the first place.
Finding company slugs
| ATS | Slug is the part of the careers URL |
|---|---|
| Greenhouse | job-boards.greenhouse.io/<slug> |
| Lever | jobs.lever.co/<slug> |
| Ashby | jobs.ashbyhq.com/<slug> |
| SmartRecruiters | jobs.smartrecruiters.com/<slug> |
| Recruitee | <slug>.recruitee.com |
| Workable | apply.workable.com/<slug> |
Source & reliability
All six sources are public ATS job-board APIs — no API keys, no proxies. A source that fails (bad slug, unreachable API) is skipped with a logged warning; the rest of your source list still comes back. SmartRecruiters sources page through up to 1,000 postings per company per run (10 pages × 100); the other five ATSs return each company's full board in a single call. Run npm test for the offline self-test covering the source parser and every ATS's field mapping.
FAQ
How am I charged?
A flat query event once per run, plus one job-record event per normalized job actually pushed — after your titleIncludes/locationIncludes/remoteOnly filters are applied. Postings your filters exclude are never normalized, pushed, or charged. With onlyNewSinceLastRun on (the default), a posting already returned in a previous run (tracked by ats:company:id, for the most recent 20,000 seen) is skipped and not re-charged.
Where does the data come from?
Six public ATS job-board APIs — Greenhouse, Lever, Ashby, SmartRecruiters, Recruitee and Workable — the same data each platform's own hosted careers pages are built from. No API keys or proxies required.
How fresh is it?
Live at request time — there's no bundled or cached dataset behind this actor, it calls each source ATS directly on every run. Schedule it (daily or weekly) to keep catching new postings; onlyNewSinceLastRun means a recurring schedule only bills for roles you haven't seen yet.
Which actor do I want?
See Which actor do I want? above — short version: tracking multiple ATS platforms at once, stay here; need one platform's extra fields (salary, department/office arrays, workplace-type detail, or the description/requirements/benefits split), use that platform's single-board actor instead.
Need the rest of the record on these companies?
This actor returns one source. KYB Company Data Enrichment takes a company name and returns what the public record says about it across up to nine sources — state business registries, UCC lien filings, tax warrants and WARN layoff notices, the FMCSA motor-carrier census, the CMS NPI Registry, USAspending federal awards, EPA ECHO compliance, SEC EDGAR filings and CourtListener federal dockets.
Every enriched field carries its source and its match confidence. Only matches above the accept threshold are merged — anything weaker is surfaced as a suggestion and never folded into the record, because a false join is worse than returning nothing.
SEO keywords: jobs aggregator, multi ATS scraper, Greenhouse Lever Ashby scraper, job listings API, job postings API, new jobs feed, hiring signals, recruiting data, job board aggregator, ATS jobs feed