# Greenhouse Jobs Scraper (`novasphere26/greenhouse-jobs-feed`) Actor

Scrape open jobs from any company's Greenhouse job board, with department, seniority, job function and stated pay. Or get only new and closed jobs since your last run. No login, no proxy.

- **URL**: https://apify.com/novasphere26/greenhouse-jobs-feed.md
- **Developed by:** [Novasphere](https://apify.com/novasphere26) (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 $1.20 / 1,000 job results

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

Get the open jobs from any company's **Greenhouse** job board in a clean format. Run it once for the full list, or on a schedule to get only the jobs that were **posted or taken down since the last run**.

It reads Greenhouse's public job board API, so there's no login, no browser and no proxy. A board usually takes a second or two.

### Use it to

- Watch hiring at a list of companies: competitors, customers, prospects, a fund's portfolio
- Send new roles to Slack, email, a spreadsheet or your own job board
- Give an AI agent a tool for "who is hiring for X at these companies"
- Build hiring-signal data for sales or research

### Input

Add one job board per line, in any of these forms:

| You enter | Example |
|---|---|
| The job board link | `https://job-boards.greenhouse.io/stripe` |
| A link to any job on it | `https://job-boards.greenhouse.io/gitlab/jobs/8860302002` |
| The company's website | `stripe.com` (the Actor follows its careers page to the Greenhouse board) |
| The board or company name | `stripe` |

Older `boards.greenhouse.io` links, embedded boards (`...embed/job_board?for=stripe`) and EU boards (`job-boards.eu.greenhouse.io`) work too. The board name is the part after `greenhouse.io/` in the board's address.

Then pick what to output:

- **All open jobs**: everything currently on each board that passes your filters.
- **Only changes since the last run**: jobs that are new since the previous run, plus jobs that have been taken down. Runs with the same **Feed name** share their memory, so use one feed name per watchlist. The first run counts every job as new, unless you tick **Skip existing jobs on the first run**.

Optional filters: words in the job title (include or exclude), seniority, job function, location, department, remote / hybrid, and posted within the last N days.

```json
{
    "companies": ["https://job-boards.greenhouse.io/stripe", "gitlab", "airbnb"],
    "mode": "changes",
    "feedName": "competitors",
    "titleKeywords": ["engineer", "product"],
    "departmentKeywords": ["engineering"]
}
```

### Output

One item per job:

```json
{
    "change": "new",
    "company": "GitLab",
    "title": "Account Executive - France",
    "location": "Remote, France",
    "workplaceType": "remote",
    "employmentType": null,
    "seniority": null,
    "jobFunction": "sales",
    "department": "EMEA - Commercial",
    "team": null,
    "salary": null,
    "salaryText": null,
    "postedAt": "2026-10-02T15:31:50Z",
    "url": "https://job-boards.greenhouse.io/gitlab/jobs/8860302002",
    "applyUrl": "https://job-boards.greenhouse.io/gitlab/jobs/8860302002",
    "platform": "greenhouse",
    "board": "gitlab",
    "jobId": "8860302002",
    "allLocations": ["Remote, France", "France"],
    "country": null,
    "updatedAt": "2026-10-15T19:24:46Z",
    "firstSeenAt": "2026-10-22T08:00:14Z",
    "closedAt": null,
    "checkedAt": "2026-10-22T08:00:14Z"
}
```

| Field | What it is |
|---|---|
| `change` | `new` or `closed` in changes mode; empty when listing all open jobs |
| `company` | The company name Greenhouse shows |
| `title`, `location` | As the employer lists them |
| `allLocations` | The job's location plus its office locations |
| `department` | The job's department |
| `salary`, `salaryText` | Pay range, when the employer states one in the description |
| `workplaceType` | `remote` or `hybrid` when the location says so |
| `employmentType` | Only when the employer adds it as a custom field, so often empty |
| `seniority` | `intern`, `entry`, `senior`, `lead`, `manager`, `director` or `executive`, read from the job title. Empty when the title doesn't say |
| `jobFunction` | Area of work (engineering, sales, data, design, people, ...), read from the job title, then the department or team |
| `postedAt`, `updatedAt` | When the job was first published and last updated (ISO 8601, UTC) |
| `url`, `applyUrl` | The job's page; many employers send it to their own careers site |
| `platform`, `board`, `jobId` | Where the job came from. `jobId` is Greenhouse's job ID |
| `firstSeenAt`, `closedAt` | Changes mode: when this feed first saw the job, and when it noticed it was gone |
| `checkedAt` | When the board was read |
| `description` | Plain-text job description, only if you tick **Include the job description** |

`team` and `country` are always empty: Greenhouse's public job list doesn't include them.

Each run also saves a summary to its key-value store under `OUTPUT`: boards checked, boards not found or unreadable (with the reason), and job counts.

### Running it as a change feed

1. Set **What to output** to **Only changes** and give the feed a name.
2. Add a schedule, for example daily.
3. Connect an integration (webhook, Slack, email, Google Sheets, Zapier, Make) to receive each run's new and closed jobs.

### Using it from an AI agent or code

Every input is plain JSON, so it works through the Apify API, the Python and JavaScript clients, and Apify's MCP server (`https://mcp.apify.com`) for Claude, ChatGPT and other agents. The input also accepts a comma-separated string for `companies` and the keyword fields.

### Good to know

- Not affiliated with or endorsed by Greenhouse. The name only says which job boards this reads.
- Only public job-board data is read. There's no candidate or employee data.
- Job descriptions are the employer's text. Respect each employer's terms if you republish them.
- In changes mode every job on a board is tracked, and filters only decide what's output, so changing a filter later won't resend old jobs.
- In changes mode, if a run stops at your maximum charge, the changes it couldn't output are kept and come out on the next run.
- The feed's memory is kept in your account in a key-value store named `greenhouse-jobs-feed-<feed name>`. Delete it to start the feed fresh.
- Boards that can't be found or read are listed in the run summary; the rest of the run carries on.
- Tracking companies on other job boards too? [The multi-platform version](https://apify.com/novasphere26/ats-jobs-feed) reads Greenhouse, Lever, Ashby, Workday, SmartRecruiters, Workable, Recruitee, Personio and BambooHR in one run. To search new jobs across thousands of companies at once, try [Career Site Job Search](https://apify.com/novasphere26/company-jobs-search).

# Changelog

This Actor's version history is a separate document: https://apify.com/novasphere26/greenhouse-jobs-feed/changelog.md

# Actor input Schema

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

One per line. Paste the company's Greenhouse job board link (for example https://job-boards.greenhouse.io/stripe), a link to any job on it, the company's website (stripe.com) or just its name (stripe).

## `mode` (type: `string`):

All open jobs lists everything currently on each board. Only changes lists jobs that are new since the last run with the same feed name, plus jobs that have been taken down. The first changes run treats every job as new, unless you tick the option below.

## `feedName` (type: `string`):

Only used for changes. Runs with the same feed name share their memory of which jobs were already seen. Use a different name for each separate watchlist.

## `baselineOnFirstRun` (type: `boolean`):

Only used for changes. On the first run for a board, remember its current jobs without outputting them, so you only get jobs posted from now on.

## `includeClosed` (type: `boolean`):

Only used for changes. Also output jobs that were on the board last run and are gone now.

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

Keep jobs whose title contains at least one of these words or phrases (not case sensitive). Example: engineer, data scientist.

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

Drop jobs whose title contains any of these. Example: intern, manager.

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

Keep jobs where any listed location contains one of these. Example: London, Sydney, United States.

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

Keep jobs whose department or team contains one of these. Example: engineering, sales. Workday sites don't give a department, so their jobs are left out when this is set.

## `seniorityLevels` (type: `array`):

Keep jobs at these levels, read from the job title. Titles that don't say a level are left out when this is set.

## `jobFunctions` (type: `array`):

Keep jobs in these areas, read from the job title, then the department or team.

## `workplaceTypes` (type: `array`):

Keep only these workplace types. Jobs whose board doesn't say are left out when this is set.

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

Keep jobs posted within this many days. Leave empty for any age.

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

Add the full job description as plain text. This makes each result much larger.

## `maxJobsPerBoard` (type: `integer`):

Output at most this many jobs per board in one run (newest first). In changes mode, the rest are held back for the next run. Leave empty for no limit.

## Actor input object example

```json
{
  "companies": [
    "https://job-boards.greenhouse.io/stripe",
    "https://job-boards.greenhouse.io/gitlab",
    "airbnb"
  ],
  "mode": "all",
  "feedName": "default",
  "baselineOnFirstRun": false,
  "includeClosed": true,
  "includeDescription": false,
  "maxJobsPerBoard": 20
}
```

# Actor output Schema

## `results` (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": [
        "https://job-boards.greenhouse.io/stripe",
        "https://job-boards.greenhouse.io/gitlab",
        "airbnb"
    ],
    "maxJobsPerBoard": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("novasphere26/greenhouse-jobs-feed").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": [
        "https://job-boards.greenhouse.io/stripe",
        "https://job-boards.greenhouse.io/gitlab",
        "airbnb",
    ],
    "maxJobsPerBoard": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("novasphere26/greenhouse-jobs-feed").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": [
    "https://job-boards.greenhouse.io/stripe",
    "https://job-boards.greenhouse.io/gitlab",
    "airbnb"
  ],
  "maxJobsPerBoard": 20
}' |
apify call novasphere26/greenhouse-jobs-feed --silent --output-dataset

```

## MCP server setup

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

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/lB5mvBFTb3YFUKZpU/builds/O9C1Z2oslD0TeZfjX/openapi.json
