# Greenhouse, Lever & Ashby Jobs API + Job Monitor (`quarrywise/greenhouse-lever-ashby-jobs-api`) Actor

Jobs API and job scraper for Greenhouse, Lever and Ashby. Give company names, domains or board URLs, get every open job with pay ranges in one schema, or schedule it as a job monitor that returns only new, updated and closed jobs since the last run.

- **URL**: https://apify.com/quarrywise/greenhouse-lever-ashby-jobs-api.md
- **Developed by:** [Quarrywise](https://apify.com/quarrywise) (community)
- **Categories:** Jobs, Lead generation, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 company checkeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Greenhouse, Lever & Ashby Jobs API + Job Monitor

**Jobs API and job scraper for Greenhouse, Lever and Ashby job boards, plus Recruitee and
Teamtailor: paste a list of companies, get every open job they publish in one clean table, and on
later runs only what changed: new jobs, updated jobs and closed jobs.** An independent tool that
reads the public job-board APIs and feeds of these five systems; it is not made or endorsed by any
of them.

Give it 1 to 1,000 company names, domains or career page URLs. It finds each company's public job
board, normalises titles, departments, locations, remote flags and published pay ranges into one
schema, and can keep a per-watchlist memory so a scheduled run returns only new, updated and
closed jobs. You pay per company checked and per job row saved; companies it cannot find are free.

### Jobs API for Greenhouse, Lever and Ashby job boards

- **Five ATS in one schema:** Greenhouse, Lever, Ashby, Recruitee and Teamtailor job boards,
  auto-detected from a name, a domain or a board URL.
- **Pay ranges** where the employer publishes them (minimum, maximum, currency, interval).
- **Filters:** title keywords (with exclusions such as `-intern`), locations, departments or
  teams, remote only.
- **A per-company report:** which board each company matched, how many jobs it has, how many
  matched your filters, how many rows were saved, and which companies could not be found (free).
- **Personal data kept out:** every text field is screened for email addresses and phone numbers
  in common formats, and links and social handles are removed. Descriptions are off by default; when turned on you get a short excerpt
  only, in which names after recruiter, contact or reporting phrases are also removed on a
  best-effort basis.

#### Greenhouse job board API without a Harvest key

Greenhouse's public Job Board API needs no API key: it lists the open jobs a company publishes on
its Greenhouse career board. This Actor reads it for every company in your list, with departments
and pay ranges, and turns it into the same rows as the other four systems. It does not use the
Greenhouse Harvest API, so it never sees candidates, applications or anything private.

#### Lever postings API and Ashby job postings API in one run

Lever's Postings API (including Lever's EU instance) and Ashby's public job posting API are read
in the same run, so a list of companies spread over several systems becomes one table. Lever boards
are read page by page and only as far as needed.

### Recruitee jobs API and Teamtailor RSS for European job boards

Many European employers, especially in the Netherlands and the Nordics, publish their jobs through
Recruitee or Teamtailor. The Actor reads the public endpoints these systems document for career
sites: the Recruitee jobs API (the Careers Site API, with published pay ranges) and the Teamtailor
RSS feed of the career site, and maps them to the same rows. Recruitee jobs and Teamtailor jobs
land in one ATS job feed next to Greenhouse, Lever and Ashby, so one company list covers European
job boards and US ones in a single run, instead of a separate Recruitee scraper per system.

### Track new, updated and closed jobs at your company list

Choose **Changes** mode, give the list a watchlist ID such as `target-accounts`, and schedule the
Actor daily or weekly. Each run is compared with the previous complete run of the same watchlist
ID and saves only jobs with `change_type` = `new`, `updated` or `closed`, with `first_seen` and
`closed_at` dates. Unchanged jobs are skipped and not charged.

- **The first run of a watchlist is a baseline:** it records the current jobs and saves no job
  rows, unless you turn on "Save the first run's jobs". Companies are still checked and charged.
