# Greenhouse Jobs Scraper | Hiring Updates & Company Watchlists (`signalfoundryhq/greenhouse-hiring-radar`) Actor

Export public Greenhouse vacancies by company, title, department and location. Track new or changed job versions. $1.50 per 1,000 records; no start fee.

- **URL**: https://apify.com/signalfoundryhq/greenhouse-hiring-radar.md
- **Developed by:** [Signal Foundry](https://apify.com/signalfoundryhq) (community)
- **Categories:** Jobs, Business, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.28 / 1,000 delivered job post versions

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 | Hiring Updates & Company Watchlists

Retrieve the current public vacancies of employers on your Greenhouse watchlist, then keep only newly observed or changed job versions on repeat calls. Useful for recruitment research, company hiring signals and job-feed integrations.

### Pricing

**$1.50 per 1,000 delivered records** ($0.0015 per result). No Actor start fee. Platform usage is included in this Actor's price. Set a run charge limit in Apify to bound the cost. Skipped duplicate versions are not charged.

For example, 100 delivered records cost $0.15. Zero delivered records have no result charge.

### Quick start

Open Input, supply your filters and click Start. The default dataset can be exported as JSON, CSV or Excel. Each public Example provides a ready-to-use preset.

```json
{
  "boardTokens": [
    "cloudflare"
  ],
  "titleContains": "engineer",
  "maxJobsScanned": 1000,
  "onlyNew": false,
  "maxItems": 25
}
```

### Repeat calls and change tracking

Use **onlyNew: true** and the same **stateKey** for a stable feed. The first call delivers current matching versions. Later calls skip already delivered versions and charge only for newly delivered versions. Use a different stateKey for a different query. Reset saved history only when you want a replay.

History retains the most recent 25,000 fingerprints. Versions that leave this bounded history may be delivered again. Collection scans within the configured scan limit before the delivery limit is applied, so unchanged rows cannot consume maxItems. Use onlyNew: false for complete snapshots within these limits.

### Search skills in job descriptions

Use **descriptionContains** for a case-insensitive substring in the public description, such as `python`. It combines with the title, department, location and update-date filters before results are delivered or charged. Matching uses extracted text, excluding HTML attributes, scripts and styles. Source introductions and conclusions are included when Greenhouse puts them in the description.

Set **includeDescription: true** to add **descriptionText** to each result for job-feed and search integrations. Entity-encoded source HTML is converted to whitespace-normalized text; no HTML or scripts are executed. Missing source descriptions produce an empty string and cannot match a nonempty description filter. The Actor does not infer skills, salary, remote eligibility or other attributes from this text.

```json
{
  "boardTokens": ["anthropic"],
  "titleContains": "engineer",
  "descriptionContains": "python",
  "includeDescription": true,
  "maxJobsScanned": 1000,
  "onlyNew": false,
  "maxItems": 10
}
```

Both options are off by default. Existing requests retain their output fields and version identifiers. With description output enabled, text changes also create a new version fingerprint. Use a separate **stateKey** when changing a feed's filters or description-output setting. A filter can return no matches within the scan limit.

### Real output example

Captured from the live source on **2026-10-02**. Selected fields are shown below; source dates are preserved.

```json
{
  "companyName": "Cloudflare",
  "title": "AI Security Research & Red Team Engineer",
  "jobId": "8097321",
  "location": "Hybrid",
  "departments": [
    "Security"
  ],
  "offices": [
    {
      "name": "Austin, TX",
      "location": "Austin, TX, United States"
    },
    {
      "name": "New York, NY",
      "location": "New York, New York, United States"
    }
  ],
  "firstPublished": "2026-08-10T11:02:09-04:00",
  "updatedAt": "2026-10-02T14:24:13-04:00",
  "jobUrl": "https://boards.greenhouse.io/cloudflare/jobs/8097321?gh_jid=8097321",
  "signalId": "cloudflare:8097321:6626369bc7c5d365a8c1a41a"
}
```

### API integration

Send your JSON input to the synchronous Apify endpoint. Supply your own Apify token in the Authorization header.

```bash
curl --request POST \
  'https://api.apify.com/v2/acts/signalfoundryhq~greenhouse-hiring-radar/run-sync-get-dataset-items?maxTotalChargeUsd=0.05' \
  --header 'Authorization: Bearer YOUR_APIFY_TOKEN' \
  --header 'Content-Type: application/json' \
  --data '{"boardTokens":["cloudflare"],"titleContains":"engineer","maxJobsScanned":1000,"onlyNew":false,"maxItems":10}'
```

For runs longer than the synchronous endpoint timeout, start an asynchronous Actor run, wait for its final status and retrieve the default dataset. The default key-value store OUTPUT record contains delivered count, skipped duplicates and charge-limit status.

### Coverage and limitations

- Provide explicit employer boards. This Actor does not discover every Greenhouse customer or search a global jobs database.
- Up to **25 boards**, **10,000 raw listings scanned**, and **5,000 delivered records** per run. Boards are examined in supplied order. Increase the scan limit when combining filters or several boards.
- One result is one public **job post version**, identified by board token, job ID and a fingerprint of emitted metadata. Multiple location-specific posts can represent one underlying job; use internalJobId if available to group them.
- Title, department and location filters are case-insensitive substrings. Location checks displayed locations and offices. Remote eligibility, salary, visa sponsorship and employer attributes are not inferred.
- The record includes public title, company name, departments, offices, source dates and application URL. Public description text is optional; applicant data is not returned. With description output off, description-only edits may still create a row if the source updated_at timestamp changes.
- Jobs removed from a board disappear from later snapshots; this Actor does not emit deletion/closed-job events.
- An invalid or inaccessible board causes a failed run rather than a silent partial result.

### Source and reuse

Data is fetched through the documented, unauthenticated [Greenhouse public Job Board API](https://docs.greenhouse.io/job-board.html). This Actor is an independent integration and is not affiliated with Greenhouse or the employers. Source content remains subject to the relevant employer and provider rights; this Actor does not grant a separate licence to job-description text.

### Support

Report a reproducible issue through this Actor's Issues tab. Include the run ID and filters. Never post an Apify token or private information.

# Actor input Schema

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

1 to 25 board tokens or public boards.greenhouse.io / job-boards.greenhouse.io URLs. Explicit employer watchlists only.

## `titleContains` (type: `string`):

Case-insensitive substring of the job title, such as engineer.

## `locationContains` (type: `string`):

Case-insensitive match in the displayed location or office location. Geography is not inferred.

## `departmentContains` (type: `string`):

Case-insensitive substring in public department names.

## `descriptionContains` (type: `string`):

Case-insensitive substring in extracted public description text. Combines with the other filters before delivery and charging. Source introductions/conclusions may also match.

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

Add descriptionText to results. HTML is converted to text without executing it. Missing source content becomes an empty string. Use a separate history key when changing this setting.

## `updatedSince` (type: `string`):

Optional inclusive YYYY-MM-DD filter on the source update date.

## `maxJobsScanned` (type: `integer`):

Maximum raw listings examined across boards, in supplied board order. Narrow filters can return fewer rows.

## `stateKey` (type: `string`):

Keep the same key across repeated calls for one feed. Use a different key for a different query.

## `onlyNew` (type: `boolean`):

Skip record versions already delivered with this history key. Changes produce a new fingerprint; field-level diffs and deletion events are not produced.

## `resetState` (type: `boolean`):

Replay current matching records by clearing remembered fingerprints.

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

Maximum matching job versions written to the dataset.

## Actor input object example

```json
{
  "boardTokens": [
    "cloudflare"
  ],
  "includeDescription": false,
  "maxJobsScanned": 5000,
  "stateKey": "default",
  "onlyNew": false,
  "resetState": false,
  "maxItems": 100
}
```

# Actor output Schema

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

No description

## `summary` (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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("signalfoundryhq/greenhouse-hiring-radar").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 = {}

# Run the Actor and wait for it to finish
run = client.actor("signalfoundryhq/greenhouse-hiring-radar").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 '{}' |
apify call signalfoundryhq/greenhouse-hiring-radar --silent --output-dataset

```

## MCP server setup

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

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/O8yDbuznb7NSgOR2Z/builds/sp8JOWQZuSXOnGJEf/openapi.json
