Greenhouse, Lever, Ashby & Workable Jobs Scraper (Any Company) avatar

Greenhouse, Lever, Ashby & Workable Jobs Scraper (Any Company)

Pricing

from $1.00 / 1,000 jobs

Go to Apify Store
Greenhouse, Lever, Ashby & Workable Jobs Scraper (Any Company)

Greenhouse, Lever, Ashby & Workable Jobs Scraper (Any Company)

Pull every open job from company career pages on Greenhouse, Lever, Ashby and Workable into one clean schema. Paste company URLs or slugs, filter by keyword, location, remote or posting date, and pay only per job returned.

Pricing

from $1.00 / 1,000 jobs

Rating

0.0

(0)

Developer

Nekruz Kasimov

Nekruz Kasimov

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

2 days ago

Last modified

Share

Get every open job from company career pages in one normalized dataset. Give it a list of companies, get back title, location, remote flag, department, employment type, salary where published, posting date, apply link, and optionally the full description. Works for any company whose careers page runs on Greenhouse, Lever, Ashby or Workable, which together host the hiring pages of tens of thousands of companies, from startups to Stripe, Palantir and OpenAI.

No browser, no proxies, no CAPTCHAs: it reads the official public job-board endpoints, so runs are fast, cheap and stable.

What you can do with it

  • Build a job board or newsletter for a niche, a city or an industry, refreshed daily.
  • Track hiring at competitors, customers or target accounts for sales and investment research.
  • Feed AI agents and RAG pipelines with structured job data. The Actor is available over API and MCP.
  • Monitor remote roles or specific titles across hundreds of companies with keyword and location filters.
  • Source candidates' market data for recruiting and salary research.

Input

FieldDescription
companiesOne entry per company. Accepts a careers URL, a platform:slug entry, or a bare slug. Examples: https://boards.greenhouse.io/stripe, https://jobs.lever.co/palantir, https://jobs.ashbyhq.com/openai, https://apply.workable.com/careers, ashby:openai, stripe. A bare slug is tried on all four platforms.
includeDescriptionAdd the full job description as plain text and HTML. Off by default to keep output small.
keywordsKeep only jobs whose title contains one of these words, for example ["engineer", "designer"].
locationsKeep only jobs whose location contains one of these strings, for example ["Remote", "Berlin"].
remoteOnlyKeep only remote jobs.
postedAfterISO date. Keep only jobs posted or updated on or after it. Use it for daily incremental runs.
maxJobsPerCompanyCap per company. 0 means no cap.

Example:

{
"companies": ["https://boards.greenhouse.io/stripe", "lever:palantir", "ashby:openai"],
"keywords": ["engineer"],
"remoteOnly": true,
"postedAfter": "2026-10-01"
}

Output

One dataset item per job:

{
"platform": "greenhouse",
"company": "Stripe",
"companySlug": "stripe",
"jobId": "8172503",
"title": "Abuse Research Engineer",
"url": "https://stripe.com/jobs/search?gh_jid=8172503",
"applyUrl": "https://stripe.com/jobs/search?gh_jid=8172503",
"location": "Remote from the US",
"locations": ["US"],
"country": null,
"isRemote": true,
"workplaceType": null,
"department": "Security Analytics",
"team": null,
"employmentType": null,
"salary": null,
"publishedAt": "2026-09-09T10:52:09-04:00",
"updatedAt": "2026-10-07T19:02:27-04:00",
"descriptionPlain": "Who we are ...",
"descriptionHtml": "<h2>Who we are</h2> ...",
"scrapedAt": "2026-10-12T20:08:49.690Z"
}

isRemote comes from the platform's workplace type where one exists (remote vs hybrid vs on-site), otherwise from its remote flag, otherwise it is inferred from the location text. salary is filled when the board publishes a range (Lever and Ashby support this). Fields that a platform does not provide are null.

The run's key-value store also gets an OUTPUT record with a per-board summary: jobs found on the board, jobs after filters, jobs pushed, and any boards that were not found or failed. It contains:

  • boards: one entry per board or input entry, with input, platform, slug, status, jobsOnBoard (jobs on the board), jobsFetched (jobs downloaded; on Workable this can be fewer when paging stops early at your cap), jobsAfterFilters, jobsPushed, plus descriptionFailures, descriptionsStopped, error or duplicateOf when they apply. descriptionsStopped is true when a Workable board's description requests were given up (see Notes). A duplicate entry carries only input, status, duplicateOf and, where the entry names a board, platform and slug.
  • jobsPushed: jobs saved to the dataset. jobsCharged: jobs billed under pay-per-event pricing, otherwise null.
  • filteredOut: jobs dropped by each filter (keywords, locations, remoteOnly, postedAfter); a job is counted under the first filter it failed.
  • descriptionFailures: jobs saved without a description (see Pricing). descriptionsRecovered: descriptions that failed once and succeeded on the automatic retry.
  • chargeLimitReached: true if the run stopped at your maximum charge per run.

