Greenhouse, Lever, Ashby & Workable Jobs Scraper
Pricing
from $0.90 / 1,000 jobs
Greenhouse, Lever, Ashby & Workable Jobs Scraper
Give it company websites like stripe.com, company names, or career page and job board URLs. It looks up the Greenhouse, Lever, Ashby, Workable, SmartRecruiters or Recruitee board and returns every open job with title, locations, remote flag, salary, description. $1.50 per 1,000 jobs, failures free.
Pricing
from $0.90 / 1,000 jobs
Rating
0.0
(0)
Developer
Gautham Krishna
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
a day ago
Last modified
Categories
Share
Get every open job from a company's own career site, straight from its applicant tracking system.
Paste company websites (stripe.com), company names (Scale AI), career page URLs, job board URLs or board names, and get one row per job with title, department, locations, remote flag, employment type, seniority level, dates, apply link, salary when published, and the full description.
For a website or a name the scraper finds the company's board itself, and every row says which board it used.
It reads Greenhouse, Lever, Ashby, Workable, SmartRecruiters and Recruitee boards. These systems publish a public JSON feed for each company board, so the scraper needs no login, no browser and no proxy.
You pay $1.50 per 1,000 jobs ($0.0015 each), and less on Apify Store discount tiers (Bronze, Silver, Gold), down to $0.90 per 1,000 on Gold. Boards that don't exist, have no open jobs or fail are never charged, and finding a company's board from its website or name costs nothing.
At a glance
| Topic | Details |
|---|---|
| Input | Many companies per run: company websites, company names, career page URLs, job board URLs or board names on Greenhouse, Lever, Ashby, Workable, SmartRecruiters and Recruitee. Filters: keywords, excluded words, locations, remote only, posted within N days, salary listed, minimum salary, seniority (intern, entry level and up) |
| Output | One JSON row per open job: title, department, locations, remote flag, employment type, seniority level, dates, apply link, salary when published and the full description |
| Price | $0.0015 per job ($1.50 per 1,000), or $1.35 per 1,000 on Bronze, $1.10 on Silver and $0.90 on Gold. No start fee. Boards that don't exist, have no open jobs or fail are free, and so are jobs your filters drop |
| Tested | Board URLs: 61 of 61 real boards across all six systems returned jobs, 7,898 in total, in about 3 minutes. Websites and names: 238 real companies in three runs, see Company websites and names |
What you get per job
| Field | What it holds |
|---|---|
status | ok for a job. Other rows say why a board gave nothing, see below |
company, ats, boardSlug | Company name, which ATS hosts the board, and the board name on that ATS |
boardUrl, resolvedBy, resolvedFrom | Only for inputs the scraper resolved (websites, names, career pages, bare board names). The board used, how it was found, and the page that linked to it. Absent for board URLs and ats:name pairs |
jobId, title | The ATS's own job ID and the job title |
department, team | As the company labels them. Not every ATS has both |
location, locations | First location, and every location the job lists |
country | Country, when the ATS gives one |
isRemote, workplaceType | true for remote jobs. workplaceType is remote, hybrid, onsite or empty |
employmentType | Full-time, Part-time, Contract, Internship, Temporary, or the company's own label |
postedAt, updatedAt | ISO dates in UTC. updatedAt only on Greenhouse and Recruitee |
url, applyUrl | The job page and the application form |
salary | { min, max, currency, period, text } when the company publishes pay, otherwise empty |
salaryZones | Greenhouse only, on jobs with pay. Every pay zone the company lists, with its own title, range, currency and period |
descriptionText, descriptionHtml | The full job description as clean text and as the original HTML |
seniority | intern, entry, mid, senior, lead, executive, or empty when the job gives no clear sign. Always the last field, see Seniority filter |
Input
{"companies": ["stripe.com","Scale AI","https://job-boards.greenhouse.io/discord","https://jobs.lever.co/spotify","https://www.notion.so/careers","ramp","workable:skroutz","https://careers.smartrecruiters.com/Wix2"],"keywords": ["engineer", "data"],"locations": ["London", "remote"],"postedWithinDays": 30}
Each line in companies can be any of these.
| You pass | Example |
|---|---|
| A company website | stripe.com, launchdarkly.com, nubank.com.br |
| A company name | Scale AI, Hims & Hers, Lucid Software |
| A job board URL | boards.greenhouse.io/figma, jobs.lever.co/palantir, jobs.ashbyhq.com/openai, apply.workable.com/huggingface, careers.smartrecruiters.com/Canva, bunq.recruitee.com |
| Any job URL on a board | jobs.lever.co/spotify/0eebb462-... reads the whole Spotify board |
| A company career page | notion.so/careers, figma.com/careers, palantir.com/careers |
| A board name | stripe. The scraper checks every ATS for a board with that name, see below |
| A board name with the ATS | greenhouse:stripe, lever:zoox, smartrecruiters:ServiceNow. Faster and never ambiguous |
Board URLs and ats:name pairs are read exactly as given.
Everything else goes through the steps in the next section.
Company websites and names
For a website or career page URL the scraper tries, in order:
- Links to a supported board on the company's own pages: the page you gave, its careers links, and the usual
/careers,/jobs,careers.andjobs.addresses. A page that links many companies' boards, like a job list, counts only for a board with the company's name. - A board named after the domain on each of the six systems, kept only when the company name the ATS reports matches (
stripe.comand "Stripe"). When two such boards exist, the one whose jobs mention the domain wins. - The name the site gives itself, when it differs from the domain (
wikimediafoundation.orgcalls itself "Wikimedia Foundation", whose board iswikimedia). - Rarer board names:
doordashusaon Greenhouse,Wix2on SmartRecruiters.
For a company name it tries spellings of the name on each system (scaleai, scale-ai, scale), then the same page search on <name>.com, then the rarer board names.
A board counts only when the company name the ATS reports matches yours, ignoring case, spaces and words like Inc, Ltd or Labs.
A bare word such as stripe is first read as a board name.
A board whose company has that name wins.
Otherwise the first board with jobs in the order Greenhouse, Lever, Ashby, Workable, SmartRecruiters, Recruitee is used, as before, even when its company has another name, so check company on the rows.
When several boards match, the one with at least twice the jobs of the next is used.
When two are closer than that, you get a free ambiguous row that lists up to 5 of them with their board URLs, and no jobs, so you can pass the one you want.
Workable, SmartRecruiters and Recruitee have free accounts, and many sit dormant under big names, such as a SmartRecruiters board "Apple2" whose one job dates from 2016.
A board on those three whose newest job is over a year old counts as empty.
What happened, measured on 238 real companies in three runs, given by website and by name, after the last change to how boards are found:
| Run | Companies | Jobs returned | Right board | Wrong board | Board exists, not found | Board exists, no open jobs | Unsupported system | Could not tell |
|---|---|---|---|---|---|---|---|---|
2xhrfhszjWElHXgtD | 87 | 57 | 57 | 0 | 4, one of them ambiguous | 1 | 22 | 3 |
IbqbOw4k0nf9ZiwQM | 79 | 50 | 50 | 0 | 2 | 0 | 14 | 13 |
emHhoiwCAXiPXAJfF | 72 | 38 | 37 | 1 | 6 | 0 | 14 | 14 |
Of the 157 companies whose board on one of the six systems we confirmed, the scraper found the right one for 144 (92%), and 93%, 96% and 84% per run.
"Unsupported system" counts companies that hire through Workday (16), Phenom (9), iCIMS (5), Eightfold (4), SuccessFactors (3) and others.
The scraper named the system in a free unsupported_ats row for 30 of those 50.
For the other 20 the system shows only after the page's JavaScript runs, or the company runs its own job site.
One more unsupported_ats row was wrong: remote.com's job list links another company's Personio board.
"Could not tell" counts companies where we found no board on any system by hand either.
The misses were boards only JavaScript reveals (digitalocean98 and ada18 on Greenhouse, an Ashby board named by a UUID), companies renamed or acquired (Superhuman, Auth0, Weights & Biases), boards named differently from the company (oscar for Oscar Health, montecarlodata for Monte Carlo), and one board named careers.
The wrong board was Hetzner, a bare word, which matched a Recruitee board named hetzner that belongs to another company, shown as company: xneelo.
Filters
| Filter | What it does |
|---|---|
keywords | Keep jobs whose title, department or team contains any of these |
excludeKeywords | Drop jobs whose title contains any of these, for example senior or intern |
locations | Keep jobs whose location or country contains any of these. remote matches every remote job |
remoteOnly | Keep only remote jobs |
postedWithinDays | Keep only jobs posted in the last N days |
hasSalary, minSalary | Keep only jobs that list pay, or pay that reaches a minimum, see Salary filter |
seniority | Keep only jobs at these levels, such as intern and entry, see Seniority filter |
onlyNewSinceLastRun | Keep only jobs this scraper has not returned to you before, see below |
maxJobsPerCompany | Stop after this many matching jobs per board |
includeDescription | Turn off for a smaller dataset. SmartRecruiters boards also run faster without it |
Filters run before charging, so jobs they drop cost nothing.
Salary filter
Both salary filters are off by default and read the salary the board itself sends.
| Input | What it does |
|---|---|
hasSalary | Keep only jobs that list a pay amount |
minSalary | Keep jobs whose listed pay reaches this amount. The top of the range counts, so a job paying 120,000 to 160,000 passes a minimum of 150,000 |
salaryCurrency | The currency of minSalary, USD unless you set it |
salaryPeriod | YEAR, MONTH, WEEK, DAY or HOUR, YEAR unless you set it |
keepJobsWithoutSalary | With minSalary, also keep jobs that list no pay in that currency and period |
Amounts are never converted.
With minSalary of 150,000 USD per year, a job paid in CAD or GBP, or per hour, counts as having no salary.
Jobs with no salary are dropped, unless you turn on keepJobsWithoutSalary.
A board where no job passes gives a free no_jobs row that says so.
How much pay the boards list, measured on 61 real boards with 7,935 open jobs (run D84UCSK0PMuLLr1gX, no filter):
| System | Jobs | List a pay amount |
|---|---|---|
| Ashby | 1,956 | 1,203 |
| Lever | 1,012 | 307 |
| SmartRecruiters | 3,158 | 423 |
| Greenhouse | 1,600 | 919 |
| Workable | 187 | 0 |
| Recruitee | 22 | 0 |
Workable boards send no pay, so the salary filter drops all of their jobs. Many companies write pay into the description instead, such as Notion on Ashby and Brex on Greenhouse, and the filter does not read descriptions.
Greenhouse pay zones
Greenhouse lists pay per zone, such as one range for New York and a lower one for Denver.
salary spans every zone in the first zone's currency, from the lowest minimum to the highest maximum, and text holds the zone titles.
salaryZones lists every zone as Greenhouse sends it.
Greenhouse has no period field, so period is set only when the zone titles name exactly one period, as in "Annual Base Salary" or "Base Pay Range (hourly)".
Otherwise it is empty, and minSalary treats the job as having no salary, because its period is unknown.
The text around a range is not read for this, because on 88 boards its period words were mostly about bonuses and stock grants.
Measured on 88 Greenhouse boards with 10,225 open jobs (run qulMDQ0C9d9IkHB0b), 2,435 jobs on 36 boards list pay.
Their titles name the year for 449, the hour for 136 and the month for 1, and no period for 1,849.
So hasSalary kept 2,435 jobs (run nUOBNbtUFCxljij4o), and minSalary: 150000 kept 348 (run 8xcK72NJIR43cpfNg).
We checked 20 of those jobs against their Greenhouse job pages by hand, and every zone range matched the page.
A few companies label a range wrongly, such as an "Annual Base Salary" of $35 to $58, and the rows keep what the company sent.
This is real output from run qulMDQ0C9d9IkHB0b, shortened.
{"status": "ok","input": "greenhouse:robinhood","company": "Robinhood","ats": "greenhouse","boardSlug": "robinhood","jobId": "6669758","title": "Android Engineer, Government Products","department": "ENG Infrastructure","team": null,"location": "New York, NY","locations": ["New York, NY"],"country": null,"isRemote": false,"workplaceType": null,"employmentType": null,"postedAt": "2025-05-08T18:52:01Z","updatedAt": "2026-10-02T22:48:15Z","url": "https://boards.greenhouse.io/robinhood/jobs/6669758?t=gh_src=&gh_jid=6669758","applyUrl": "https://boards.greenhouse.io/robinhood/jobs/6669758?t=gh_src=&gh_jid=6669758","salary": {"min": 130000,"max": 195000,"currency": "USD","period": null,"text": "Zone 1 (Menlo Park, CA; New York, NY; Bellevue, WA; Washington, DC); Zone 2 (Denver, CO; Westlake, TX; Chicago, IL); Zone 3 (Lake Mary, FL; Clearwater, FL; Gainesville, FL)"},"salaryZones": [{"title": "Zone 1 (Menlo Park, CA; New York, NY; Bellevue, WA; Washington, DC)","min": 166000,"max": 195000,"currency": "USD","period": null},{"title": "Zone 2 (Denver, CO; Westlake, TX; Chicago, IL)","min": 147000,"max": 172000,"currency": "USD","period": null},{"title": "Zone 3 (Lake Mary, FL; Clearwater, FL; Gainesville, FL)","min": 130000,"max": 152000,"currency": "USD","period": null}],"descriptionText": "Join us in building the future of finance.\n\nOur mission is to democratize finance for all. An estima...","descriptionHtml": "<div class=\"content-intro\"><h2>Join us in building the future of finance.</h2>\n<p>Our mission is to ..."}
On SmartRecruiters, pay comes from the same extra request per job as the description, so a salary filter makes those boards slower even with includeDescription off.
With minSalary: 150000 (run DWdnqZebGdQH2Hb3J) the scraper kept 1,763 jobs from 23 boards, every one paid in USD per year with a range that reaches 150,000.
Before Greenhouse pay was added, we checked 20 kept and 20 dropped jobs on the other systems against their job pages by hand, and all 20 kept jobs showed the same range on their page.
Turning the filters off gives the rows the scraper gave before they existed, except that Greenhouse jobs with pay now carry it.
This is real output from run khMgPJqa0fyXPNgfv on Ashby, shortened.
Its range starts below 150,000 and reaches above it, so it passes.
{"status": "ok","input": "ramp","company": "Ramp","ats": "ashby","boardSlug": "ramp","jobId": "4e64ab86-4e30-403b-b1b9-41dc052570ce","title": "Software Engineer, Frontend","department": "Engineering","team": "Frontend","location": "New York, NY (HQ)","locations": ["New York, NY (HQ)", "Remote (Canada)", "San Francisco, CA", "Remote (US)", "Miami, FL"],"country": "USA","isRemote": false,"workplaceType": "hybrid","employmentType": "Full-time","postedAt": "2023-03-09T17:44:00Z","updatedAt": null,"url": "https://jobs.ashbyhq.com/ramp/4e64ab86-4e30-403b-b1b9-41dc052570ce","applyUrl": "https://jobs.ashbyhq.com/ramp/4e64ab86-4e30-403b-b1b9-41dc052570ce/application","salary": { "min": 143200, "max": 284000, "currency": "USD", "period": "YEAR", "text": "$143.2K - $284K" },"descriptionText": "About Ramp\n\nRamp is building the smart infrastructure for finance teams, embedded in the transaction flow of every dolla...","descriptionHtml": "<h1><strong>About Ramp</strong></h1><p style=\"min-height:1.5em\">Ramp is building the smart infrastructure for ...","boardUrl": "https://jobs.ashbyhq.com/ramp","resolvedBy": "board_name","resolvedFrom": null}
A Workable board gave this free row in run DWdnqZebGdQH2Hb3J.
{"status": "no_jobs","input": "https://apply.workable.com/huggingface/","ats": "workable","boardSlug": "huggingface","message": "Board has 6 matching jobs, none with a salary that passes your salary filter. This scraper reads no salary from Workable boards, so none of their jobs lists one"}
With onlyNewSinceLastRun, the salary filter is part of the filter set, so turning it on starts a new memory for that board.
Runs without it keep the memory they had.
Seniority filter
Every job row ends with seniority, one of intern, entry, mid, senior, lead or executive, or empty when the job gives no clear sign.
The seniority filter keeps only the levels you list, and it is off by default.
| Input | What it does |
|---|---|
seniority | Keep jobs at these levels. ["intern", "entry"] finds internships, new grad and junior roles |
keepJobsWithoutSeniority | With seniority set, also keep jobs whose level is empty |
The scraper reads the level in this order, and the first answer wins.
| Step | What it reads | Examples |
|---|---|---|
| 1 | Level words in the title | Intern, Co-op, Working Student, Stage, New Grad, Graduate, Junior, Entry Level, Senior, Sr., Staff, Principal, Lead, Supervisor, Engineering Manager, Director, VP, Head of, Chief |
| 2 | The board saying intern or early career | An Internship employment type, an Internships, Early Career or University department |
| 3 | The years of experience the description asks for | 0 or 1 year is entry, 2 to 4 is mid, 5 or more is senior. Also "this internship" (intern) and "new grad" or "recent graduates" (entry) |
| 4 | A level number or "Associate" in the title | Engineer I is entry, II is mid, III is senior. Sales Associate is entry |
| 5 | The board's own level field | SmartRecruiters and Workable experience level, Recruitee experience, Lever level |
lead covers staff and principal engineers and people managers.
executive covers directors and above.
Titles that only look like levels are skipped, such as Account Executive, Member of Technical Staff, Staff Accountant and University Recruiter.
A title with a range, such as "Senior / Staff" or "Junior - Mid level", gets the lowest level in it.
The board's level field comes last because companies set it loosely: on 8 SmartRecruiters boards, 19 of the 36 "Entry Level" jobs whose title names a level had "Senior" in the title.
Jobs with an empty level are dropped by the filter, unless you turn on keepJobsWithoutSeniority.
On 61 real boards with 7,934 open jobs (run P6vp0RLRT5mbKdPoL, no filter), 1,308 jobs (16%) had no clear level.
Only 772 were intern or entry, so keeping the empty ones would make most rows of an intern search jobs with no level.
| Level | Jobs |
|---|---|
| intern | 317 |
| entry | 455 |
| mid | 1,487 |
| senior | 2,458 |
| lead | 1,506 |
| executive | 403 |
| empty | 1,308 |
With seniority: ["intern", "entry"] on 75 real boards across all six systems (run yeKF2Sv0WnKDsVXem), every board was read with no error.
The scraper kept 1,927 jobs (444 intern, 1,483 entry) from 58 boards, and the other 17 boards each gave a free no_jobs row.
A hand check of 30 kept jobs from 18 boards against their job pages found 28 at the level given and 29 that are intern or entry jobs.
One was a fixed-term program for current university students, labelled entry where intern fits better. The other was a yoga instructor role asking for teaching experience, which only the board's own level field called entry.
On SmartRecruiters, the description comes from one extra request per job, so the seniority filter makes those boards slower even with includeDescription off.
With the filter off and includeDescription off, SmartRecruiters rows get their level without the description.
With onlyNewSinceLastRun, the seniority filter is part of the filter set, so turning it on starts a new memory for that board.
Runs without it keep the memory they had.
Monitoring: only new jobs
Turn on onlyNewSinceLastRun and schedule the run, for example daily.
The first run returns every matching job and remembers them.
Each later run returns only jobs it has not returned to you before for the same board and filters, so you pay only for new postings.
A job that a company re-posts under a new ID counts as new.
A board with nothing new comes back as a free no_jobs row.
The memory is a key-value store named career-site-jobs-scraper-monitor in your own Apify account, one record per board and filter set.
Delete that store to start over.
Output example
This is real output for a Lever board URL from run P6vp0RLRT5mbKdPoL, with the description shortened here.
{"status": "ok","input": "https://jobs.lever.co/aircall","company": "aircall","ats": "lever","boardSlug": "aircall","jobId": "0fbc9b05-520b-4cbb-a276-5a9a75e8f3bf","title": "Regional Marketing Intern - NA Market","department": "Marketing","team": "11003 - Demand Generation","location": "San Francisco Office","locations": ["San Francisco Office"],"country": "US","isRemote": false,"workplaceType": "hybrid","employmentType": "Internship","postedAt": "2026-01-14T18:14:31Z","updatedAt": null,"url": "https://jobs.lever.co/aircall/0fbc9b05-520b-4cbb-a276-5a9a75e8f3bf","applyUrl": "https://jobs.lever.co/aircall/0fbc9b05-520b-4cbb-a276-5a9a75e8f3bf/apply","salary": { "min": 20, "max": 22, "currency": "USD", "period": "HOUR", "text": "The hourly rate is not including benefits, including health insurance and public transit + gym reimbursement." },"descriptionText": "Aircall is a unicorn, AI-powered customer communications platform used by 22,000+ companies worldwide to drive revenue, resolve issues faste...","descriptionHtml": "<div><span style=\"font-size: 16px;\">Aircall is a unicorn, AI-powered customer communications platform used by 22,000+ co...","seniority": "intern"}
This is real output for the board name ramp from the same run, shortened the same way.
boardUrl, resolvedBy and resolvedFrom say which board the scraper used and how it found it.
{"status": "ok","input": "ramp","company": "Ramp","ats": "ashby","boardSlug": "ramp","jobId": "fc971889-db1d-4a20-a25e-f282f9296936","title": "University Grad | Customer Experience Associate","department": "Customer Experience","team": "Customer Experience","location": "New York, NY (HQ)","locations": ["New York, NY (HQ)", "San Francisco, CA", "Remote (US)"],"country": "USA","isRemote": false,"workplaceType": "hybrid","employmentType": "Full-time","postedAt": "2026-09-09T12:19:47Z","updatedAt": null,"url": "https://jobs.ashbyhq.com/ramp/fc971889-db1d-4a20-a25e-f282f9296936","applyUrl": "https://jobs.ashbyhq.com/ramp/fc971889-db1d-4a20-a25e-f282f9296936/application","salary": { "min": 60000, "max": 70000, "currency": "USD", "period": "YEAR", "text": "$60K - $70K" },"descriptionText": "About Ramp\n\nRamp is building the smart infrastructure for finance teams, embedded in the transaction flow of every dollar a business spends....","descriptionHtml": "<h1><strong>About Ramp</strong></h1><p style=\"min-height:1.5em\">Ramp is building the smart infrastructure for finance te...","boardUrl": "https://jobs.ashbyhq.com/ramp","resolvedBy": "board_name","resolvedFrom": null,"seniority": "entry"}
resolvedBy is careers_page_link when a company page linked the board (resolvedFrom is that page), company_name_match when the board's company name matched, or board_name when a bare word matched a board name.
A company on a system this scraper does not read gives one free row, from an earlier test run.
{"status": "unsupported_ats","input": "Nike","ats": "workday","boardSlug": null,"message": "'Nike' uses workday, linked as https://nike.wd1.myworkdayjobs.com. This actor reads Greenhouse, Lever, Ashby, Workable, SmartRecruiters and Recruitee boards only.","boardUrl": "https://nike.wd1.myworkdayjobs.com"}
Two close boards give one free ambiguous row, from run 2xhrfhszjWElHXgtD.
{"status": "ambiguous","input": "Doctolib","ats": null,"boardSlug": null,"message": "Several boards match 'Doctolib' about equally well (none has twice the jobs of the next): Doctolib on Greenhouse, 149 jobs, https://job-boards.greenhouse.io/Doctolib; Doctolib on Ashby, 146 jobs, https://jobs.ashbyhq.com/Doctolib. Pass the board URL you want.","candidates": [{ "ats": "greenhouse", "boardSlug": "Doctolib", "company": "Doctolib", "jobs": 149, "boardUrl": "https://job-boards.greenhouse.io/Doctolib" },{ "ats": "ashby", "boardSlug": "Doctolib", "company": "Doctolib", "jobs": 146, "boardUrl": "https://jobs.ashbyhq.com/Doctolib" }]}
A board that doesn't exist gives one free row too.
{"status": "not_found","input": "greenhouse:thisdoesnotexist999","ats": "greenhouse","boardSlug": "thisdoesnotexist999","message": "Board does not exist (https://boards-api.greenhouse.io/v1/boards/thisdoesnotexist999/jobs answered 404)"}
| Status | Meaning | Charged |
|---|---|---|
ok | One open job | Yes |
no_jobs | The board exists but has no open jobs, or none matched your filters, including the salary and seniority filters | No |
not_found | No such board on that ATS | No |
ats_not_detected | No supported board found for the website, name or page. The message lists the pages read and the board names tried | No |
unsupported_ats | The company's pages link to a system this scraper does not read, such as Workday, iCIMS or SuccessFactors. ats names it and boardUrl is the link found | No |
ambiguous | Several boards match the name about equally well. candidates lists them with their board URLs | No |
invalid_input | The line is not a URL, a name or a board name | No |
error | The ATS kept failing after retries | No |
Use it from AI agents
The scraper works as a tool through the Apify MCP server in Claude, Cursor, VS Code and other MCP clients. An agent can pass a list of companies and a keyword, and get back structured jobs it can rank, summarise or match to a CV.
You can also call it through the Apify API or the Python and JavaScript clients, and schedule it or connect it to Make, Zapier and n8n.
A daily schedule with onlyNewSinceLastRun: true gives you a feed of new jobs at the companies you track.
Pricing
Prices are per 1,000 jobs.
| Event | No discount | Bronze | Silver | Gold |
|---|---|---|---|---|
| Job returned | $1.50 | $1.35 | $1.10 | $0.90 |
| Board not found, empty, filtered out, undetected, unsupported, ambiguous or failed | Free | Free | Free | Free |
Bronze, Silver and Gold are Apify Store discount tiers, and Apify's Pricing tab shows them as Starter, Scale and Business. Apify decides which tier an account is on.
Set a maximum cost per run in the run options. The scraper stops once that limit is reached.
FAQ
How do I find a company's board name?
You usually don't need to: pass the company's website or name and check boardUrl on the rows.
To be certain, open its careers page and click any job.
The address shows the ATS and the board name, for example jobs.lever.co/zoox/... means Lever, board zoox.
A board URL is always read exactly as given.
Why is a field empty for some jobs?
Each ATS publishes different fields, and companies fill in different ones.
Greenhouse has no employment type field of its own, so it only appears when the company adds one.
Salary appears only when the company publishes it in the board's own pay fields.
Workable boards send no pay, so their salary is always empty, even when the description states pay.
How fresh is the data? Every run reads the live board, so you get the jobs open at that moment.
Is a job listed once per location?
No.
A job with several locations is one row, with all of them in locations.
Limitations
- Only Greenhouse, Lever, Ashby, Workable, SmartRecruiters and Recruitee boards. Workday, iCIMS, Taleo and custom career sites are not supported.
- It reads the boards you give it, or the ones it finds for the companies you name. It does not search across all companies.
- Finding a board from a website or name reads only the HTML the site sends. Boards that only JavaScript reveals, boards with numbered names such as
digitalocean98, and companies renamed or acquired are often missed. Pass the board URL for those. - Workable boards give a posting date without a time.
- SmartRecruiters needs one extra request per job for the description, so large SmartRecruiters boards take longer.
seniorityis worked out by fixed rules from the title, the description and the board's level fields, not read from a field every board has. On the 30 hand-checked jobs it was right for 28, and 16% of jobs get no level.- The salary filter reads only the board's pay fields. It never converts currencies or periods, it drops every Workable job, and Greenhouse pay whose zone titles name no period never passes
minSalary.