- **Adding a company** to an existing watchlist works the same way: its current jobs are recorded
  as a baseline (or saved as new with "Save the first run's jobs"), so you get its changes from
  the next run instead of a burst of "new" jobs that are not new.
- **Changing the filters, the ATS setting, the job cap or the description setting** records a new
  baseline silently: no closed or new rows for jobs that only moved in or out of your filters.
- **Removing a company** simply stops tracking it; it produces no closed rows.

### Use job postings data as hiring and buying signals

- **Sales, RevOps and account teams** use hiring as a buying signal: a new "Head of Data" or ten
  new sales roles at an account is a reason to call.
- **Recruiters and talent teams** watch target employers for new openings and for roles that close.
- **Job board and newsletter operators** get fresh postings from a fixed list of companies without
  building five integrations.
- **Analysts and AI agents** track hiring velocity, departments and pay ranges over time.

### How to use it

1. Add your companies, one per line: `Stripe`, `notion.so` or `https://jobs.lever.co/palantir`.
2. Optionally add filters, for example title keywords `engineer, data` and location `London`.
3. For a job monitor, choose **Changes** mode with a watchlist ID and schedule the Actor. Send new
   rows to email, chat or a spreadsheet with Apify's integrations or webhooks.

Board URLs match exactly, for example `https://jobs.lever.co/palantir`,
`https://channable.recruitee.com` or a Teamtailor career site such as
`https://{name}.teamtailor.com`. Names and domains are first looked up in a built-in list of about
8,300 known public Greenhouse, Lever and Ashby boards (board names and Greenhouse display names
only, no job data), which finds boards whose name differs from the company name. They are then
matched by trying the board names a company usually uses (for example `palantirtechnologies`, then
`palantir`) on Greenhouse, Lever and Ashby, then on Recruitee and Teamtailor, at most 12 tries per
company by default. Check the `COMPANIES` record to confirm
each match, and paste the board URL for any company that matched the wrong board or none. Inputs
that lead to the same board (for example `Stripe` and `stripe.com`) are read, saved and charged
once; the others are marked `duplicate`.

### Use it from n8n, Make, Zapier or an AI agent

- **n8n:** add Apify's verified n8n node (`@apify/n8n-nodes-apify`; on n8n Cloud the instance owner
  first enables verified community nodes). Use the operation "Run an Actor and Get Dataset" and
  select this Actor By URL (enter exactly
  `https://console.apify.com/actors/TkQ2FpRa9SELdahOH/input`) or by ID (`TkQ2FpRa9SELdahOH`); the
  From list picker may not show it while the Actor is hidden from Store search. The Actor's Store
  page URL does not work in the By URL field. Then paste the same JSON input.
  For a daily hiring digest, put a Schedule Trigger in front, use **Changes** mode with a
  `watchlistId`, and keep rows where `change_type` is `new` or `closed`.
- **Make:** Apify's app: the "Run an Actor" module, then "Get Dataset Items" (the "Watch Actor
  Runs" trigger fires when a run finishes).
- **Zapier:** Apify's app: the "Run Actor" action, then "Fetch dataset items" (the "Finished
  Actor run" trigger fires when a run finishes).
- **AI agents (MCP clients):** add this Actor as a tool through Apify's MCP server:
  `https://mcp.apify.com?tools=quarrywise/greenhouse-lever-ashby-jobs-api`
  The MCP server needs an Apify sign-in or API token.
- **API:** `POST https://api.apify.com/v2/acts/quarrywise~greenhouse-lever-ashby-jobs-api/run-sync-get-dataset-items`
  with your Apify token in the `Authorization: Bearer` header and the input JSON as the body.
  The call waits at most 300 seconds (HTTP 408 after that); for longer runs start the Actor
  asynchronously and read the dataset when it finishes.

### Input

