# Greenhouse Jobs Scraper — Any Company Board (`devilscrapes/greenhouse-jobs-scraper`) Actor

Scrape every open job posting from any Greenhouse-hosted company career board in one run. We normalize titles, departments, locations, remote signals, full HTML and plain-text descriptions, and best-effort salary snippets into one clean dataset across every board you track.

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

## Pricing

from $1.50 / 1,000 results

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/platform/actors/running/actors-in-store#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

<div align="center">
  <img src=".actor/icon.svg" width="160" alt="Devil Scrapes mark" />

## Greenhouse Jobs Scraper — Any Company Board

**💰 $1.50 / 1 000 results**  ·  pay only for results  ·  no credit card to try

*We do the dirty work so your dataset stays clean.* 😈

Scrape every open job posting from any Greenhouse-hosted company career board in one run. We normalize titles, departments, locations, remote signals, full HTML and plain-text descriptions, and best-effort salary snippets into one clean dataset across every board you track.

</div>

***

### 🎯 What this scrapes

Greenhouse (boards-api.greenhouse.io) is one of the most widely-adopted ATS platforms among VC-backed tech employers — Stripe, Airbnb, GitLab, Coinbase, Robinhood, Instacart, Pinterest, Lyft, Discord and more all run live Greenhouse boards today. This Actor is the Greenhouse specialist: richer per-job fields, department/location/remote filtering, full HTML+plain-text content extraction, and best-effort salary mining, sold on depth rather than the shallow multi-ATS breadth of `multi-ats-jobs-scraper`.

### 🔥 What we handle for you

- 🔁 **Retries with exponential backoff** on `408 / 429 / 5xx` and network errors (2s start, doubling, 30s cap, 5 attempts) — one flaky board never stalls the rest of a bulk multi-board run.
- 📋 **Per-run status you can trust** — every run reports exactly how many jobs were scraped, across how many boards, and how many tokens weren't found. No silent empty datasets.
- 🧩 **One normalized schema across every company** — raw Greenhouse responses vary wildly in which optional fields each employer populates; we flatten that into one consistent, Pydantic-validated row shape whether you're pulling from 1 board or 1,000.
- 🧊 **Clean, typed dataset rows** — ISO-8601 timestamps, stable job IDs, JSON / CSV / Excel export straight from the Apify Console.
- 💰 **Pay-Per-Event pricing** — you only pay for job rows that land in your dataset. No data, no charge.

### 💡 Use cases

- **Recruiter pipelines** — track specific employers' Greenhouse boards for new openings by department or location.
- **Hiring-intent signal** — SDR/BD teams reading open reqs as a growth signal ahead of outbound.
- **HR-tech and comp benchmarking** — build a normalized Greenhouse coverage dataset across many companies at once.
- **Remote-jobs aggregation** — filter to heuristically-remote roles across every tracked board.

### ⚙️ How to use it

1. Click **Try for free** at the top of the page.
2. Fill in the input form — most fields have sensible defaults.
3. Click **Start**. Output streams into the run's dataset.
4. Export from **Storage → Dataset** as JSON, CSV, or Excel — or fetch via the API.

### 📥 Input

| Field | Type | Required | Default | Notes |
|---|---|:--:|---|---|
| `boardTokens` | `array` | **yes** | \['stripe', 'airbnb'] | Greenhouse board token, e.g. "stripe", or a full boards.greenhouse.io / job-boards.greenhouse.io board URL — both normal |
| `departmentFilter` | `['string', 'null']` | no | None | Case-insensitive substring match against any entry in departments\[].name. |
| `locationFilter` | `['string', 'null']` | no | None | Case-insensitive substring match against location.name. |
| `remoteOnly` | `boolean` | no | False | Keep only jobs whose heuristic remote flag is true. |
| `maxItems` | `integer` | no | 500 | Cap total rows emitted across ALL boards combined in the run, applied after filtering. |
| `proxyConfiguration` | `object` | no | {'useApifyProxy': True} | Apify Proxy configuration. No anti-bot signal observed on this target. |

#### Example input

