# Greenhouse Jobs Scraper & API: Any Job Board (`mrslm_data/greenhouse-jobs`) Actor

Scrape any company's Greenhouse job board by name or URL: title, department, location, posted date, apply link, description. Keyword, remote, date filters.

- **URL**: https://apify.com/mrslm_data/greenhouse-jobs.md
- **Developed by:** [shawn slam](https://apify.com/mrslm_data) (community)
- **Categories:** Jobs
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.80 / 1,000 jobs

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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 Jobs Scraper & API: Any Job Board

**Open roles from any company's Greenhouse job board: title, department, offices, posted and updated dates, links and optional full description. $1.00 per 1,000 jobs.**

Greenhouse is the most common ATS among funded tech companies, so a short list of boards covers a big slice of the startup job market.

### Quick start

1. Press **Start**. The form is already filled with this example input, so the first run returns real rows in under a minute:

```json
{
  "companies": [
    "stripe",
    "https://job-boards.greenhouse.io/airbnb"
  ],
  "maxJobsPerCompany": 10
}
```

2. Swap in your own values, or call the Actor through the Apify API, Make, Zapier, n8n or the Python/JS clients with the same JSON.
3. Download the results as JSON, CSV or Excel from the **Output** tab, or schedule the run to repeat.

### Output fields

Every row has: platform, company, companyName, jobId, title, department, team, location, locations, isRemote, postedAt, updatedAt, url, applyUrl, boardUrl, scrapedAt, plus Greenhouse extras (requisitionId, language, customFields) and the description when you turn it on.

*The full guide below is also published as "Greenhouse Jobs API: How to Scrape Any Company's Greenhouse Job Board".*

### Is there a Greenhouse jobs API?

Yes. Greenhouse has a public **Job Board API** at `boards-api.greenhouse.io`. It needs no key and lists every job a company has published on its board:

```
GET https://boards-api.greenhouse.io/v1/boards/{board_token}/jobs?content=true
```

`{board_token}` is the company's board name: `airbnb`, `stripe`, `figma`, `gitlab`. `content=true` adds the job description (HTML-escaped), departments and offices.

Greenhouse also has the **Harvest API**, but that is the private API for a company's own recruiting data (candidates, applications, scorecards). It needs that company's API key. You cannot use it to read other companies' jobs, and nothing in this guide touches it.

So the real question is not "is there an API". It is how to turn a public per-company endpoint into a clean, filtered, multi-company dataset you can schedule. That is the part the Actor does.

### How to find a company's Greenhouse board name

1. Open the company's careers page and click any job.
2. If the link goes to `boards.greenhouse.io/<name>/jobs/...` or `job-boards.greenhouse.io/<name>/jobs/...`, the board name is `<name>`.
3. If the company hosts jobs on its own domain, look for `gh_jid=` in the job URL (Airbnb's links look like `careers.airbnb.com/positions/8325753?gh_jid=8325753`). That means Greenhouse is underneath. The board name is usually the company name in lower case; try it.
4. EU-hosted boards live at `job-boards.eu.greenhouse.io/<name>`.

The Actor accepts any of these forms: a bare name (`stripe`), an old `boards.greenhouse.io` link, a new `job-boards.greenhouse.io` link or an EU link. You do not need to normalise them first.

### How to scrape Greenhouse jobs, step by step

#### Step 1: Add companies

Open [Greenhouse Jobs Scraper & API](https://apify.com/mrslm_data/greenhouse-jobs) and press **Try for free** (a free Apify account is enough). In **Companies**, paste one board per line:

```
stripe
https://job-boards.greenhouse.io/airbnb
https://boards.greenhouse.io/figma
gitlab
```

There is no hard limit on the number of companies. A list of 500 boards is a normal run.

#### Step 2: Filter (optional, and free)

Filters run before anything is saved, and **you only pay for jobs that pass them**:

- **Title keywords**: keep a job if its title contains any of these words (`engineer`, `data`, `account executive`). Case-insensitive.
- **Exclude title keywords**: drop titles containing any of these (`intern`, `senior`, `manager`).
- **Location keywords**: match against the location text, all office locations and the workplace type (`London`, `Germany`, `Remote`).
- **Department keywords**: match against the department and team (`Engineering`, `Sales`).
- **Remote only**: keep jobs whose location or workplace says remote.
- **Posted within days**: only jobs first published in the last N days. Set 1 for a daily "new jobs" feed.

#### Step 3: Choose what to keep

- **Include description**: adds the job description as plain text, up to 5,000 characters. Emails and phone numbers in descriptions are removed. Leave it off for lead lists; turn it on for job boards, classification or AI matching.
- **Newest first** (on by default): sorts each company's jobs by posting date before the per-company limit applies, so "10 per company" means the 10 newest.
- **Max jobs per company** and **Max results**: hard caps. The run stops cleanly when it hits them and you are never charged past them.

#### Step 4: Run, export, schedule

Press **Start**. Greenhouse feeds are fast: a few companies take 5 to 10 seconds. Export from the **Output** tab as CSV, Excel or JSON, or read the dataset over the API. The `RUN_SUMMARY` record in the run's key-value store shows, per board, how many roles were open, how many matched your filters and how many were saved. A company name that is not found is reported there with a reason, and costs nothing.

To keep a live feed, save the input as a **Task** and add a daily **Schedule**. With *Posted within days* set to 1, each run returns only the roles posted since yesterday. Use the Apify integrations to push rows to Google Sheets, Slack, a CRM via Make or Zapier, or n8n.

From Python:

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("mrslm_data/greenhouse-jobs").call(run_input={
    "companies": ["stripe", "airbnb", "figma", "gitlab"],
    "titleKeywords": ["engineer"],
    "postedWithinDays": 7,
})
for job in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(job["companyName"], job["title"], job["location"], job["postedAt"])
```

### Real example 1: the default input

```json
{
  "companies": ["stripe", "https://job-boards.greenhouse.io/airbnb"],
  "maxJobsPerCompany": 10
}
```

Result: **20 jobs in 6.1 seconds**, 10 from each board, newest first, and 20 job charges ($0.02). According to the run summary, Stripe had 716 open roles on its board that day and Airbnb 196. Some of the rows:

| companyName | title | department | location | postedAt |
|---|---|---|---|---|
| Airbnb | Supervisor - Trust and Safety Operations | Community Support | Bangalore, India | 2026-10-28 |
| Airbnb | Staff Software Engineer, UI Tooling | Software Engineering | United States | 2026-10-26 |
| Airbnb | Senior Compliance Lead, EMEA | Business Operations/Analysis | Brussels | 2026-10-23 |
| Stripe | Software Engineer, New Grad | 5112 General University | Bangalore | 2026-10-28 |
| Stripe | Head of AMER Startups (AI Sales) | 1170 GEO Sales HQ (NA) | San Francisco | 2026-10-28 |
| Stripe | Design Engineer, Brand Studio | 8113 Brand Studio | US, Canada, Remote | 2026-10-27 |

One full row, exactly as saved:

```json
{
  "platform": "Greenhouse",
  "company": "airbnb",
  "companyName": "Airbnb",
  "jobId": "8327049",
  "title": "Supervisor - Trust and Safety Operations",
  "department": "Community Support",
  "location": "Bangalore, India",
  "locations": ["Bangalore, India"],
  "isRemote": false,
  "postedAt": "2026-10-28T02:33:51.000Z",
  "updatedAt": "2026-10-28T02:33:51.000Z",
  "url": "https://careers.airbnb.com/positions/8327049?gh_jid=8327049",
  "applyUrl": "https://careers.airbnb.com/positions/8327049?gh_jid=8327049",
  "requisitionId": "ONE",
  "language": "en",
  "customFields": { "Is this job part of ACC?": true, "Workplace Type": "Onsite" },
  "boardUrl": "https://job-boards.greenhouse.io/airbnb",
  "scrapedAt": "2026-10-28T11:48:30.673Z"
}
```

`customFields` is worth a look. Many companies publish extra fields on their board, such as workplace type, cost centre or employment type, and the Actor keeps any simple public ones.

### Real example 2: engineering roles posted in the last 30 days

```json
{
  "companies": ["gitlab", "https://job-boards.greenhouse.io/airbnb", "https://boards.greenhouse.io/figma"],
  "titleKeywords": ["engineer"],
  "postedWithinDays": 30,
  "maxJobsPerCompany": 4
}
```

Result: **12 jobs in 5.8 seconds**, 12 charges. The run summary shows how much the filters cut: GitLab had 237 open roles and 31 matched; Airbnb 198 and 20 matched; Figma 142 and 7 matched. Only the 4 newest matches per company were saved, and only those were billed.

| companyName | title | location | isRemote | postedAt |
|---|---|---|---|---|
| GitLab | Manager, Professional Services Engineers, EMEA | Remote Ireland; Remote, France; Remote, Germany; ... | true | 2026-10-28 |
| GitLab | Software Engineer, Delivery : Release | Bangalore, India | false | 2026-10-28 |
| Airbnb | Senior Software Engineer, Experimentation Interfaces | Brazil | false | 2026-10-26 |
| Figma | AI Engineer, Internal Tools | San Francisco, CA • New York, NY • United States | false | 2026-10-27 |
| Figma | Manager, Software Engineering - AI Infrastructure | San Francisco, CA • New York, NY • United States | false | 2026-10-19 |

### What people use Greenhouse job data for

- **Sales prospecting and intent signals.** A company opening five data-engineering roles is probably buying data tools. Filter by department or title, schedule daily, and send new matches to your CRM.
- **Recruiting and staffing.** See the day a target company opens a role you can fill, and which teams and cities it is growing in.
- **Investor and market research.** Track open roles by department and office for a portfolio or watch list over time. Hiring freezes and new offices show up here first.
- **Job boards and newsletters.** Build a niche board (remote design jobs, climate tech, EU fintech) from a curated list of companies, with descriptions and apply links.
- **Job seekers.** Watch the 30 companies you want to work for and get only new roles that match your title and location.

### Greenhouse job board API vs. this Actor

**Calling the Job Board API yourself** is free and fine for one or two companies. You will need to:

- find and validate each board token (a typo returns 404, and you must tell that apart from a company that left Greenhouse)
- unescape and strip the HTML in `content`, and clean contact details out of descriptions
- flatten departments, offices and metadata into columns
- write your own title, location, remote and date filters, and sorting
- deduplicate, handle retries and rate limits, and host the script somewhere that runs daily

**Using the Actor** costs **$1.00 per 1,000 jobs saved**, after filters. A daily feed of new engineering roles across 200 companies is typically a few hundred jobs a week, so a few dollars a month. The same input also works from Make, Zapier, n8n and the API, and failed company names are explained in the summary instead of crashing the run.

If a company you track uses Lever, Workable or another ATS as well as Greenhouse, our sister Actor [ATS Jobs Scraper](https://apify.com/mrslm_data/ats-jobs) reads seven platforms in one run, with the same columns.

### Pricing

- **$1.00 per 1,000 jobs saved** (Apify Silver plan $0.90, Gold $0.80), plus $0.00005 per run.
- You pay only for jobs saved after your filters. Companies that are not found, and jobs your filters drop, are free.
- Examples: the default run (20 jobs) costs $0.02. A full export of Stripe's 716 open roles costs about $0.72.

### FAQ

**Do I need a Greenhouse API key?** No. Job boards are public. The Actor reads the same public feed the company's careers page uses. Harvest API keys are only for a company's own private recruiting data.

**Where does the data come from?** From the company's public Greenhouse job board: the same list of open roles the company shows on its careers page.

**Does it include salaries?** Not as a column. Greenhouse's public board has no structured salary field, so `salary` is empty for Greenhouse jobs. Many companies (especially for US roles) put the pay range in the description; turn on *Include description* to get it.

**Why is `isRemote` false for a job that says "hybrid"?** Greenhouse has no structured remote flag. The Actor marks a job remote when its location says so, which matches how companies label remote roles on their boards. Use *Location keywords* = `hybrid` if you need hybrid roles.

**How fresh is the data?** Live. Each run reads the board at that moment, so closed jobs disappear and new ones appear. `postedAt` is the date the job was first published; `updatedAt` is the last edit.

**Does it include recruiter or applicant data?** No. Only the public job ad. Email addresses and phone numbers in descriptions are removed.

**Can it scrape Workday, Ashby or iCIMS boards?** No. This Actor reads Greenhouse only, and the multi-ATS Actor covers Greenhouse, Lever, Workable, Recruitee, Personio, Breezy HR and Teamtailor. We would rather say so than return empty runs.

**How do I get only new jobs every day?** Save your input as a Task with *Posted within days* = 1 and add a daily Schedule.

### Is it legal?

This Actor only reads pages and feeds that the website itself publishes to every visitor without a login. It obeys each site's robots.txt, keeps a polite request rate, and never tries to get past logins, paywalls, captchas or bot checks. It does not collect personal data such as emails, phone numbers or names of private individuals. You are responsible for how you use the data: follow the source site's terms, copyright and the privacy laws that apply to you (GDPR, CCPA, etc.). This tool is independent and not affiliated with or endorsed by the platforms it reads.

# Actor input Schema

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

Greenhouse board URLs or names. One per line.

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

Keep jobs whose title contains any of these words (e.g. engineer, sales). Empty = all jobs.

## `excludeTitleKeywords` (type: `array`):

Drop jobs whose title contains any of these words (e.g. intern).

## `locationKeywords` (type: `array`):

Keep jobs whose location or country contains any of these (e.g. London, Germany, remote).

## `departmentKeywords` (type: `array`):

Keep jobs whose department or team contains any of these (e.g. engineering).

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

Keep only jobs marked remote.

## `postedWithinDays` (type: `integer`):

Keep only jobs posted in the last N days. 0 = any date.

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

Add the full job description as plain text (up to 5,000 characters). Makes rows larger.

## `newestFirst` (type: `boolean`):

Sort each company's jobs by posting date, newest first (so per-company limits keep the newest).

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

0 = all open jobs.

## `maxResults` (type: `integer`):

Safety cap on saved jobs across the whole run. 0 = no cap. Your max cost per run is always respected.

## `proxyConfiguration` (type: `object`):

Usually not needed.

## Actor input object example

```json
{
  "companies": [
    "stripe",
    "https://job-boards.greenhouse.io/airbnb"
  ],
  "remoteOnly": false,
  "postedWithinDays": 0,
  "includeDescription": false,
  "newestFirst": true,
  "maxJobsPerCompany": 10,
  "maxResults": 0,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

All jobs as a dataset (JSON, CSV, Excel and more via the API or Console).

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

Jobs per board, boards that failed, and the stop reason.

# 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://job-boards.greenhouse.io/airbnb"
    ],
    "maxJobsPerCompany": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("mrslm_data/greenhouse-jobs").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://job-boards.greenhouse.io/airbnb",
    ],
    "maxJobsPerCompany": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("mrslm_data/greenhouse-jobs").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://job-boards.greenhouse.io/airbnb"
  ],
  "maxJobsPerCompany": 10
}' |
apify call mrslm_data/greenhouse-jobs --silent --output-dataset

```

## MCP server setup

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

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/PW6o3GNeymunk8Gie/builds/nURDjpTwAru3VJrcQ/openapi.json
