# Greenhouse Jobs Scraper API (`kman-br/greenhouse-jobs`) Actor

Job postings from public Greenhouse job boards through the same public board API the boards use. Title, location, departments, posting date, job description as clean text and HTML, and recruiter contacts removed.

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

## Pricing

Pay per event

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

Job postings from public **Greenhouse job boards** (`boards.greenhouse.io/{board}`), read from the public Job Board API that the boards use. One item per job, with the description as clean text and HTML, and recruiter contact details removed.

> Independent tool. Not affiliated with, endorsed or sponsored by Greenhouse Software, Inc. or any employer whose board it reads. Company names and trademarks belong to their owners.

### What it does

- Takes one or more board tokens or board URLs: `stripe`, `https://boards.greenhouse.io/stripe` or `https://job-boards.greenhouse.io/stripe`. Any other host (for example a company site with a `?gh_jid=` link) gets an error item with no charge.
- Makes one request per board: `GET https://boards-api.greenhouse.io/v1/boards/{board}/jobs`, with `?content=true` only when `includeDescription` is on. No authentication is used.
- Filters run on the data already downloaded. There are no per-job requests.
- Never logs in and never calls application endpoints. No candidate data is read or stored.

### Input

```json
{
  "boards": ["stripe"],
  "searchText": "engineer",
  "departmentContains": "Engineering",
  "locationContains": "Dublin",
  "postedWithinDays": 30,
  "maxItems": 20,
  "includeDescription": true
}
```

| Field | Type | Default | Notes |
|---|---|---|---|
| `boards` | string\[] | required | Board tokens or board URLs. Max 50 per run. Duplicates count once. |
| `searchText` | string | — | Case- and accent-insensitive. Matches the title and, when `includeDescription` is on, the description. |
| `locationContains` | string | — | Case- and accent-insensitive, on the job location. |
| `departmentContains` | string | — | Case- and accent-insensitive, on the department names. |
| `postedWithinDays` | integer ≥ 0 | — | UTC calendar days from the first published date (the updated date if that is missing). `0` keeps jobs published today. |
| `maxItems` | integer | `50` | Items per run. Hard cap 1000, enforced in code. |
| `includeDescription` | boolean | `true` | Adds `descriptionText` and `descriptionHtml`. See "Large boards" below. |

### Output (one item per job)

```json
{
  "company": "Figma",
  "title": "Account Executive, Enterprise",
  "jobId": "5426468004",
  "url": "https://boards.greenhouse.io/figma/jobs/5426468004?gh_jid=5426468004",
  "location": "San Francisco, CA • New York, NY • United States",
  "departments": ["Sales"],
  "offices": ["US"],
  "postedDate": "2025-01-28T23:57:29.000Z",
  "updatedAt": "2026-07-22T09:37:08.000Z",
  "descriptionText": "Figma is growing our team of passionate creatives ...",
  "descriptionHtml": "<div class=\"content-intro\"><p>Figma is growing ...",
  "source_url": "https://boards.greenhouse.io/figma",
  "retrieved_at": "2026-10-23T16:16:02.486Z",
  "source_name": "Greenhouse",
  "status": "ok",
  "error_message": null,
  "warnings": ["contato_removido"]
}
```

- `status` is `ok` (a job, charged) or `erro` (a board or URL that could not be read, free). `error_message` says why.
- `postedDate` is Greenhouse's `first_published`; `updatedAt` is `updated_at`. Both are UTC.
- `descriptionHtml` is `null` when `includeDescription` is off.
- Warnings: `contato_removido` (a contact was removed), `descricao_indisponivel` (the job has no description in the response, so it is returned without one and not charged), `teto_margem:<board>`, `maxItems_limitado:<n>`.
- The key-value record `RUN_SUMMARY` has the run `status` (`ok`, `sem_resultado`, `parcial`, `fonte_indisponivel`, `erro_url`, `erro_input`), `completo`, the stop reason `motivo` (`maxItems`, `teto_cobranca`, `prazo_interno`, `teto_margem`), per-board HTTP status and counts, the charged events and a `margem` block.

