# Greenhouse Jobs Scraper — Keyword, Location, Date (`keyman98/greenhouse-jobs-scraper`) Actor

Get job openings from any company on Greenhouse via the official API. Filter by keyword, location, department and days since posted. No personal data — public job listings only.

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

## Pricing

from $2.00 / 1,000 job postings

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 Jobs Scraper

Scrape job postings and job listings from any company careers page built on **Greenhouse**, the applicant tracking system (ATS). Pulls data through Greenhouse's public, official API — not by scraping the web page — and exports the results to CSV, Excel, or JSON.

### What you get (output fields)

For each job posting, one dataset row with:

- `company` — `boardToken` and `name` of the company this job belongs to.
- `jobId` — Greenhouse internal job id.
- `title` — job title.
- `location` — job location, as published by the company.
- `departments` — department name(s) this job belongs to.
- `offices` — office name(s) this job belongs to.
- `url` — public URL of the job posting.
- `updatedAt` — when the job posting was last updated (ISO 8601).
- `firstPublished` — when the job posting was first published (ISO 8601), when available.
- `description` — job description as cleaned plain text (null if not requested).
- `descriptionHtml` — job description as original HTML (null unless requested).
- `metadata` — extra fields the company attached to the job (e.g. employment type).
- `scrapedAt` — when this row was collected (ISO 8601, UTC).

### Who it's for

- **Recruiting** — track which positions a company opens, when, and where.
- **B2B lead generation** — a company hiring for a specific role (e.g. "DevOps", "Sales") is often a qualified lead for whoever sells tools or services to that team.
- **Job market research** — see which skills, roles, or locations a company or industry is hiring for right now.

This Actor does not extract personal data of recruiters or candidates — only public job posting data (title, location, department, description) that the company itself publishes.

### How to find a company's board token

Every company on Greenhouse has a careers page shaped like:

```
https://boards.greenhouse.io/gitlab
https://job-boards.greenhouse.io/gitlab
```

The part after `greenhouse.io/` (here, `gitlab`) is the **board token**. You can paste this kind of URL directly into the "Companies" field — it is recognized and converted automatically. If a company does not use Greenhouse, no page in this format will exist for it.

### How to use

1. **Companies** — paste one or more board tokens or URLs, e.g. `gitlab`, `stripe`, `https://boards.greenhouse.io/coinbase`.
2. **Optional filters** — title keywords (`keywords`), location (`locations`), department (`departments`), or only jobs updated within the last N days (`postedWithinDays`).
3. **Run the Actor.** Each job posting that passes the filters becomes one row in the dataset.

Description options: *Include description* adds the cleaned plain-text description; *Include description (original HTML)* also adds the original HTML, if you need to format it yourself.

### Input example (JSON)

```json
{
  "companies": ["gitlab", "https://boards.greenhouse.io/stripe"],
  "keywords": ["engineer"],
  "locations": [],
  "departments": [],
  "includeDescription": true,
  "includeDescriptionHtml": false,
  "maxJobsPerCompany": 500
}
```

### Output example (JSON)

```json
{
  "company": { "boardToken": "gitlab", "name": "GitLab" },
  "jobId": 8556658002,
  "title": "AI Engineer",
  "location": "Remote, Bangalore",
  "departments": ["Enterprise Applications"],
  "offices": ["India"],
  "url": "https://job-boards.greenhouse.io/gitlab/jobs/8556658002",
  "updatedAt": "2026-09-14T16:01:39-04:00",
  "firstPublished": "2026-05-22T09:16:29-04:00",
  "description": "GitLab is the intelligent orchestration platform...",
  "descriptionHtml": null,
  "metadata": { "Quota Coverage Type": "n/a" },
  "scrapedAt": "2026-09-23T10:00:00+00:00"
}
```

### If a company is not found or does not respond

That company is **skipped**: the rest of the list keeps running, the run does not fail, and you are **not charged** for that company. The reason (board not found, network error) is written to the run log.

### Pricing

Pay only for job postings actually returned in the results, after filters — no fixed cost, nothing charged for companies that could not be found or fetched. Pricing model: **pay-per-event**.

