# Greenhouse Jobs Scraper (`lightmoon/greenhouse-jobs-scraper`) Actor

Scrape open jobs from any Greenhouse board to CSV, Excel or JSON: title, location, department, exact posting and update dates, and the full job description. Paste a company name, a careers URL or a board token - it resolves either. From $0.55 per 1,000 jobs.

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

## Pricing

from $0.95 / 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.
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?

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

### Every open role on any Greenhouse board, with the full description

Give it company board names, careers URLs, or a board you already know. Get their
open jobs as CSV, Excel or JSON — title, location, department, the exact posting
and update dates, and the complete job description. No account, no API key.

**You do not need to know the board token.** Paste `gitlab`, or
`job-boards.greenhouse.io/gitlab`, or the company's own careers URL — whichever
you have. The output says which one it resolved.

#### One row, from a real run

`gitlab`, read on 27 August 2026. Complete except the description, cut after its
first sentence.

```json
{
  "platform": "greenhouse",
  "companyToken": "gitlab",
  "companyName": "GitLab",
  "companyBoardUrl": "https://job-boards.greenhouse.io/gitlab",
  "jobId": "8503792002",
  "internalJobId": "6396658002",
  "requisitionId": "6263",
  "title": "Account Executive - Italy",
  "jobUrl": "https://job-boards.greenhouse.io/gitlab/jobs/8503792002",
  "applyUrl": "https://job-boards.greenhouse.io/gitlab/jobs/8503792002",
  "locationRaw": "Remote, Italy",
  "isRemote": true,
  "departments": "EMEA - Commercial",
  "offices": "Italy",
  "postedAt": "2026-04-17T05:58:03-04:00",
  "updatedAt": "2026-08-10T16:52:46-04:00",
  "language": "en",
  "descriptionHtml": "<div class=\"content-intro\"><p>GitLab is the intelligent orchestration platform for DevSecOps…</p>",
  "descriptionText": "GitLab is the intelligent orchestration platform for DevSecOps…",
  "scrapedAt": "2026-08-27T15:37:12+00:00"
}
```

### Fields

**21 per job**, counted. Descriptions are optional and off by default, so a run
that only needs titles and links does not pay for the text.

| | |
|---|---|
| where it came from | `platform`, `companyToken`, `companyName`, `companyBoardUrl` |
| identifiers | `jobId`, `internalJobId`, `requisitionId` |
| the role | `title`, `jobUrl`, `applyUrl` |
| where | `locationRaw`, `isRemote`, `offices` |
| org | `departments` |
| dates | `postedAt`, `updatedAt` — both real timestamps, not "3 days ago" |
| the text | `descriptionHtml`, `descriptionText`, `language` |
| when read | `scrapedAt` |

`postedAt` and `updatedAt` are what make a watch list work: run it daily, keep
what is newer than your last run, and you have a feed of what opened today.

### What it costs

Pay per event, and there is one paid event: **a job stored**.

| | GOLD per 1,000 |
|---|---|
| **Job** | **$0.55** |

**You are not billed for** a board that does not exist, a company name that
turned out not to be on this platform, a duplicate line in your list, or a job
removed by one of your filters.

- One company with 250 open roles: **$0.14**.
- A watch list of 50 companies, first full run ≈ 2,000 jobs: **$1.10**. Every
  day after that you pay only for what changed.
- Apify's $5 of monthly free credit: about **4,500 jobs**.

A company board is one request and comes back in about a second. Descriptions
cost one extra request per job and are worth turning on only when you need the
text.

### Limits

- **Public boards only.** A board behind a login, or one the company has closed,
  is reported by name with the reason — never silently missing.
- **This platform only.** A company on a different applicant-tracking system
  returns nothing here, and says so rather than pretending the board is empty.
- **The company name comes from the board itself** where the platform publishes
  it. Where it does not, you still get the token, which is unique.
- **No salary field**, because this platform does not publish one in a
  machine-readable form. Where a company writes pay into the description text,
  it is in `descriptionText`.
- **On the free plan** everything works at the FREE rate until Apify's monthly
  platform credit runs out; the run then stops cleanly and keeps what it has.

# Actor input Schema

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

One company per line. Paste either the Greenhouse board name (`stripe`) or the board URL you were looking at (`https://job-boards.greenhouse.io/stripe`). A job URL works too — the company is read out of it.

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

On by default. Descriptions also bring departments and offices with them — turning this off makes each row smaller and plainer, not cheaper.

## `maxJobs` (type: `integer`):

Stops the run once this many rows are stored. Set 0 for no limit.

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

Keeps one large employer from filling the whole result. 0 means no per-company cap.

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

Case-insensitive. A job is kept if its title contains at least one of these.

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

Case-insensitive. Drops jobs whose title contains any of these — useful for `intern`, `senior`, `manager`.

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

Matched against the posting's location and offices, for example `Berlin`, `Germany`, `EMEA`.

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

For example `Engineering`, `Sales`. Requires descriptions to be on, since that is where departments arrive.

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

Keeps only jobs that say they are remote. Postings that do not mention it either way are dropped rather than guessed at.

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

Keeps jobs first published on or after this date.

## `updatedAfter` (type: `string`):

Keeps jobs edited on or after this date — the usual way to fetch only what changed since the last run.

## `concurrency` (type: `integer`):

Raise it for long company lists. Each board is a single request, so there is rarely a reason to go far above the default.

## Actor input object example

```json
{
  "companies": [
    "stripe",
    "gitlab",
    "https://job-boards.greenhouse.io/monzo"
  ],
  "includeDescription": true,
  "maxJobs": 100,
  "maxJobsPerCompany": 20,
  "titleKeywords": [],
  "excludeTitleKeywords": [],
  "locationKeywords": [],
  "departmentKeywords": [],
  "remoteOnly": false,
  "concurrency": 5
}
```

# Actor output Schema

## `overview` (type: `string`):

No description

## `descriptions` (type: `string`):

No description

## `all` (type: `string`):

No description

# 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",
        "gitlab",
        "https://job-boards.greenhouse.io/monzo"
    ],
    "maxJobsPerCompany": 20,
    "titleKeywords": [],
    "excludeTitleKeywords": [],
    "locationKeywords": [],
    "departmentKeywords": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("lightmoon/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",
        "gitlab",
        "https://job-boards.greenhouse.io/monzo",
    ],
    "maxJobsPerCompany": 20,
    "titleKeywords": [],
    "excludeTitleKeywords": [],
    "locationKeywords": [],
    "departmentKeywords": [],
}

# Run the Actor and wait for it to finish
run = client.actor("lightmoon/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",
    "gitlab",
    "https://job-boards.greenhouse.io/monzo"
  ],
  "maxJobsPerCompany": 20,
  "titleKeywords": [],
  "excludeTitleKeywords": [],
  "locationKeywords": [],
  "departmentKeywords": []
}' |
apify call lightmoon/greenhouse-jobs-scraper --silent --output-dataset

```

## MCP server setup

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