Job Listings from Career Pages (Greenhouse, Lever, Ashby) avatar

Job Listings from Career Pages (Greenhouse, Lever, Ashby)

Pricing

from $1.00 / 1,000 job postings

Go to Apify Store
Job Listings from Career Pages (Greenhouse, Lever, Ashby)

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

Ground Truth

Maintained by Community

Actor stats

0

Bookmarked

1

Total users

0

Monthly active users

5 hours ago

Last modified

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 role
boards: ["https://boards.greenhouse.io/stripe", "ashby:ramp", "spotify"]

And two ways to run it:

modereturnsgood for
allevery open joba one-off pull, a first sync
newonly postings you have not been sent beforerunning 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

SystemWho is on itReads
GreenhouseStripe, Figma, Airbnb, Cloudflare, Databricks, Dropboxtitle, department, location, dates, and pay where the board has pay transparency on
LeverSpotify, Palantirtitle, department, team, employment type, workplace type, ISO country, date
AshbyRamp, OpenAI, Linear, Verceleverything 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 / hour gives no salary. An hourly rate is a different quantity and reporting it as an annual figure would be a confident lie.
  • 600 000 kr gives no salary. kr is Swedish, Norwegian and Danish.
  • 0.5% – 1.75% • Offers Equity gives 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:

FieldMeaning
titleJob title
companyEmployer. Greenhouse gives a display name; Lever and Ashby give the board name
ats, boardWhich system and which board this came from
job_idThe posting's id on that system
department, teamAs the employer files it
employment_typefull_time, part_time, contract, temporary, internship, apprenticeship, volunteer, other
workplace_typeon_site, remote, hybrid, unknown
is_remoteBoolean, where the system states one
location_raw, location_city, location_countryLocation as written, and split where the system splits it
salary_min, salary_max, salary_currency, salary_textSee above
posted_date, updated_dateISO dates
first_seenThe 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_textFull description, only if you ask for it
apply_urlWhere a candidate applies
verify_urlThe 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

FieldMeaning
company_searchA company name to look up in the built-in directory.
boardsOne entry per company. A careers URL, or system:name, or just the board name. Up to 500.
modeall, or new for only what has appeared since your last run.
feed_idNames your feed, so two schedules do not consume each other's "already seen".
max_boardsCaps how many search matches are read. Default 25.
limitTotal jobs across every company. 1 to 50,000. You are charged per job.
include_descriptionOff 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".