# Greenhouse Jobs Scraper — All Jobs, Salaries & New Roles (`brilliant_gum/greenhouse-jobs-scraper`) Actor

Scrape every open job from any company's Greenhouse board: full descriptions, departments, locations, salary when disclosed. Tracks which roles appeared and closed since your last run. No API key.

- **URL**: https://apify.com/brilliant\_gum/greenhouse-jobs-scraper.md
- **Developed by:** [Yuliia Kulakova](https://apify.com/brilliant_gum) (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 $0.01 / job scraped

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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Greenhouse Jobs Scraper — All Jobs, Salaries & New Roles

![Greenhouse Jobs Scraper — every job, salaries, new & closed roles](https://i.imgur.com/avXtf8G.png)

Every open job at any company that hires through Greenhouse — full descriptions, departments, locations, disclosed salaries — and, on repeat runs, exactly which roles appeared and which closed.

No login, no API key. One request per company covers the whole board.

***

### What you get

Point it at company slugs (the last part of a `boards.greenhouse.io` address):

```json
{
  "companies": ["stripe", "airbnb", "gitlab"]
}
```

An unedited record from testing:

```json
{
  "companySlug": "gitlab",
  "title": "Commercial Legal Counsel, Public Sector",
  "location": "Remote, US",
  "isRemote": true,
  "departments": ["Legal and Corporate Affairs"],
  "salaryMin": 115000,
  "salaryMax": 162000,
  "salaryCurrency": "USD",
  "salaryRaw": "$115,000—$162,000",
  "description": "GitLab is an open core software company…",
  "daysOpen": 12,
  "postingAge": "RECENT",
  "isNewSinceLastRun": true,
  "firstPublished": "2026-08-05",
  "applyUrl": "https://about.gitlab.com/jobs/apply/…",
  "scrapedAt": "2026-08-17T07:20:00.000Z"
}
```

Measured on live boards: Stripe returns 578 open roles, Cloudflare 304, GitLab 197, Airbnb 185 — each in a single request, a few seconds per company.

***

### Track hiring, not just jobs

With `trackChanges: true` (default), every run remembers the board and the next run tells you what moved:

```
gitlab: 197 open jobs, 60 match filters | 4 new, 2 closed since 2026-08-10
```

- Every job carries `isNewSinceLastRun`.
- Set `newJobsOnly: true` and scheduled runs deliver just the fresh roles — a hiring feed for the companies you care about.
- "New" always means new **on the company's board**. Changing your filters between runs never relabels old jobs as new — the snapshot tracks the board, not your query.

Why that matters: a new posting is a signal. Sales teams read "3 new DevOps roles at X" as a buying signal, recruiters get first-mover advantage on fresh openings, analysts read hiring velocity as company health.

***

### Salary data — the honest version

Greenhouse has no structured salary field. Salaries appear in the description text when pay-transparency laws or company policy require them, and this scraper extracts the range into `salaryMin` / `salaryMax` / `salaryCurrency`.

Coverage measured on live boards: **Airbnb 73%**, **GitLab 36%**, **Stripe 2%** of postings disclose a range. It varies exactly this much between companies — any tool promising salaries for every job is making them up. Use `minSalary` knowing it also drops every posting that discloses nothing.

***

### Filters

| Input | What it does |
|---|---|
| `keywords` / `excludeKeywords` | Match against title, department and full description |
| `locations` | e.g. `["remote", "london", "new york"]` |
| `departments` | e.g. `["engineering", "sales"]` |
| `remoteOnly` | Title or location marked remote |
| `postedAfter` | Only roles first published since a date |
| `minSalary` | Only roles whose disclosed range reaches the number |
| `newJobsOnly` | Only roles that appeared since the previous run |

A typical monitoring setup:

```json
{
  "companies": ["stripe", "gitlab", "figma", "cloudflare"],
  "keywords": ["engineer", "developer"],
  "remoteOnly": true,
  "trackChanges": true,
  "newJobsOnly": true
}
```

Schedule it daily and the dataset is your feed of new remote engineering roles at those companies.

***

### Use cases

- **Recruiting** — monitor competitors' boards; fresh postings mean active budgets and fair game for sourcing.
- **Sales intelligence** — new roles reveal initiatives: a company posting three data-platform jobs is about to buy data tooling.
- **Job search** — watch your target companies and apply on day one, not week three.
- **Market research** — hiring velocity and department mix across a portfolio of companies.
- **HR analytics** — how long roles stay open (`daysOpen`, `postingAge`) and where teams are growing.

***

### Limitations, honestly

**You supply the company list.** Greenhouse has no public directory of all boards; the actor scrapes the companies you name. A wrong slug is reported clearly (`"xyz" is not a Greenhouse board`) and does not break the run.

**Salary coverage varies by company** — see above. The field is extracted, never estimated.

**Greenhouse boards only.** Companies hiring through Lever, Ashby or Workday are different systems — this actor does one system well.

**Remote detection is heuristic.** A role is flagged remote when its title or location says so. Companies with unusual conventions may need the `locations` filter instead.

***

### FAQ

**Do I need an API key or Greenhouse account?**
No. The board API is public.

**How fast is it?**
One request per company. Two large boards (763 jobs combined) returned in 5 seconds in testing.

**How do I find a company's slug?**
Open their careers page and look for `boards.greenhouse.io/<slug>` or `job-boards.greenhouse.io/<slug>` — pass the slug or the whole URL.

**Can it discover which companies use Greenhouse?**
No — no such public index exists. Curate your list once; the actor handles the rest on schedule.

**What happens on the very first run?**
All jobs return with `isNewSinceLastRun: null` and the baseline is saved; deltas start from the second run.

***

Maintained by **brilliant\_gum**. Issues and feature requests welcome on the Issues tab.

# Actor input Schema

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

Greenhouse board slugs or URLs, e.g. \["stripe", "https://boards.greenhouse.io/airbnb"]. The slug is the last part of a company's boards.greenhouse.io address.

## `includeContent` (type: `boolean`):

Include the complete description as readable text. Also enables salary extraction from pay-transparency listings. Turning it off makes records much smaller.

## `trackChanges` (type: `boolean`):

Remembers each board between runs, marks every job as new or existing, and logs which jobs closed. Ideal for scheduled monitoring of hiring activity.

## `newJobsOnly` (type: `boolean`):

From the second run on, return only the roles that appeared since the previous run. Needs change tracking enabled.

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

Cap on jobs returned per board. Large employers keep hundreds of roles open.

## `keywords` (type: `array`):

Keep jobs whose title, department or description contains at least one of these. Case-insensitive.

## `excludeKeywords` (type: `array`):

Drop jobs matching any of these words.

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

Keep jobs whose location or office matches any of these, e.g. \["remote", "new york", "london"].

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

Keep jobs in departments matching any of these, e.g. \["engineering", "sales"].

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

Keep only roles whose title or location marks them as remote.

## `postedAfter` (type: `string`):

Keep jobs first published on or after this date (YYYY-MM-DD).

## `minSalary` (type: `integer`):

Keep jobs whose disclosed salary range reaches this number. Only a minority of postings disclose salary — this filter drops the rest.

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

Optional. The Greenhouse API is public — leave this empty unless your runs get throttled.

## Actor input object example

```json
{
  "companies": [
    "stripe",
    "gitlab",
    "cloudflare"
  ],
  "includeContent": true,
  "trackChanges": true,
  "newJobsOnly": false,
  "maxJobsPerCompany": 200,
  "keywords": [
    "engineer",
    "python"
  ],
  "excludeKeywords": [
    "intern",
    "staffing agency"
  ],
  "locations": [
    "remote",
    "london"
  ],
  "departments": [
    "engineering"
  ],
  "remoteOnly": false,
  "postedAfter": "2026-08-01",
  "minSalary": 150000
}
```

# Actor output Schema

## `dataset` (type: `string`):

One item per job.

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

Per-company rollup: open jobs, matches, new and closed roles.

## `consoleUrl` (type: `string`):

Open this run in Apify Console.

# 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",
        "airbnb"
    ],
    "includeContent": true,
    "trackChanges": true,
    "maxJobsPerCompany": 200
};

// Run the Actor and wait for it to finish
const run = await client.actor("brilliant_gum/greenhouse-jobs-scraper").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",
        "airbnb",
    ],
    "includeContent": True,
    "trackChanges": True,
    "maxJobsPerCompany": 200,
}

# Run the Actor and wait for it to finish
run = client.actor("brilliant_gum/greenhouse-jobs-scraper").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",
    "airbnb"
  ],
  "includeContent": true,
  "trackChanges": true,
  "maxJobsPerCompany": 200
}' |
apify call brilliant_gum/greenhouse-jobs-scraper --silent --output-dataset

```

## MCP server setup

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

```

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/CGQxgGyrluOedYO1j/builds/txNaPMPar9pdeHDiQ/openapi.json
