Job Listings from Career Pages (Greenhouse, Lever, Ashby)
Pricing
from $1.00 / 1,000 job postings
Job Listings from Career Pages (Greenhouse, Lever, Ashby)
Read any company's open jobs straight from the applicant-tracking system behind its careers page. One schema across Greenhouse, Lever and Ashby, with salaries parsed into numbers. No login, no scraping, no LinkedIn.
Pricing
from $1.00 / 1,000 job postings
Rating
0.0
(0)
Developer
Ground Truth
Maintained by CommunityActor stats
0
Bookmarked
1
Total users
0
Monthly active users
5 hours ago
Last modified
Categories
Share
Job Listings from Company Career Pages (Greenhouse, Lever, Ashby)
Read any company's open jobs straight from the applicant-tracking system its careers page runs on, in one consistent schema, with salaries parsed into numbers.
Two ways in. Search 7,500 companies by name, or paste a careers URL.
company_search: "anthropic" -> finds the board, reads every open roleboards: ["https://boards.greenhouse.io/stripe", "ashby:ramp", "spotify"]
And two ways to run it:
| mode | returns | good for |
|---|---|---|
all | every open job | a one-off pull, a first sync |
new | only postings you have not been sent before | running it every morning |
In new mode a quiet day costs you nothing but the Actor start, because you
are charged per job returned and there are none. That is what makes this
affordable as a daily feed rather than a one-off query.
The directory: 7,500 companies, and why it has to exist
Greenhouse, Lever, Ashby and Workable all publish open, documented job board
APIs. All four are useless unless you already know the company's board
name — that Anthropic's is anthropic, that Ramp's is ramp, that Mistral's
is mistral.ai.
None of them publishes a directory of its customers. Checked 2026-08-24:
sitemap.xml on job-boards.greenhouse.io, boards.greenhouse.io,
jobs.lever.co and apply.workable.com all 404 or redirect. And no vendor could
publish one anyway, because no vendor knows about the others.
So this Actor ships one: 7,500 boards, every one confirmed against the
vendor's own API, with the date it was confirmed. company_search uses it,
and it also means a bare company name resolves instantly instead of costing
three board downloads while the tool guesses which system you meant.
Why this and not a LinkedIn or Indeed scraper
Because a job aggregator built on scraping breaks, and this does not.
Greenhouse, Lever and Ashby each publish a job board API so that third parties can build job sites from their customers' openings. Lever's own documentation says it in one line: "This API is designed to help you create a job site." Ashby's says "if you host your own careers page, you can use this data to populate it."
So there is no bot protection to fight, no login, no cookies to keep alive, no rate-limit arms race, and nothing that can be switched off because it was never sneaking in. The data comes from the employer, first-hand, the moment they publish it, which is also before it reaches the big job boards.
SOURCES.md records the vendors' exact words, their crawl directives, and the
date each was checked.
SmartRecruiters is not covered, on purpose. Its API tells crawlers
User-agent: LinkedInBot / Allow, then User-agent: * / Disallow. It permits
LinkedIn by name and refuses everyone else, so this tool does not read it. It
seemed worth saying out loud which door is shut.
Covered
| System | Who is on it | Reads |
|---|---|---|
| Greenhouse | Stripe, Figma, Airbnb, Cloudflare, Databricks, Dropbox | title, department, location, dates, and pay where the board has pay transparency on |
| Lever | Spotify, Palantir | title, department, team, employment type, workplace type, ISO country, date |
| Ashby | Ramp, OpenAI, Linear, Vercel | everything above plus city, remote flag, and structured pay |
Workable and Personio are next; both endpoints are verified and open.
Salaries, parsed properly
Ashby prints pay as "$211.4K - $290.6K". Greenhouse prints it in cents,
sometimes as several bands at once. Neither is filterable.
You get:
"salary_min": 211400.0,"salary_max": 290600.0,"salary_currency": "USD","salary_text": "$211.4K - $290.6K"
The parser refuses more often than it guesses, and that is the design:
$50 - $80 / hourgives no salary. An hourly rate is a different quantity and reporting it as an annual figure would be a confident lie.600 000 krgives no salary.kris Swedish, Norwegian and Danish.0.5% – 1.75% • Offers Equitygives no salary. Equity is not pay.- A posting with several bands in different currencies is never spanned. Airbnb prints a German band in EUR beside a US band in USD; taking the minimum of one and the maximum of the other would invent a range that exists nowhere.
salary_text always carries the original string, so nothing is lost when the
parser declines.
Every parsed figure is tested against the employer's own structured
baseSalary block, published for Google to index. Not against a number
someone typed into a test.
Output
One record per job, identical fields from every system:
| Field | Meaning |
|---|---|
title | Job title |
company | Employer. Greenhouse gives a display name; Lever and Ashby give the board name |
ats, board | Which system and which board this came from |
job_id | The posting's id on that system |
department, team | As the employer files it |
employment_type | full_time, part_time, contract, temporary, internship, apprenticeship, volunteer, other |
workplace_type | on_site, remote, hybrid, unknown |
is_remote | Boolean, where the system states one |
location_raw, location_city, location_country | Location as written, and split where the system splits it |
salary_min, salary_max, salary_currency, salary_text | See above |
posted_date, updated_date | ISO dates |
first_seen | The date this feed first saw the posting. Only in new mode, and the one fact the vendors cannot give you: they publish when a job was posted, not when you first heard of it |
description_text | Full description, only if you ask for it |
apply_url | Where a candidate applies |
verify_url | The employer's own posting. Open it and check any field |
Employment and workplace types are normalised to a single vocabulary. Three systems use about thirty words between them for eight real distinctions, and somebody comparing boards needs eight buckets, not thirty.
Input
| Field | Meaning |
|---|---|
company_search | A company name to look up in the built-in directory. |
boards | One entry per company. A careers URL, or system:name, or just the board name. Up to 500. |
mode | all, or new for only what has appeared since your last run. |
feed_id | Names your feed, so two schedules do not consume each other's "already seen". |
max_boards | Caps how many search matches are read. Default 25. |
limit | Total jobs across every company. 1 to 50,000. You are charged per job. |
include_description | Off by default. A description is most of a record's size and you pay per record. |
A bare company name is now free to resolve if it is one of the 7,500 in
the directory. If it is not, the tool falls back to asking each system in turn,
which costs a whole board download per system tried. greenhouse:stripe is
always exactly one request.
What it does not collect
No personal data, and that took two pieces of code rather than one rule.
Recruiter names and contact details are never mapped. That part is the schema.
The harder part is prose: job descriptions genuinely say things like "questions? email careers@acme.com", and a column of those is a marketing list. So email addresses and phone numbers are stripped out of every description before it is published, whether you asked for descriptions or not.
Unlisted Ashby postings are dropped, because Ashby's documentation says plainly that they should not be shown publicly.
Reliability
One company failing does not lose the rest. A run of 200 companies where three have closed their boards returns the other 197 and says what happened to the three.
A board with no open jobs returns nothing and the log says so in those words. That is an answer, not a bug, and the tool does not dress it up as a failure.
If a system changes the shape of its response, the run says which system and why. It will never return an empty dataset that reads as "this company is not hiring".