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
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 matching postings published in the last day (the last-updated date is used when a board gives no publish date, and postings with no date at all always pass), 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 outside the six integrations this tool supports.
- 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, or by last-updated date when there is no publish date, so an older posting that was edited recently can show up again. Dedupe onats+company+jobIdif you need strictly new jobs.
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"])
Use it in Claude, ChatGPT or Cursor (MCP)
Add this URL to any MCP client as a remote (HTTP) server. It loads only this tool plus Apify's run and dataset tools, so your agent calls it by name instead of searching the Apify Store:
https://mcp.apify.com?tools=echolab/ats-jobs-scraper
- Claude Code: run the command below.
- Claude.ai and Claude Desktop: Settings → Connectors → Add custom connector, then paste the URL.
- ChatGPT: add a custom MCP server with this URL and choose OAuth.
- Cursor and other clients: use the JSON config below.
claude mcp add echolab-ats-jobs-scraper "https://mcp.apify.com?tools=echolab/ats-jobs-scraper" -t http
{
"mcpServers": {
"echolab-ats-jobs-scraper": {
"url": "https://mcp.apify.com?tools=echolab/ats-jobs-scraper"
}
}
}
You need an Apify account: the client signs you in with OAuth the first time, or you can send an Authorization: Bearer <APIFY_TOKEN> header if your client cannot do OAuth. The tool shows up as echolab--ats-jobs-scraper. Then ask in plain words, for example: "List the open jobs on Stripe's Greenhouse board and group them by location." Runs are billed to your Apify account at the price above.
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-08 — added a ready-to-paste MCP setup (Claude, ChatGPT, Cursor) that loads this tool by name.
- 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).