### Pricing (pay per event)

| Event | When | Proposed price |
|---|---|---|
| `inicio-run` | Once per run, after the input is valid and at least one board is valid | US$0.005 |
| `job` | Each job with status `ok`. A job whose description was unavailable (warning `descricao_indisponivel`) is returned and not charged | US$0.001 |

Invalid input and runs where no board is valid are not charged. Error items are free. The maximum charge per run (`ACTOR_MAX_TOTAL_CHARGE_USD`) is respected.

### Limits

- Memory 256 MB, timeout 300 s. The Actor stops on its own at 270 s (or 20 s before the platform timeout, if that comes first) and keeps what it already saved.
- Requests run one at a time, at least 1 s apart. Up to 3 tries on HTTP 429 and 5xx, with `Retry-After` honored up to 10 s.
- A source failure never fails the run. A board that is down, returns 404 or returns a non-JSON page gets an `erro` item, and the run ends `SUCCEEDED`.
- **Download budget and large boards.** With descriptions on, a board comes in one response that includes every description. Before each request the Actor reserves the download its revenue can pay for: with only the start charge (US$0.005) that is about 15.8 MB, and each charged job adds about 4 MB. A board bigger than that is cut off mid-download and stops the run with `motivo: teto_margem` and nothing charged. Measured on 23/10/2026 with `content=true`: Stripe 5.6 MB (722 jobs), Figma 1.6 MB (140 jobs), Vercel 0.9 MB (95 jobs), Mercury 0.7 MB (75 jobs). Without descriptions the same boards are much smaller (Stripe: about 450 KB).
- **Cost guard.** Before each request the Actor checks that the estimated cost (compute time since the start, plus a 20 s tail for the request in flight, plus download bytes at US$0.20/GB) stays under 80% of the revenue charged so far, minus US$0.0005. When it would not, the run stops with `motivo: teto_margem` and `status: parcial`.
- **Search is local.** Greenhouse has no server-side search, so `searchText`, `locationContains` and `departmentContains` are applied after the board is downloaded.

### Polite crawling

- Public, unauthenticated GET requests only. One request at a time, 1 s apart.
- Check each board's terms of use before large runs. You are responsible for how you use the data.

### Data use and privacy

Job postings are company data. The only personal data that can appear is a recruiter's contact inside the free text. The Actor removes:

- every e-mail address (`[email removido]`), including company inboxes such as an accommodations address,
- Brazilian and US-format phone numbers (`[telefone removido]`),
- lines such as `Recruiter: Name`, `Recrutador(a): Nome` or `Talent Acquisition: Name`, in the text and in the HTML,

and adds the `contato_removido` warning. No candidate data is read or stored.

The filter is pattern based. Check the output before you use it for mailings or a CRM, and make sure your use meets the rules that apply to you (for example LGPD or GDPR).

### Known limits

- Phone detection covers Brazilian and US formats. Other formats may pass through.
- A board with no open jobs returns `sem_resultado` with zero items.
- The download allowance applies to the whole board response (see "Large boards with descriptions").
- Machine values (`status` ok/erro, the warning codes, the `RUN_SUMMARY` keys and the `motivo` values) are in Portuguese, as in the sister Actors. Field names and prose are in English.

### Run locally

```
node --test                    # unit tests, no network
node src/main.js               # local run; input in storage/key_value_stores/default/INPUT.json
node scripts/evidencia.mjs     # regenerates prova/run-local and prova/conferencia.md
```

***

## Greenhouse Jobs Scraper (português)

Vagas publicadas em **boards do Greenhouse** públicos, lidas pela API pública que os próprios boards usam. Um item por vaga, com a descrição em texto limpo e em HTML, sem contato pessoal de recrutador.