| Event | When it's charged | Price |
| --- | --- | --- |
| `job-scraped` | a job posting was returned in the results (after filters) | 0.002 USD |

### Limitations

- Works only on companies that use **Greenhouse** as their ATS — not LinkedIn, Lever, Ashby, Workday, or others.
- No personal data of recruiters or candidates — only job posting data.
- `maxJobsPerCompany` caps how many matching jobs are returned per company, to keep time and cost predictable on very large boards.
- Does not bypass rate limits or protections: it only uses Greenhouse's public, documented API endpoint.

### FAQ

#### Can I use this on LinkedIn or other job boards?

No. It works only on companies that use Greenhouse as their applicant tracking system.

#### Does it extract recruiter emails or contact details?

No, by design: only job posting data (title, location, department, description), never personal data.

#### A company does not appear in the results, why?

Either the board token is wrong, or that company does not use Greenhouse. The run still continues for the other companies, and you are not charged for the one not found.

#### Am I charged if a company is not found?

No. You are only charged for job postings actually returned in the results.

#### How do I filter by keyword, location, or department?

Use the `keywords`, `locations`, and `departments` input fields — each matches case-insensitively against the job title, location, or department, and can hold multiple terms. Leave a filter empty to keep everything.

#### Can I get the full HTML of the job description?

Yes, enable *Include description (original HTML)* in the input.

#### Is this legal? Am I scraping personal data?

No personal data is involved. This Actor only reads job postings that the company itself has published on its public Greenhouse careers page — no recruiter or candidate personal data of any kind.

#### Can I monitor a company for new postings over time?

Yes. Set `postedWithinDays` to a short window (e.g. 1-3) and run the Actor on a schedule (Apify's built-in scheduler) — each run then only returns postings updated since the last check.

#### Which companies use Greenhouse?

Any company with a careers page shaped like `boards.greenhouse.io/<token>` or `job-boards.greenhouse.io/<token>` is covered — Greenhouse is one of several ATS providers, alongside Lever, Ashby, and others this Actor does not support. See "How to find a company's board token" above.

#### Can I use this through the Apify API or an MCP server?

Yes. Like any Apify Actor, you can run it and read results through the standard Apify API, or through the Apify MCP server if you use Claude, Cursor, or another MCP-enabled client.

### Export

Results can be downloaded from the Apify dataset as JSON, CSV, or Excel, or accessed via the Apify API.

# Actor input Schema

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

Greenhouse board tokens (e.g. "gitlab") or career page URLs (e.g. https://boards.greenhouse.io/gitlab or https://job-boards.greenhouse.io/gitlab). See README for how to find the board token.

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

Only keep jobs whose title contains at least one of these words (case-insensitive). Leave empty to keep all titles.

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

Only keep jobs whose location contains at least one of these words (case-insensitive). Leave empty to keep all locations.

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

Only keep jobs whose department contains at least one of these words (case-insensitive). Leave empty to keep all departments.

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

Include the job description as cleaned plain text.

## `includeDescriptionHtml` (type: `boolean`):

Also include the original HTML of the job description, unmodified.

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

Only keep jobs last updated within this many days. Leave empty for no limit.

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

Stop after this many matching jobs for each company (keeps cost and run time predictable for very large boards).

## Actor input object example

```json
{
  "companies": [
    "gitlab"
  ],
  "keywords": [],
  "locations": [],
  "departments": [],
  "includeDescription": true,
  "includeDescriptionHtml": false,
  "maxJobsPerCompany": 500
}
```

# Actor output Schema

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

All results in the default dataset (JSON, CSV, Excel).

# 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": [
        "gitlab"
    ],
    "keywords": [],
    "locations": [],
    "departments": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("keyman98/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": ["gitlab"],
    "keywords": [],
    "locations": [],
    "departments": [],
}

# Run the Actor and wait for it to finish
run = client.actor("keyman98/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": [
    "gitlab"
  ],
  "keywords": [],
  "locations": [],
  "departments": []
}' |
apify call keyman98/greenhouse-jobs-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,keyman98/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/SaiBNOcAxNO9dsser/builds/HruagHBJh3UtUxU0V/openapi.json