Each board's status is one of:

  • ok: the board was read.
  • partial: a later page of a Workable board failed. Jobs from the earlier pages are kept and error says which page failed.
  • not_found: the careers URL or platform:slug entry points to no board.
  • not_found_on_any_platform: a bare company name matched no board on Greenhouse, Lever, Ashby or Workable.
  • unrecognized: the entry is not a supported careers URL or slug.
  • error: the board request failed after retries; error has the reason.
  • duplicate: the entry is the same board as an earlier one (see Notes), so it was not requested and not charged; duplicateOf is the first input for it. A bare company name that an earlier entry already looked up and did not find is reported the same way.

With includeDescription, a Workable job whose description could not be fetched is still returned, with null descriptions and a descriptionError field (for example HTTP 503, or not requested: 3 descriptions in a row failed (HTTP 503) when the Actor stopped requesting that board's descriptions, see Notes).

Pricing

You pay per job returned. Filtered-out jobs are free, so narrow filters keep costs low. Set a maximum charge per run in the run options if you want a hard cap; the Actor stops cleanly when it is reached (chargeLimitReached is true in OUTPUT).

A board entered more than once is requested and charged once: repeat entries show as duplicate in OUTPUT and cost nothing.

A Workable job whose description could not be fetched is not charged. Failed description requests are retried (rate limits and server errors get one more try after a short pause); if the description is still missing, the job is returned with descriptionError set and counted in descriptionFailures. If the Actor stops requesting a board's descriptions (see Notes), the jobs it skipped are saved the same way, free, with descriptionError: "not requested: ...". jobsCharged shows what was actually billed. Such a job still counts toward maxJobsPerCompany.

Limits and notes

  • Only public, listed jobs are returned. Internal or unlisted postings are not visible on these endpoints.
  • A company that moved from one ATS to another may still have an old, empty board on the previous platform. The OUTPUT summary tells you which board returned how many jobs.
  • Workable descriptions require one extra request per job, so includeDescription is slower for Workable boards.
  • Workable description requests stop on a board after 3 failures in a row (rate limits, server errors or network errors, after their own retries). The remaining jobs wait for the retry after 5 s. If it works, the skipped jobs are fetched; if it fails, the board's descriptions are given up: those jobs are saved free with descriptionError: "not requested: ...", the board's OUTPUT entry gets descriptionsStopped: true, and the final status message adds ", description requests stopped as Workable kept failing".
  • If a later page of a Workable board fails after retries, the board is marked partial in OUTPUT and a warning is logged. The jobs already fetched are kept, and the final status message adds "(1 incomplete, see OUTPUT)", for example Done: 10 jobs from 1 boards (1 incomplete, see OUTPUT). Check OUTPUT for partial boards.
  • Boards are compared by platform and slug. Greenhouse and Ashby ignore case, so greenhouse:Stripe, https://boards.greenhouse.io/stripe/ and boards.greenhouse.io/STRIPE are one board. Lever and Workable slugs are case-sensitive, so another case there is another board. A repeat makes no request and is not charged.
  • A bare company name is probed across the four platforms only once; entering it again, or entering the board it matched as platform:slug or as a URL, is a repeat. Known limit: greenhouse:x followed by a bare x still probes the other three platforms. A repeat of a board that failed is not retried.
  • Careers URLs without https:// (for example jobs.lever.co/palantir) are read as URLs, not as bare slugs.
  • On Workable, a job that appears on two list pages (the board changed while it was being read) is kept once, so jobsFetched and maxJobsPerCompany count it once.
  • A run that saves no jobs says why in its status message and in a warning, for example Done: 0 jobs. 1033 jobs found but all filtered out (1033 by keywords) (see OUTPUT). Other reasons it can give: charge limit reached, board empty, not found, not recognized, failed, or only partly fetched.
  • Rate limits (HTTP 429), server errors (5xx) and network errors are tried up to 3 times. The Actor waits for the Retry-After the server sends (at most 60 s), otherwise 1.5 s, then 3 s.
  • Found a company whose careers page runs on a different ATS? Open an issue on the Actor page and it may be added.

Integrations

Run it on a schedule, then push results to Google Sheets, Airtable, Slack, a webhook or your own database with Apify integrations. Call it from code with the Apify API clients for JavaScript and Python, or let an AI agent call it as a tool through the Apify MCP server.