| Field | What it does | Default |
|---|---|---|
| `companies` | Names, domains or board URLs, one per line (1 to 1,000) | required |
| `ats` | `auto`, or only `greenhouse`, `lever`, `ashby`, `recruitee` or `teamtailor` | `auto` |
| `mode` | `snapshot` (all open jobs) or `changes` (new, updated, closed) | `snapshot` |
| `watchlistId` | Name of the watchlist; required in changes mode | none |
| `includeBaseline` | Changes mode: also save a new watchlist's or new company's current jobs as `new` | `false` |
| `titleKeywords` | Keep titles containing any of these; `-word` excludes | none |
| `locations` | Keep jobs whose location contains any of these (`remote` matches remote jobs) | none |
| `departments` | Keep jobs whose department or team contains any of these | none |
| `remoteOnly` | Keep only remote jobs | `false` |
| `includeDescription` | Add a short scrubbed description excerpt | `false` |
| `descriptionMaxChars` | Excerpt length, 50 to 1,000 characters | `300` |
| `maxJobsPerCompany` | Cap on saved jobs per company | `500` |
| `maxDetectionAttempts` | Boards tried per company for names and domains (1 to 24; 6 keeps a two-word name on the first three systems) | `12` |

Example: a daily job monitor for engineering roles at three companies.

```json
{
    "companies": ["Stripe", "notion.so", "https://jobs.lever.co/palantir"],
    "mode": "changes",
    "watchlistId": "eng-targets",
    "titleKeywords": ["engineer", "-intern"],
    "locations": ["London", "remote"]
}
```

### Output fields

One dataset row per job. Every row has the same fields; missing values are `null`.

| Field | Meaning |
|---|---|
| `company`, `company_input` | Company name, and the company exactly as you entered it |
| `ats`, `board_slug` | `greenhouse`, `lever`, `ashby`, `recruitee` or `teamtailor`, and the board name on that system |
| `job_id`, `title`, `department`, `team` | The job as published |
| `location`, `locations`, `remote`, `workplace_type` | Primary and all locations; remote flag; `remote`, `hybrid` or `onsite` when published |
| `employment_type` | For example full-time, contract, internship |
| `salary_min`, `salary_max`, `salary_currency`, `salary_interval` | Published pay range (interval: year, month, week, day or hour) |
| `posted_at`, `updated_at` | First published; last updated (Greenhouse and Recruitee only) |
| `job_url`, `apply_url` | Public posting and application pages |
| `description_excerpt` | Short scrubbed excerpt, only when enabled |
| `change_type`, `first_seen`, `closed_at` | Changes mode only |
| `error` | Set only on free error rows: a board that refused or failed. Error rows carry only the company, ATS, board and the reason |
| `source_url`, `retrieved_at` | The public endpoint read, and when |

Example row (snapshot mode, from a local run of the prefill input on 2026-10-25):

```json
{
    "company": "Stripe",
    "company_input": "Stripe",
    "ats": "greenhouse",
    "board_slug": "stripe",
    "job_id": "8172487",
    "title": "Abuse Investigator",
    "department": "8611 Security Analytics",
    "team": null,
    "location": "Dublin",
    "locations": ["Dublin"],
    "remote": null,
    "workplace_type": null,
    "employment_type": null,
    "salary_min": null,
    "salary_max": null,
    "salary_currency": null,
    "salary_interval": null,
    "posted_at": "2026-09-03T17:30:34.000Z",
    "updated_at": "2026-10-22T13:16:14.000Z",
    "job_url": "https://stripe.com/jobs/search?gh_jid=8172487",
    "apply_url": "https://stripe.com/jobs/search?gh_jid=8172487",
    "description_excerpt": null,
    "change_type": null,
    "first_seen": null,
    "closed_at": null,
    "error": null,
    "source_url": "https://boards-api.greenhouse.io/v1/boards/stripe/jobs?pay_transparency=true",
    "retrieved_at": "2026-10-25T23:11:46.540Z"
}
```

