Career Site Job Scraper – Greenhouse, Lever, Ashby, Workable
Pricing
from $4.80 / 1,000 result delivereds
Career Site Job Scraper – Greenhouse, Lever, Ashby, Workable
Career site jobs scraper that reads the job feed behind an employer's own careers page: Greenhouse, Lever, Ashby, Workable and more. Paste careers pages, board URLs or provider:board pairs and get one row per vacancy - title, department, location, remote, apply link, posting date and pay range.
Pricing
from $4.80 / 1,000 result delivereds
Rating
0.0
(0)
Developer
Locomint
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
3 days ago
Last modified
Categories
Share
This career site jobs scraper reads the job feed behind an employer's own careers page and hands you one row per open role: title, company, department, location, remote flag, employment type, the full description, the apply link, the posting date and the pay range where the employer publishes one. Six applicant tracking systems are supported - Greenhouse, Lever, Ashby, Workable, Recruitee and Personio - and no job board sits in between, so the jobs are the employer's own words on the day you run it.
Give it whatever you have: a company careers page, a board URL, or a provider:board pair.
What it does
Every line of input is turned into one board to read:
- A careers page such as
https://monzo.com/careersis read once and searched for the applicant tracking system behind it. Verified live: it resolves togreenhouse:monzo. - A board URL such as
https://jobs.lever.co/matchgroup,https://jobs.ashbyhq.com/posthog,https://job-boards.greenhouse.io/duolingoorhttps://apply.workable.com/blueground/is recognised from the URL itself, with no request at all. - A
provider:boardpair such asgreenhouse:duolingois used as given.
Duplicates are removed. An entry that repeats another exactly is read once, and where two
different-looking entries turn out to be the same board - ashby:linear and a careers page
that resolves to ashby:Linear - every vacancy is still delivered, and charged for, exactly
once. Anything that is neither a URL nor a known pair is named in the log and skipped.
The board's feed is then read once over a direct datacenter connection - no browser, no proxy, no login - and parsed into one row per job.
| Field | What goes in it |
|---|---|
job_id | The provider's own id for the opening, as a string. Unique within a board. |
title | The job title as posted. |
company | The employer as the feed names them. Some feeds carry no company name, and those fall back to the board token. |
company_domain | The employer's own website, when the row can show it: the careers page you pasted, or an apply link that is not on the platform. null otherwise. |
department | The team or department the feed names. |
location | The primary location, as the employer wrote it. |
locations | Every location this one opening is posted for, primary first. One Greenhouse posting listing three cities comes back as three entries. |
remote | true when the feed says remote, false when it says on-site or hybrid, null when it does not say. Null is not "no". |
employment_type | Full-time, Part-time, Contract, Internship, and so on. |
description | The posting body as readable Markdown. null when you turn descriptions off. |
apply_url | Where a candidate applies - the employer's own link. |
posted_at, updated_at | ISO 8601. updated_at exists on Greenhouse and Recruitee; it is null elsewhere. |
salary_min, salary_max, salary_currency, salary_interval | The published pay range, split into columns. Only ever what the employer stated, never estimated. |
provider, board | Which applicant tracking system, and which board token. |
source_url | The public feed the row was read from. Fetch it yourself and you get the same bytes. |
fetched_at | ISO 8601 UTC, when the feed was read. |
input | The line you typed, so a row can be matched back to your sheet. |
Filters are free
Filters are applied before a job is handed back, so a job you filtered out never appears in your dataset and is never charged for. A run that reads a 795-job board and keeps four nurses in Lisbon costs four jobs.
| Filter | What it keeps |
|---|---|
titleIncludes | Jobs whose title contains one of these words. |
titleExcludes | Drops jobs whose title contains one of these words. |
locationIncludes | Jobs matching one of these in any location the job lists. |
departmentIncludes | Jobs in a matching department. |
remoteOnly | Jobs the employer marks remote, or whose location says remote. |
postedWithinDays | Jobs posted inside that many days. |
Two of them leave out jobs where the feed is silent, and deliberately: a job with no posting date cannot be shown to be from the last seven days, and calling it recent because the field is empty would be inventing the answer. The same goes for a department filter against a job with no department.
A board that gives nothing costs nothing
Only jobs are charged for. A board that no longer exists, one that answered with nothing readable, one that has no vacancies today, a careers page with no feed behind it and a line that turns out to be a board you already named all produce no row at all - each is reported in the run log, with the reason, and costs you nothing.
Which platforms, and what each feed carries
Measured on a real board per provider on 12 September 2026. What a feed omits, the row leaves
null rather than guessing.
| Provider | Board token looks like | Department | Type | Pay range | Description |
|---|---|---|---|---|---|
greenhouse | job-boards.greenhouse.io/monzo → monzo | yes | no | not published | yes |
lever | jobs.lever.co/matchgroup → matchgroup | yes | yes | about half of jobs | yes |
ashby | jobs.ashbyhq.com/vanta → vanta | yes | yes | about half of jobs | yes |
workable | apply.workable.com/blueground → blueground | yes | mostly | rarely | yes |
recruitee | acme.recruitee.com → acme | yes | yes | sometimes | yes |
personio | acme.jobs.personio.de → acme | mostly | yes | rarely | usually |
Greenhouse publishes the department only in the payload that carries descriptions, so turning
descriptions off on a Greenhouse board leaves department empty as well - unless you set
departmentIncludes, which asks for that payload anyway so the filter has something to read, and
then drops the body you said you did not want.
Who it is for
- Job boards and aggregators that want listings from the source rather than from another board, with the employer's own apply link so candidates land in the employer's pipeline.
- Recruiters and agencies watching a list of target employers and seeing what opened this week, filtered to the roles they place.
- Sales and market research teams using hiring as a signal: who is opening five sales roles, which companies publish pay, which are hiring in a market you are entering.
- Compensation research, using only the ranges employers publish themselves.
- AI and data teams who want a clean, typed job dataset with a
source_urlper row that anybody can check.
How to use it
In the Apify Console:
- Paste your boards into Careers pages, board URLs or provider:board pairs, one per line, up to 200 per run.
- Set Max jobs per board. You are charged per job delivered.
- Add filters if you only want some of the jobs. They are free.
- Turn Include the job description off for a much smaller dataset when you only need titles, locations and apply links.
- Start the run and export the dataset as CSV, Excel or JSON.
From the API, this call starts a run, waits for it and returns the rows:
curl -X POST \"https://api.apify.com/v2/acts/locomint~career-site-jobs-scraper/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \-H "Content-Type: application/json" \-d '{"boards": ["greenhouse:monzo", "https://jobs.ashbyhq.com/vanta"], "maxJobsPerBoard": 50}'
The synchronous endpoint waits up to 300 seconds. For a long list of boards, start the run with
POST https://api.apify.com/v2/acts/locomint~career-site-jobs-scraper/runs?token=YOUR_APIFY_TOKEN
and read its dataset when the run finishes. With the Python client (pip install apify-client):
from apify_client import ApifyClientclient = ApifyClient("YOUR_APIFY_TOKEN")run = client.actor("locomint/career-site-jobs-scraper").call(run_input={"boards": ["https://monzo.com/careers", "ashby:vanta", "lever:matchgroup"],"titleIncludes": ["engineer"],"remoteOnly": True,"maxJobsPerBoard": 100,})for row in client.dataset(run.default_dataset_id).iterate_items():print(row["title"], row["location"], row["apply_url"])
Input example
{"boards": ["https://monzo.com/careers","https://jobs.ashbyhq.com/vanta","lever:matchgroup","workable:blueground"],"descriptions": true,"maxJobsPerBoard": 200,"titleIncludes": ["engineer", "developer"],"titleExcludes": ["intern"],"locationIncludes": [],"departmentIncludes": [],"remoteOnly": false,"postedWithinDays": 0,"concurrency": 4,"boardTimeout": 60}
| Field | Default | Allowed | What it does |
|---|---|---|---|
boards | required | 1 to 200 | Careers pages, board URLs or provider:board pairs. Duplicates are removed. |
descriptions | true | true / false | Keep the posting body as Markdown. Off is a much smaller dataset for the same price. |
maxJobsPerBoard | 500 | 1 to 10,000 | Most jobs delivered from each board. |
titleIncludes, titleExcludes | empty | any words | Title filters, applied before a job is charged. |
locationIncludes | empty | any words | Matched against every location a job lists. |
departmentIncludes | empty | any words | Jobs with no department are left out by this filter. |
remoteOnly | false | true / false | Remote jobs only. |
postedWithinDays | 0 (any) | 0 to 3,650 | Jobs with no posting date are left out by this filter. |
concurrency | 4 | 1 to 10 | Boards read at the same time. |
boardTimeout | 60 | 10 to 180 seconds | A board with no answer by then is left out, and not charged. |
Output example
A real row from a run on 12 September 2026. The description is 4,966 characters of Markdown and is cut here.
{"job_id": "dbd4e02c-0c6c-4031-a658-f57e88ec9943","title": "Senior Software Engineer, Developer Experience","company": "vanta","company_domain": null,"department": "Software Engineering","location": "Remote U.S.","locations": ["Remote U.S."],"remote": true,"employment_type": "Full-time","description": "At Vanta, our mission is to help businesses earn and prove trust ...","apply_url": "https://jobs.ashbyhq.com/vanta/dbd4e02c-0c6c-4031-a658-f57e88ec9943/application","posted_at": "2026-03-17T18:17:38.753000+00:00","updated_at": null,"salary_min": 224000.0,"salary_max": 263000.0,"salary_currency": "USD","salary_interval": "year","provider": "ashby","board": "vanta","source_url": "https://api.ashbyhq.com/posting-api/job-board/vanta?includeCompensation=true","fetched_at": "2026-09-12T12:57:29+00:00","input": "ashby:vanta"}
A row from a careers page rather than a board token carries the employer's own site:
{"title": "Senior / Staff Fullstack Engineer","company": "Linear","company_domain": "linear.app","location": "Europe","remote": true,"provider": "ashby","board": "Linear","input": "https://linear.app/careers"}
Pricing
| Event | Price |
|---|---|
| Job delivered (one dataset row) | $0.006, which is $6.00 per 1,000 jobs |
| Actor start | $0.00005 per GB of run memory, charged once per run |
Worked example: 50 employers with about 40 open roles each is 2,000 jobs, so 2,000 x $0.006 = $12.00 plus one start event. Users on paid Apify plans pay less per job.
You pay only these event prices; Apify compute is not billed to you separately. Jobs removed by a filter are not charged. Boards that could not be read, boards with no open jobs and careers pages with no feed behind them produce no rows and cost nothing. If you set a maximum cost per run, no row past that cost is ever delivered or charged for: the run stops there, the log says how many jobs it had found but did not hand over, and the run's status message says the same.
FAQ
Where does the data come from?
From the feed the employer's own careers page uses. Greenhouse, Lever, Ashby, Workable, Recruitee
and Personio each publish one for every board, served to anyone with no key and no login -
Greenhouse documents that no authentication is required. source_url on every row is the exact
address it was read from, so you can check any row yourself.
Why are there no job boards in the list?
Because the big boards block automated requests, and this actor does not work around a block. A site that answers with a challenge or a 403 is backed off from and is never retried through another address. Employers' own feeds are published to be read, which is why they are the whole product here.
My careers page was not recognised. Now what?
Open the page and click through to where the jobs are actually listed, then paste that URL. Pages
that build their job list in the browser with JavaScript have nothing for a plain fetch to find,
and some company sites refuse datacenter connections outright. The board URL always works, and so
does the provider:board pair.
Do I get every job on the board?
Yes, up to maxJobsPerBoard and after your filters. There is no paging to run out of: one board
is one feed, read in one request, and the run log says how many open roles that board actually has
beside how many it kept for you.
Why is the pay range empty?
Because that employer did not publish one. Greenhouse's board feed carries no pay range at all, so every Greenhouse row is null there; on Lever and Ashby about half the jobs carry one. Nothing is estimated or inferred - an empty range means the employer stated none.
Is there any contact information in the rows?
No. There is no recruiter, hiring manager or contact name in any field, and personal contact points are removed from descriptions before a row is written. The apply link is the employer's own application page.
How do I find new jobs since my last run?
Run on a schedule and compare job_id within a board, which is stable while a posting is open.
postedWithinDays narrows a run to recent postings, but only where the feed publishes a date.
Limits
- One request per board.
maxJobsPerBoardcaps the rows, not the fetch: every one of these feeds publishes the whole board as a single document, so it is read in full and then cut. No provider is asked to page - Lever accepts a page size and then caps it at 100 whatever you ask for, which would silently cut a 311-role board down to 100. - 200 boards per run, 10,000 jobs per board, 60 seconds per board by default (10 to 180).
- Detection reads one page, and the
/careerspath when you pass a bare domain. A JavaScript-built careers page, or one that refuses datacenter connections, comes back as no feed found. - Feeds differ. Employment type is missing on Greenhouse,
updated_aton everything but Greenhouse and Recruitee, and some Personio boards publish no description at all. - Board tokens can carry capitals (
ashby:Linear), and the token is used as given. - Closed postings are not in the feed, so a run shows what is open at that moment. Nothing is cached between runs.
- Datacenter connections only. A provider that answers with a challenge or a 403 is not retried from another address; that board is reported in the log and costs nothing.
Compliance
Job postings only, from feeds employers publish for their own careers pages. No field holds a person: there is no recruiter, hiring manager or contact name, and personal contact points are removed from descriptions. You are responsible for using the data lawfully.
Questions, bug reports and feature requests go on this actor's Issues tab. Business owners can send data-removal requests to info@locomint.io; that address is for removal requests only. This actor keeps no copy of what it reads between runs, so each run returns what the public feeds show at that moment.
Other Locomint actors
- Google Maps Scraper & Email Extractor – Business Leads: Search terms and a city in, business records with website contacts out.
- Google Maps Scraper – Multi-City Lead Lists with Emails: Many categories across many cities in one deduplicated run.
- Google Maps Place Details Scraper – Bulk Place ID Lookup: Place IDs or place-page links in, full records out.
- Website Email Scraper – Contact Details, Socials & WhatsApp: Contact points from website URLs you supply.
- Website Content Crawler – Markdown for AI, Emails & Contacts: A whole site as Markdown, with its contact points.
- Bulk Email Verifier & Validator: Checks whether addresses can receive mail.
- Company Enrichment API – Domain to Emails, Socials & Tech: A domain in, its contacts and technologies out.
- AI Crawler Checker – robots.txt Rules for GPTBot & ClaudeBot: Which AI crawlers a site's robots.txt allows.
- Schema Markup Validator & Generator – JSON-LD Checker: Checks and generates schema.org markup.