**Entrada:** tokens de board ou URLs de `boards.greenhouse.io` e `job-boards.greenhouse.io` (`boards`). Outros hosts, como link `?gh_jid=` de site da empresa, viram item `erro`, sem cobrança. **Saída:** um item por vaga (`company`, `title`, `jobId`, `url`, `location`, `departments`, `postedDate`, `descriptionText`, `descriptionHtml`, `status`, `warnings` etc.). **Erros de fonte não derrubam o run:** ele termina com sucesso e registra um item `erro`.

**Preços:** `inicio-run` US$0,005 por run com board válido; `job` US$0,001 por vaga com `status: ok` e descrição. Erros e vagas sem descrição disponível não são cobrados.

**Limites:** até 50 boards por run; `maxItems` padrão 50, teto de 1000 no código; 256 MB; prazo interno de 270 s; requisições uma a uma, com 1 s entre elas. Um board com descrição maior que a folga de download (cerca de 15,8 MB no início do run) para com `teto_margem` e não cobra nada. O Stripe, com descrição, tem 5,6 MB e cabe.

**Privacidade (LGPD):** a vaga é dado da empresa. E-mails, telefones (BR e EUA) e linhas do tipo `Recrutador(a): Nome` saem da descrição, com o aviso `contato_removido`. Nenhum dado de candidato é lido ou guardado. O uso dos dados é responsabilidade de quem usa.

### Related Actors

More Actors from the same publisher:

- [Lever Jobs Scraper API](https://apify.com/kman-br/lever-jobs): Job postings from public Lever job boards, with clean descriptions.
- [Ashby Jobs Scraper API](https://apify.com/kman-br/ashby-jobs): Job postings from public Ashby job boards, with department, employment type and pay summary.
- [Workday Jobs Scraper API](https://apify.com/kman-br/workday-jobs): Job postings from public Workday career sites (myworkdayjobs.com), with clean descriptions.
- [Brazil Jobs Scraper: Greenhouse & Lever (Vagas BR)](https://apify.com/kman-br/vagas-ats): Job openings at Brazilian companies from public Greenhouse and Lever job boards, with normalized city, state and work model.

Browse the full catalog: [kman-br.github.io](https://kman-br.github.io/) ([Português](https://kman-br.github.io/pt/)).

# Actor input Schema

## `boards` (type: `array`):

Greenhouse board tokens (for example stripe) or board URLs on boards.greenhouse.io or job-boards.greenhouse.io. Maximum 50 per run. Other hosts, such as a company site with a ?gh_jid= link, get an error item and no charge.

## `searchText` (type: `string`):

Case- and accent-insensitive text that must appear in the job title or, when Include description is on, in the description. Leave empty for all jobs.

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

Case- and accent-insensitive text that must appear in the job location.

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

Case- and accent-insensitive text that must appear in one of the job's department names.

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

Keeps jobs first published within this many UTC calendar days (0 keeps jobs published today). Uses the updated date when the first published date is missing. Jobs without any date are dropped when this is set.

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

Maximum number of job items returned per run. Items without an available description are returned with a warning and not charged. The hard cap is 1000, enforced in the code.

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

Adds each job's description as clean text and as HTML. Greenhouse returns descriptions only in the full board response, which is larger; a board above the run's download allowance (about 15 MB at the start charge) stops before any charge. Turn off for a smaller, list-only run.

## Actor input object example

```json
{
  "boards": [
    "stripe"
  ],
  "maxItems": 20,
  "includeDescription": true
}
```

# Actor output Schema

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

No description

## `summary` (type: `string`):

RUN_SUMMARY: status, motivo (maxItems, teto_cobranca, prazo_interno, teto_margem), boards checked and charged events.

# 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 = {
    "boards": [
        "stripe"
    ],
    "maxItems": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("kman-br/greenhouse-jobs").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 = {
    "boards": ["stripe"],
    "maxItems": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("kman-br/greenhouse-jobs").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 '{
  "boards": [
    "stripe"
  ],
  "maxItems": 20
}' |
apify call kman-br/greenhouse-jobs --silent --output-dataset

```

## MCP server setup

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

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/COiCTYsztUeGzXwTj/builds/dQ6R4WIaQy47Rijpf/openapi.json