In changes mode a closed job looks like this (other fields `null`, `locations` empty). This one is
illustrative, with a made-up company and ID:

```json
{
    "company": "Acme Robotics",
    "company_input": "Acme Robotics",
    "ats": "lever",
    "board_slug": "acmerobotics",
    "job_id": "00000000-0000-0000-0000-000000000001",
    "title": "Example Job Title",
    "change_type": "closed",
    "first_seen": "2026-10-01T08:00:00.000Z",
    "closed_at": "2026-10-08T08:00:00.000Z",
    "job_url": "https://jobs.lever.co/acmerobotics/00000000-0000-0000-0000-000000000001"
}
```

The run also writes two records to its key-value store:

- `OUTPUT`: the run summary (status, counts, charged events, stopped sources, warnings).
- `COMPANIES`: one entry per company with its status (`matched`, `unmatched`, `duplicate`,
  `error` or `skipped`), the matched board, `matched_by` (board URL, name, domain or cache), jobs
  on the board, jobs matching your filters, rows saved, and why a company was not matched.

### Get the jobs as JSON, CSV, Excel or through the API

Every run's dataset can be downloaded from the run page as JSON, CSV, Excel, XML or HTML, or read
through the Apify API (the dataset items endpoint). The "Jobs" view is the default table; the
"Changes (changes mode)" and "Source and status" views show other columns. You can also start the
Actor through the Apify API with the same input as above, schedule it in Apify Console, and attach
webhooks or integrations that run when a run finishes. The `OUTPUT` and `COMPANIES` records are
JSON and are available through the key-value store API.

### Pricing per company checked

Pay per event. You pay for companies whose board was found and read, and for job rows saved; the
closed-jobs monitor is what you get for the per-company fee.

| Event | Price |
|---|---|
| Company checked | $2.00 per 1,000 companies |
| Job row | $1.50 per 1,000 rows |

Free: companies with no board found, duplicates of a company already checked, boards that refused
or failed (error rows), and unchanged jobs in changes mode. Apify's standard Actor start event also
applies. Apify's free plan includes $5 of usage a month, enough for about 100 companies and 3,000
job rows.

| Example | Cost |
|---|---|
| 2 companies, 25 jobs each, snapshot | about $0.08 |
| 50 companies, 1,000 matching jobs, snapshot | about $1.60 |
| 300 companies daily, about 40 changed jobs a day | about $0.66 a day, about $20 a month |
| 100 companies weekly, about 60 changes a week | about $0.29 a week |

Set a **maximum charge per run** in the run options to cap your spend: the Actor stops cleanly at
that limit, saves what it has, and charges nothing beyond it.

### Data source, licence and attribution

The Actor reads only the public, unauthenticated job-board endpoints that the vendors document for
publishing job listings: the Greenhouse Job Board API, the Lever Postings API, the Ashby job
posting API, the Recruitee Careers Site API (read only; its application endpoint is never used)
and the Teamtailor career-site RSS feed. It never logs in, uses no API keys, solves no CAPTCHAs and uses no
proxies. Each job posting is published by the employer and belongs to that employer; the Actor
returns facts about the posting (title, department, location, pay range, dates) and links to the
original posting, plus at most a short excerpt of the description when you ask for one.

Greenhouse, Lever and Ashby document these public endpoints for employers' own career sites and
publish no terms specific to them. Recruitee's terms describe the careers site as meant, among
other things, to provide the public with a list of job opportunities, and Teamtailor says the data
in its RSS feed is publicly available. You are responsible for how you reuse the data, and the
vendors may change, limit or close the endpoints at any time; if one does, the Actor stops that
source and reports it. Jobs an employer marks as unlisted on Ashby (reachable by direct link only)
are never returned.