```json
{
  "boardTokens": [
    "stripe"
  ],
  "departmentFilter": null,
  "locationFilter": null,
  "remoteOnly": false,
  "maxItems": 5,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

### 📤 Output

Every row is one dataset item.

| Field | Type | Notes |
|---|---|---|
| `job_id` | `integer` | List item `id`. |
| `company` | `string` | Normalized input board token (not `company_name`). |
| `company_name` | `['string', 'null']` | List item `company_name`, direct passthrough. |
| `title` | `string` | List item `title`. |
| `department` | `['string', 'null']` | First entry's name in departments\[]; null when empty. |
| `location` | `['string', 'null']` | Raw location.name string, verbatim. |
| `remote` | `boolean` | Heuristic remote flag (location/office name contains "remote"). |
| `absolute_url` | `string` | List item `absolute_url`, verbatim. |
| `posted_at` | `['string', 'null']` | List item `first_published`, parsed ISO-8601. |
| `updated_at` | `string` | List item `updated_at`, parsed ISO-8601. |
| `description_html` | `['string', 'null']` | `content`, html.unescape()'d, tags kept. |
| `description_text` | `['string', 'null']` | Same source, unescape()'d then tags stripped. |
| `salary` | `['string', 'null']` | Best-effort regex/metadata match, never fabricated. |
| `scraped_at` | `string` | Wall-clock time of row construction. |

#### Example output

```json
{
  "job_id": 7954688,
  "company": "stripe",
  "company_name": "Stripe",
  "title": "Staff Software Engineer",
  "department": "Engineering",
  "location": "San Francisco, CA",
  "remote": false,
  "absolute_url": "https://stripe.com/jobs/search?gh_jid=7954688",
  "posted_at": "2026-07-01T09:00:00-04:00",
  "updated_at": "2026-07-27T11:17:30-04:00",
  "description_html": "<h2>Who we are</h2><p>...</p>",
  "description_text": "Who we are\n...",
  "salary": null,
  "scraped_at": "2026-07-29T12:00:00Z"
}
```

### 💰 Pricing

Pay-Per-Event — you pay only when these events fire:

| Event | USD | What it is |
|---|---:|---|
| `actor-start` | $0.005 | One-off warm-up charge per run |
| `result` | $0.0015 | Per dataset item |

Example: 1 000 results at the rates above ≈ **$1.50**. No subscription, no minimum, no card to start — Apify gives every new account $5 of free credit.

### 🚧 Limitations

remote is a best-effort heuristic (matches "remote" in the location or office name), not an authoritative Greenhouse field. salary is best-effort regex/metadata extraction and is frequently null. department and location are single free-text passthrough fields with no structured breakdown — Greenhouse itself provides none. Greenhouse-only; see the sibling ATS Actors for Lever, Ashby, Workday, and SmartRecruiters coverage.

### ❓ FAQ

**Do I need a Greenhouse API key or login?**

No — every request hits Greenhouse's own public board JSON endpoint (boards-api.greenhouse.io).

**Where do I find a company's board token?**

It's the path segment in https://boards.greenhouse.io/{token} (or job-boards.greenhouse.io/{token}) — you can also paste the full URL and we normalize it.

**Why is salary null on most rows?**

Greenhouse has no dedicated compensation field; we only surface a value when the posting text or metadata contains a clear match.

**Does this replace multi-ats-jobs-scraper?**

No — that Actor covers Greenhouse, Lever, and Ashby shallowly across three ATSes. This Actor is Greenhouse-only, deep: full descriptions, filters, and salary mining.

**Why a Greenhouse-only Actor instead of a generic job-board scraper?**

Job-board scraping is proven demand on the Apify Store — generic multi-source actors like fantastic-jobs have racked up 22.8k+ lifetime runs. That demand comes with a quality ceiling: generic coverage means shallow per-job fields. Going deep on one ATS lets us ship department/location/remote filtering, full HTML+plain-text descriptions, and salary mining that a jack-of-all-ATSes actor typically skips.

### 💬 Your feedback

Spotted a bug, hit a weird edge case, or need a new field? Open an
issue on the Actor's **Issues** tab on Apify Console — we ship
fixes weekly and we read every report.

***

<div align="center">

Built by **[Devil Scrapes](https://apify.com/DevilScrapes)** 😈 — a small fleet of
opinionated public-data Actors. Honest pricing, real engineering, zero fine print.

</div>

# Actor input Schema

## `boardTokens` (type: `array`):

Greenhouse board token, e.g. "stripe", or a full boards.greenhouse.io / job-boards.greenhouse.io board URL — both normalize to the token.

## `departmentFilter` (type: `string`):

Case-insensitive substring match against any entry in a job's departments\[].name. Leave empty to keep every department.

## `locationFilter` (type: `string`):

Case-insensitive substring match against a job's location.name. Leave empty to keep every location.

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

Keep only jobs whose heuristic remote flag is true (location or office name contains "remote"). Best-effort, not an authoritative Greenhouse field.

## `maxItems` (type: `integer`):

Cap total rows emitted across ALL boards combined in the run, applied after filtering.

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

Apify Proxy configuration. No anti-bot signal observed on this target, so no RESIDENTIAL group is required by default.

## Actor input object example

```json
{
  "boardTokens": [
    "stripe",
    "airbnb"
  ],
  "remoteOnly": false,
  "maxItems": 500,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `datasetItems` (type: `string`):

All dataset items as JSON.

## `datasetItemsCsv` (type: `string`):

Same data exported to CSV.

## `datasetView` (type: `string`):

Open the run dataset in the 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 = {
    "boardTokens": [
        "stripe",
        "airbnb"
    ],
    "maxItems": 500,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("devilscrapes/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 = {
    "boardTokens": [
        "stripe",
        "airbnb",
    ],
    "maxItems": 500,
    "proxyConfiguration": { "useApifyProxy": True },
}

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

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

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

```

## CLI example

```bash
echo '{
  "boardTokens": [
    "stripe",
    "airbnb"
  ],
  "maxItems": 500,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call devilscrapes/greenhouse-jobs-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=devilscrapes/greenhouse-jobs-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/acts/qrN2MEpLczsl5JlWF/builds/KVtFBXL1Ljel4oFjn/openapi.json
