Echo Lab Tools › ATS jobs
Pull open jobs from Greenhouse, Lever, Ashby, Workable, SmartRecruiters and Recruitee in one dataset
The ATS jobs scraper is an Apify Actor that fetches open job postings from the public job board APIs of Greenhouse, Lever, Ashby, Workable, SmartRecruiters and Recruitee and returns them in one normalized JSON schema: title, department, location, remote/hybrid status, structured salary when the employer publishes it, dates and apply links.
What you get
- One schema for six ATS job board APIs; input is
greenhouse:airbnb, a board URL or a bare slug - Structured salary (min, max, currency, period) from Greenhouse, Lever, Ashby and Recruitee when published
- Filters for title keywords, location, remote-only and posting date; optional plain-text descriptions
Price: $1.50 per 1,000 job postings + $0.00005 per runLogin needed: noRuns on: Apify (API, schedule, MCP)
How it works
List the job boards you want, in any of three forms: greenhouse:airbnb, a board URL such as https://jobs.lever.co/spotify, or a bare slug like ramp. A bare slug is tried on Greenhouse, then Lever, then Ashby, and the first populated board wins. Lever's EU API is tried automatically after a 404 on the global one.
The Actor calls each ATS's official, keyless job board API (boards-api.greenhouse.io, api.lever.co, api.ashbyhq.com, apply.workable.com, api.smartrecruiters.com and company recruitee.com APIs), maps every posting into the same fields, applies your filters, and pushes each job as soon as it is ready. Boards that are invalid or fail are skipped without billing and listed in a SUMMARY record with the reason. Recruiter and hiring-manager contact fields are never copied, and emails and phone numbers in descriptions are redacted.
Sample output
One row from a real run on the Apify platform (run I1xMk6IPKPAjd4QsD, build 0.1.5, 2026-11-06), which pulled 25 postings each from Airbnb's and Stripe's Greenhouse boards:
{
"ats": "greenhouse",
"company": "airbnb",
"companyName": "Airbnb",
"jobId": "8306554",
"title": "AirCover Specialist, Claims Experience",
"department": "Community Support",
"team": null,
"employmentType": null,
"workplaceType": "remote",
"isRemote": true,
"location": "Canada",
"allLocations": [
"Canada"
],
"country": null,
"salaryMin": 58000,
"salaryMax": 68000,
"salaryCurrency": "CAD",
"salaryPeriod": "year",
"salaryText": "Canada Annual Pay Range",
"publishedAt": "2026-10-28T14:52:05Z",
"updatedAt": "2026-10-28T14:52:05Z",
"jobUrl": "https://careers.airbnb.com/positions/8306554?gh_jid=8306554",
"applyUrl": "https://careers.airbnb.com/positions/8306554?gh_jid=8306554",
"scrapedAt": "2026-11-06T04:23:16.670356Z"
}
What the 50 rows of that run looked like:
- Salary: 20 of 25 Airbnb postings carried a structured pay range; 0 of 25 Stripe postings did. Salary coverage is the employer's choice, not the API's.
- Salary period: 13 rows had a range but no period, because Greenhouse only gives a free-text range title and the period is inferred from it ("annual", "hourly"…) only when the title says so.
- Work type: 23 remote, 5 hybrid, 22 unknown. Greenhouse has no standard remote field, so it comes from the board's own "Workplace Type" field or a "remote" location.
- The whole run took 5 seconds at 256 MB.
What each ATS provides
This is the part most job scrapers skip. The six APIs expose different fields; the table shows what our normalizer can fill for each. Individual employers may still leave fields empty.
| ATS | Department | Team | Employment type | Remote / hybrid | Structured salary | Extra locations |
|---|---|---|---|---|---|---|
| Greenhouse | ✓ | — | — | Only via the board's "Workplace Type" field or a "remote" location | ✓ when the board publishes pay ranges | — |
| Lever | ✓ | ✓ | ✓ (commitment) | ✓ | ✓ when set | ✓ |
| Ashby | ✓ | ✓ | ✓ | ✓ (workplace type + remote flag) | ✓ when published | ✓ |
| Workable | if set | — | ✓ | ✓ (telecommuting flag) | — | ✓ |
| SmartRecruiters | ✓ | — | ✓ | ✓ (remote / hybrid flags) | — | — |
| Recruitee | if set | — | ✓ | ✓ (remote / hybrid / on-site) | ✓ when set | ✓ |
Output fields
| Field | Type | Example | Meaning |
|---|---|---|---|
ats, company, companyName |
string | greenhouse, airbnb |
Source ATS, input slug, company name if supplied |
jobId, title |
string | 8306554 |
Source ID and job title |
department, team, employmentType |
string / null | Community Support |
As classified by the source, never guessed |
workplaceType, isRemote |
string / boolean / null | remote, true |
remote, hybrid, onsite or null; remote eligibility |
location, allLocations, country |
string / array | Canada |
Primary and additional locations |
salaryMin, salaryMax, salaryCurrency, salaryPeriod, salaryText |
number / string / null | 58000, 68000, CAD |
Structured pay range and source label |
publishedAt, updatedAt, scrapedAt |
string | 2026-10-28T14:52:05Z |
ISO 8601 UTC |
jobUrl, applyUrl |
string | — | Public posting and application links |
description |
string | — | Only with includeDescription; plain text, max 20,000 characters |
How much does it cost to scrape job postings?
Pricing is per result: $1.50 per 1,000 job postings ($0.0015 per job) plus $0.00005 per run, platform usage included. Failed boards are not billed.
Worked examples: the sample run above saved 50 postings, $0.075. A weekly pull of every opening at 20 companies that average 150 postings each is 3,000 rows, $4.50 per week. A daily alert with postedWithinDays: 1 only bills for postings published in the last day that match (undated postings always pass the filter), so watching 100 boards that post 60 matching jobs a day costs about $0.09 a day. Use maxJobs and maxJobsPerCompany to cap any run. Apify's free plan includes $5 a month for Store tools (apify.com/pricing, checked 2026-11-06).
Limits you should know about
- Six ATS only. Workday, iCIMS, Taleo, LinkedIn and Indeed are not supported: they do not offer the public, keyless job board APIs this tool relies on.
- You need the board slug. The tool reads boards you name; it does not discover which companies use which ATS. Bare slugs are only tried on Greenhouse, Lever and Ashby.
- Salaries come only from structured fields. Nothing is guessed from description text, currencies and periods are not converted, and a Greenhouse period is only set when the range title names it.
- Completeness depends on each employer. Missing departments, dates or salaries are null. Workable's widget API can return an empty board even when the company's other careers page shows openings.
- No cross-run memory. Each run returns the current postings; there is no stored diff between runs.
postedWithinDaysfilters by publish date, which is the practical way to get "new since yesterday".
API: call it from code
curl -X POST "https://api.apify.com/v2/acts/echolab~ats-jobs-scraper/run-sync-get-dataset-items" \
-H "Authorization: Bearer $APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"companies": ["greenhouse:airbnb", "lever:spotify", "ashby:ramp"], "keywords": ["engineer"], "remoteOnly": true, "maxJobsPerCompany": 10}'
Python, keeping only postings with a published salary:
from apify_client import ApifyClient
client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("echolab/ats-jobs-scraper").call(run_input={
"companies": ["greenhouse:airbnb", "lever:spotify", "ashby:ramp"],
"postedWithinDays": 7,
"maxJobsPerCompany": 0, # 0 = no per-board cap
})
for job in client.dataset(run["defaultDatasetId"]).iterate_items():
if job["salaryMin"] is not None:
print(job["companyName"] or job["company"], job["title"], job["salaryMin"], job["salaryMax"], job["salaryCurrency"])
ATS jobs scraper vs. alternatives
| Option | Good for | Trade-off |
|---|---|---|
| This Actor | A known list of companies on these six ATS, normalized into one schema, paid per job | No company discovery; no Workday/iCIMS/Taleo |
| Calling each ATS API yourself | Free and well documented, fine for one ATS | Six schemas, pagination styles and salary formats to map and maintain |
| LinkedIn / Indeed scrapers | Companies without a public ATS board | Behind logins and anti-bot systems; more fragile; terms-of-service risk |
| Commercial job-data feeds | Millions of pre-indexed companies, history, company discovery | Subscription or contract pricing |
When not to use it: if your target companies run Workday or iCIMS, or you need to find every company hiring for a role across the web, use a commercial job-data feed. If you only follow one Greenhouse board, its API is free and simple. This tool fits when you track tens or hundreds of named companies across several ATS and want one clean table.
Open the ATS jobs scraper on Apify for the full input schema.
Changelog
- 2026-11-06 — build 0.1.5: boards that send
"metadata": null(Stripe's Greenhouse board does) no longer lose every posting; a board whose postings all fail to parse now counts as failed instead of OK. - 2026-11-05 — first public release on the Apify Store with pay-per-result pricing ($1.50 per 1,000 jobs).