The built-in list of known Greenhouse, Lever and Ashby boards used for name matching holds only
board names and Greenhouse display names: the names were found through Common Crawl's URL index
(its CDX API, used as Common Crawl's FAQ documents for scripted access), and each board was checked
on its vendor's public job-board API on 2026-10-26.

Personio boards are not read in this version; Personio board URLs are reported as not available,
free, with no request.

SmartRecruiters and Workable are not supported: SmartRecruiters' Posting API host asks crawlers
other than LinkedIn's not to fetch it (robots.txt), and Workable documents no public jobs endpoint
for third parties (its documented API needs the employer's key). Their board URLs are reported as
not supported, free.

Please keep the `job_url` link when you republish or share jobs, so readers reach the employer's
original posting.

This Actor is independent. It is not affiliated with, endorsed by or supported by Greenhouse,
Lever, Ashby, Recruitee, Teamtailor or any employer whose jobs it returns. Product names belong to their owners, and no
vendor logos are used.

**Polite by design:** every request identifies itself as `QuarrywiseBot/1.0`, robots.txt and
text-and-data-mining reservations (TDMRep) are checked before reading a host, each job board host
gets at most 2 requests at a time with spacing between them (Lever's requested 1-second delay is
honoured; each Recruitee and Teamtailor board host gets one request at a time, at least
1 second apart, and at most 2 boards per vendor are read at once), and a refusal (403, a challenge
page or repeated rate limiting) stops that source for the rest of the run instead of being retried
around. On Recruitee and Teamtailor every employer has its own board address, so there a
challenge page or repeated rate limiting from the vendor stops that vendor at once, a 401 or 403
stops it when a second employer's board answers the same way, and a refusal from an employer's own
domain fails only that board. Once a vendor has stopped, later companies whose known board is on it
get a free error row, and companies for which that vendor was only a guess are reported unmatched
with a note. Greenhouse is read without descriptions
unless you ask for excerpts, and detection results are cached, so repeat runs download as little
as possible.

**Personal data:** custom fields that employers add to postings are never read, except the
workplace type and employment type. Office street addresses and postal codes (Recruitee,
Teamtailor), Recruitee's per-job application mailbox and application questions are never read. Every text field is screened for email addresses and phone
numbers in common formats (best effort for unusual phone formats), links and social handles are
removed, and titles such as "Executive Assistant to ..." lose the name.
Descriptions are excluded by default; in excerpts, names that follow recruiter, contact or
reporting phrases are also removed, on a best-effort basis: a name written in another way can
remain.

### Limitations

- **Five systems in this version:** Greenhouse, Lever (including its EU instance via board URL),
  Ashby, Recruitee and Teamtailor. Companies on other systems (Workday, SmartRecruiters, Workable,
  Personio and others) are reported as not found, not supported or not available (free).
- **Custom career domains:** Recruitee and Teamtailor boards are read on their vendor address
  (`{name}.recruitee.com`, `{name}.teamtailor.com`). A board
  shown only on the employer's own domain (for example `careers.example.com`) is found when that
  vendor address uses the same name; otherwise paste the vendor address.
- **Fields differ by system:** Teamtailor's feed carries no pay ranges or employment type.
- **The built-in board list** covers Greenhouse, Lever and Ashby boards seen up to 2026-10-26; a
  board it does not know is still found by guessing, as above.
- **Name matching is a guess.** A name can match a different company that uses the same board
  name, or miss a company whose board name is unusual. Board URLs are always exact; check the
  `COMPANIES` record.
- **What counts as updated:** a job is `updated` when its title, department, team, locations,
  remote or workplace type, employment type, pay range or URLs change. The row shows the new
  values, not the old ones. Description edits and Greenhouse's `updated_at` alone do not count.
- **Closed detection needs the whole picture:** if a company has more matching jobs than
  `maxJobsPerCompany`, or its board could not be read, its jobs are not reported as closed in that
  run (they are kept for the next run). If a company's board disappears, its jobs are reported
  closed.
- **Watchlist size:** a watchlist's state is stored in 16 parts of about 59,000 tracked jobs each
  (about 900,000 in total; each board belongs to one part). If a company's part is ever full, that
  company gets a free error row and is not charged; split the watchlist into several IDs.
- **Salary** appears only where the employer publishes it. Greenhouse ranges do not always state
  an interval, so `salary_interval` can be `null`.
- **Remote** is `true` when the board marks the job remote or its location says so, `false` when
  it is marked hybrid or on-site, and `null` when unknown. Ashby's "remote eligible" flag counts as
  remote. Greenhouse office locations (and office names such as "Remote (US)") are only read when
  description excerpts are on.
- One run handles up to 1,000 companies; split larger lists into several watchlist IDs.

### FAQ

**How do I get alerts for new jobs?** Use changes mode with a watchlist ID, schedule the Actor
(daily or weekly), and attach an integration or webhook to send new rows to email, chat or a sheet.

**Does it read candidates or applications (Greenhouse Harvest API)?** No. It reads only public job
postings, so no API key is needed.

**Why did a company not match?** It may use another system, or its board name differs from its
name. Open its careers page, copy the job board URL (for example `jobs.ashbyhq.com/...` or
`name.recruitee.com`) and use that instead. Unmatched companies are free and listed in the run summary.

**Can I monitor several separate lists?** Yes. Give each list its own watchlist ID. Runs with the
same ID are compared with each other.

**What counts as one job row?** One saved dataset row: each job in snapshot mode; each new,
updated or closed job in changes mode. Unchanged jobs and error rows are free.

**Does it store anything about me?** It keeps its own store, `quarrywise-ats-job-watchlist-state`,
with the watchlist state and a detection cache: for each watchlist its settings fingerprint, a
hashed key per company input and the board it matched, board names and company names, and for
each tracked job its ID, title, department, location, job URL and dates; the detection cache holds
hashed company inputs, public board names and dates. No user identifiers. It runs with limited
permissions and only touches its own storages.

**Is the data official?** It is read from each employer's public job board as published at run
time. The Actor is independent and the data is provided as is.

### Changelog

See the Changelog tab. Version 0.2 (2026-10-26): Recruitee and Teamtailor boards, and a built-in
list of known boards for faster detection. Version 0.1 (2026-10-25): first
release with Greenhouse, Lever and Ashby, snapshot and changes modes, filters, salary ranges and
scrubbed excerpts.

### Provider

Provided by Quarrywise, a trade name of Picazzo Research & Development, Zinkstraat 24, Box C4687, 4823 AD Breda, the Netherlands. KvK 42060545, VAT NL005464235B85. Contact: hello@quarrywise.com or the Issues tab.

# Changelog

This Actor's version history is a separate document: https://apify.com/quarrywise/greenhouse-lever-ashby-jobs-api/changelog.md

# Actor input Schema

## `companies` (type: `array`):

One company per line: a name (Stripe), a domain (stripe.com) or a job board URL (https://jobs.lever.co/palantir, https://job-boards.greenhouse.io/stripe, https://jobs.ashbyhq.com/openai, https://channable.recruitee.com, https://{name}.teamtailor.com). Board URLs match exactly; names and domains are matched by guessing the board name, so check the COMPANIES record for the board each one matched. Inputs that lead to the same board are read and charged once. Up to 1,000 companies per run.

## `ats` (type: `string`):

Auto tries Greenhouse, Lever and Ashby first, then Recruitee and Teamtailor for every name or domain. Pick one system to try only that one (fewer requests, fewer wrong matches).

## `mode` (type: `string`):

Snapshot returns every open job that matches your filters. Changes returns only jobs that are new, updated or closed since the previous run with the same watchlist ID; schedule it daily or weekly as a job monitor.

## `watchlistId` (type: `string`):

Required in changes mode. A name for this watchlist (letters, digits, '.', '\_' or '-'), for example target-accounts. Runs with the same ID are compared with each other; use a different ID for each separate list.

## `includeBaseline` (type: `boolean`):

Changes mode only. Off: the first run of a new watchlist, and the first run after you add a company, only record the current jobs as a baseline and save no job rows for them (companies are still checked). On: those jobs are also saved as new. Changing the filters, ATS, job cap or description setting always records a new baseline silently.

## `titleKeywords` (type: `array`):

Keep jobs whose title contains any of these words (not case-sensitive), for example engineer, sales, data. Start a word with - to exclude it, for example -intern. Empty keeps all titles.

## `locations` (type: `array`):

Keep jobs whose location contains any of these values, for example London, Germany, New York. The word remote also matches remote jobs. Empty keeps all locations.

## `departments` (type: `array`):

Keep jobs whose department or team contains any of these values, for example Engineering, Sales. Empty keeps all departments.

## `remoteOnly` (type: `boolean`):

Keep only jobs the board marks as remote (or whose location says remote).

## `includeDescription` (type: `boolean`):

Adds a short plain-text excerpt of each job description. Email addresses and phone numbers in common formats, links and social handles are removed; names after recruiter, contact or reporting phrases are removed on a best-effort basis. Off by default. On Greenhouse this also reads office locations, so turning it on or off re-baselines a changes-mode watchlist.

## `descriptionMaxChars` (type: `integer`):

Maximum length of the description excerpt.

## `maxJobsPerCompany` (type: `integer`):

Saves at most this many matching jobs per company. In changes mode, closed jobs are only reported for companies whose matching jobs all fit under this limit.

## `maxDetectionAttempts` (type: `integer`):

For names and domains: how many boards may be tried per company across the job board systems. The default 12 covers the joined name and the name without words such as Technologies or AI on Greenhouse, Lever and Ashby, then on Recruitee and Teamtailor. 6 keeps detection to the first three systems for a two-word name. Boards that did not exist are remembered for 7 days and not asked again.

## Actor input object example

```json
{
  "companies": [
    "Stripe",
    "https://jobs.lever.co/palantir"
  ],
  "ats": "auto",
  "mode": "snapshot",
  "includeBaseline": false,
  "remoteOnly": false,
  "includeDescription": false,
  "descriptionMaxChars": 300,
  "maxJobsPerCompany": 25,
  "maxDetectionAttempts": 12
}
```

# Actor output Schema

## `results` (type: `string`):

One row per job: company, title, department, locations, remote flag, pay range, dates and links. In changes mode only new, updated and closed jobs. Rows with error set are free error rows.

## `summary` (type: `string`):

Run status, counts, charged events, stopped sources and warnings (the OUTPUT record).

## `companies` (type: `string`):

One entry per company input with its matched board, job counts, rows saved and the reason a company was not matched or failed (the COMPANIES record).

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "companies": [
        "Stripe",
        "https://jobs.lever.co/palantir"
    ],
    "mode": "snapshot",
    "maxJobsPerCompany": 25
};

// Run the Actor and wait for it to finish
const run = await client.actor("quarrywise/greenhouse-lever-ashby-jobs-api").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "companies": [
        "Stripe",
        "https://jobs.lever.co/palantir",
    ],
    "mode": "snapshot",
    "maxJobsPerCompany": 25,
}

# Run the Actor and wait for it to finish
run = client.actor("quarrywise/greenhouse-lever-ashby-jobs-api").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "companies": [
    "Stripe",
    "https://jobs.lever.co/palantir"
  ],
  "mode": "snapshot",
  "maxJobsPerCompany": 25
}' |
apify call quarrywise/greenhouse-lever-ashby-jobs-api --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,quarrywise/greenhouse-lever-ashby-jobs-api"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/TkQ2FpRa9SELdahOH/builds/rhSF7cksWFeNMOo2n/openapi.json
