# Greenhouse, Lever & Ashby Jobs Scraper (`heaped_qualm/job-board-monitor`) Actor

Greenhouse, Lever & Ashby jobs scraper and monitor (plus Recruitee) using official public job-board APIs. Paste careers URLs, filter by keyword, location, remote and salary, and get only new, updated and closed jobs each run.

- **URL**: https://apify.com/heaped_qualm/job-board-monitor.md
- **Developed by:** [Artem Kulyk](https://apify.com/heaped_qualm) (community)
- **Categories:** Jobs, Lead generation, AI
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 16.7% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

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

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, Lever & Ashby Jobs Scraper

This **Greenhouse, Lever & Ashby jobs scraper** (Recruitee too) exports and monitors job postings from company careers pages. Monitor company job boards on **Greenhouse, Lever, Ashby and Recruitee** through their **official public job-board APIs**. Paste careers URLs (the ATS is detected automatically, also when the board is embedded in a company site), filter by keyword, location, remote, department, employment type, salary and posting date, and get **only NEW, UPDATED and CLOSED jobs since the last run**. Salaries are normalised to min, max, currency and interval.

**Why this actor:** job scrapers return the same thousand jobs every day. This one remembers each board and reports only what changed, including jobs that were closed, which most scrapers cannot see.

🇩🇪 [Deutsche Version](#deutsch)

### Contents

[Key features](#key-features) · [Use cases](#use-cases) · [Quick start](#quick-start) · [Input](#input) · [Output](#output) · [Pricing](#pricing) · [Integrations](#integrations) · [Limits](#limits) · [FAQ](#faq) · [Legal and data source](#legal-and-data-source) · [Changelog](#changelog) · [Related actors](#related-actors) · [Support](#support)

### Key features

- Official public ATS APIs only: no HTML scraping, no login, no proxies.
- **Four ATS in one actor:** Greenhouse, Lever (US and EU), Ashby and Recruitee. Paste careers URLs, `ats:slug` or a bare company slug.
- **Change detection:** `NEW`, `UPDATED` (title, location, salary or department changed) and `CLOSED` (job removed from the board).
- **Salary data:** structured ranges from Lever, Ashby and Recruitee; ranges parsed from Greenhouse job descriptions (company figures such as "$20M in volume" are ignored).
- **Filters:** title keywords (or description), exclusions, locations, remote only, departments, employment types, minimum salary, posted within N days.
- Cheap first run: **Max rows on first run** caps the baseline, but every open job is remembered, so later runs report all real changes.
- Polite: at most 2 requests per second per ATS host; retries with backoff; one failing company does not stop the others.

### Use cases

- **Sales prospecting:** hiring signals at target accounts (a new "Head of Data" often means budget).
- **Recruiters and agencies:** alerts when 50 target companies open new roles.
- **Job seekers:** remote engineering jobs above a salary floor, every morning.
- **Market and talent research:** hiring velocity, closed roles and salary ranges by company.
- **AI agents:** flat job rows with apply links.

### Quick start

1. Click **Try for free** / **Start**. The form is prefilled (Stripe, Palantir and Ramp, first 100 jobs), so the first run works without changes.
2. Replace the example with your own careers URLs or ATS slugs and adjust the filters.
3. Open the **Output** tab. Use the views (Jobs, Changes and closures, Salaries) or export JSON, CSV, Excel or HTML.
4. To monitor: switch on **Only changes since last run (monitor mode)**, give the watch a **Monitor name**, save it as a task and add a **schedule** (for example daily at 07:00). From the second run you receive only changes.

### Input

Add companies, one per line. Accepted forms: a careers URL (`https://boards.greenhouse.io/stripe`, `https://jobs.lever.co/palantir`, `https://jobs.ashbyhq.com/ramp`, `https://bunq.recruitee.com`), a company careers page that embeds one of these boards, a prefixed slug (`greenhouse:stripe`) or a bare slug (all four ATS are tried).

```json
{
  "companies": [
    "https://boards.greenhouse.io/stripe",
    "lever:palantir",
    "ashby:ramp",
    "recruitee:bunq"
  ],
  "keywords": [
    "engineer",
    "data"
  ],
  "excludeKeywords": [
    "intern"
  ],
  "locations": [
    "Berlin",
    "Amsterdam",
    "Remote"
  ],
  "minSalary": 80000,
  "onlyNewSinceLastRun": true,
  "stateKey": "eu-engineering",
  "maxResults": 100
}
```

Tips: the same board entered twice (for example as a URL and as a slug) is fetched once. **Minimum salary** drops jobs that publish no salary. A job that only stops matching your filters is not reported as closed.

### Output

Each row is one job. `changeType` tells you why it is in the dataset: `BASELINE` (first run), `NEW`, `UPDATED`, `CLOSED` (with `closedDetectedAt`) and `SEEN` (only when monitor mode is off).

Real rows from a run on 1 November 2026:

| Company | Job title | Location | Department | Salary | Posted | Change |
|---|---|---|---|---|---|---|
| Ramp | Security Engineer, Cloud | New York, NY (HQ) | Remote (Canada) | Remote (US) | Miami, FL | Engineering | $211.4K - $290.6K | 2026-04-07 | BASELINE |
| Ramp | Mobile Engineer, Android | New York, NY (HQ) | Remote (Canada) | San Francisco, CA | Remote (US) | Engineering | $189K - $330K | 2025-07-31 | BASELINE |
| Ramp | Software Engineer, Frontend | New York, NY (HQ) | Remote (Canada) | San Francisco, CA | Remote (US)… | Engineering | $143.2K - $284K | 2023-03-09 | BASELINE |

One full record (long values shortened):

```json
{
  "changeType": "BASELINE",
  "ats": "ashby",
  "companySlug": "ramp",
  "company": "Ramp",
  "jobId": "ashby:ramp:34413f8d-26bf-4bbc-8ade-eb309a0e2245",
  "id": "34413f8d-26bf-4bbc-8ade-eb309a0e2245",
  "title": "Security Engineer, Cloud",
  "location": "New York, NY (HQ) | Remote (Canada) | Remote (US) | Miami, FL",
  "remote": true,
  "workplaceType": "hybrid",
  "department": "Engineering",
  "team": "Backend",
  "employmentType": "FullTime",
  "salary": "$211.4K - $290.6K",
  "salaryMin": 211400,
  "salaryMax": 290600,
  "salaryCurrency": "USD",
  "salaryInterval": "year",
  "postedAt": "2026-04-07T17:12:35.753Z",
  "updatedAt": null,
  "applyUrl": "https://jobs.ashbyhq.com/ramp/34413f8d-26bf-4bbc-8ade-eb309a0e2245/application",
  "jobUrl": "https://jobs.ashbyhq.com/ramp/34413f8d-26bf-4bbc-8ade-eb309a0e2245",
  "description": null,
  "firstSeenAt": "2026-11-01T20:34:53.432Z",
  "closedDetectedAt": null,
  "scrapedAt": "2026-11-01T20:34:53.432Z"
}
```

<details><summary>All output fields</summary>

| Field | Description |
|---|---|
| `changeType` | BASELINE on the first run, then NEW, UPDATED (title, location, salary or department changed) or CLOSED; SEEN only when monitor mode is off. |
| `ats` | Applicant tracking system. |
| `companySlug` | Board slug on the ATS. |
| `company` | Company name. |
| `jobId` | Stable unique ID: ats:slug:id. |
| `id` | Job ID on the ATS. |
| `title` | Job title. |
| `location` | Location(s), separated by " | ". |
| `remote` | Remote job (flag or location text). |
| `workplaceType` | remote, hybrid or onsite, when the ATS publishes it. |
| `department` | Department. |
| `team` | Team. |
| `employmentType` | Employment type as published. |
| `salary` | Salary text as published. |
| `salaryMin` | Lower end of the salary range. |
| `salaryMax` | Upper end of the salary range. |
| `salaryCurrency` | Salary currency code. |
| `salaryInterval` | year, month, hour, etc. |
| `postedAt` | First published (UTC). |
| `updatedAt` | Last updated on the ATS (UTC). |
| `applyUrl` | Application link. |
| `jobUrl` | Job page. |
| `description` | Plain-text description (only with "Include description"). |
| `firstSeenAt` | When this monitor first saw the job (UTC). |
| `closedDetectedAt` | When the job was found closed (CLOSED rows). |
| `scrapedAt` | Run time (UTC). |

</details>

A run summary is saved as `RUN_SUMMARY` in the run's key-value store (counts per change type, boards found, jobs fetched and matched, boards not found, rows held back by the first-run cap).

### Pricing

Pay per result: **$0.75 per 1,000 jobs** plus **$0.003 per run**. Apify platform usage is included, so there is nothing else to pay. Check the **Pricing** tab for the current price.

| Scenario | Volume | Cost |
|---|---|---|
| Try it: prefilled Stripe, Palantir, Ramp (first 100 jobs) | 100 jobs, 1 run | **$0.078** |
| Daily monitor of 20 companies, about 15 changes a day | 450 jobs, 30 runs | **$0.43** |
| Full export of 3 large boards | 1,200 jobs, 1 run | **$0.90** |

Monitor mode keeps a schedule cheap: after the first run you pay only for new, updated and closed jobs. Apify's free plan includes $5 of monthly credit, which covers small monitors.

### Integrations

- **Schedules:** Console → Schedules → add this actor's saved task (daily or hourly). Keep the same **Monitor name** so each run compares with the previous one.
- **Webhooks:** on the Integrations tab add a webhook for *Run succeeded* to POST the run to your own endpoint, then fetch rows from `https://api.apify.com/v2/datasets/{datasetId}/items?format=json`.
- **Slack and email:** add the Slack or Email integration on the Integrations tab to get a message after each run.
- **Zapier, Make and n8n:** use the official Apify app ("Watch actor runs" trigger, then "Get dataset items"). Filter on `changeType` to forward only `NEW` or `CLOSED` jobs.
- **Google Sheets:** connect through Make or Zapier, or export the dataset as CSV and import it.
- **MCP and AI agents:** add `https://mcp.apify.com?tools=heaped_qualm/job-board-monitor` to Claude, Cursor or any MCP client. The flat rows are easy for agents to read.

**Python** (`pip install apify-client`):

```python
import os
from apify_client import ApifyClient

client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("heaped_qualm/job-board-monitor").call(run_input={"companies": ["https://boards.greenhouse.io/stripe", "lever:palantir", "ashby:ramp", "recruitee:bunq"], "keywords": ["engineer", "data"], "excludeKeywords": ["intern"], "locations": ["Berlin", "Amsterdam", "Remote"], "minSalary": 80000, "onlyNewSinceLastRun": true, "stateKey": "eu-engineering", "maxResults": 100})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["changeType"], item["company"], item["title"])
```

**JavaScript** (`npm install apify-client`):

```js
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('heaped_qualm/job-board-monitor').call({"companies": ["https://boards.greenhouse.io/stripe", "lever:palantir", "ashby:ramp", "recruitee:bunq"], "keywords": ["engineer", "data"], "excludeKeywords": ["intern"], "locations": ["Berlin", "Amsterdam", "Remote"], "minSalary": 80000, "onlyNewSinceLastRun": true, "stateKey": "eu-engineering", "maxResults": 100});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.table(items);
```

**HTTP** (waits for the run and returns the rows):

```bash
curl -X POST "https://api.apify.com/v2/acts/heaped_qualm~job-board-monitor/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"companies": ["https://boards.greenhouse.io/stripe", "lever:palantir", "ashby:ramp", "recruitee:bunq"], "keywords": ["engineer", "data"], "excludeKeywords": ["intern"], "locations": ["Berlin", "Amsterdam", "Remote"], "minSalary": 80000, "onlyNewSinceLastRun": true, "stateKey": "eu-engineering", "maxResults": 100}'
```

### Limits

- Supported: Greenhouse, Lever, Ashby and Recruitee. Workday, SmartRecruiters, Personio and others are not supported yet.
- Up to 10,000 matching jobs per company per run.
- Greenhouse publishes no structured salary; it is parsed from the description when a range like "$120,000 - $150,000" appears.
- `postedAt` is the date the ATS publishes (first published for Greenhouse, created for Lever, published for Ashby and Recruitee).

### FAQ

**Is it legal to scrape Greenhouse, Lever or Ashby jobs?**
The actor does not scrape web pages. It reads the public job-board APIs that these ATS provide so companies can show their jobs anywhere. It stays at 2 requests per second per host and collects no personal data.

**How do I find a company's slug?**
You do not need to. Paste the careers URL; the actor detects the ATS, also when the board is embedded in the company's own site.

**How is CLOSED detected?**
A job that the monitor saw before and that is no longer on the board is reported once as `CLOSED`.

**Why are there fewer rows than open jobs on the first run?**
**Max rows on first run** (default 100) limits the baseline output so a first try costs cents. All open jobs are still remembered. Set it to 0 for a full export.

**How do I get a full export instead of only changes?**
Switch off **Only changes since last run (monitor mode)**, or use a new **Monitor name**.

**Why are all rows BASELINE on the first run?**
The first run has nothing to compare with, so everything it finds is the baseline. Later runs with the same Monitor name report only changes.

**The run failed or returned nothing. What now?**
Read the status message and the `RUN_SUMMARY` record. A company listed under `notFound` has no public board on the four supported ATS; try its careers URL. If it still looks wrong, open an issue on the **Issues** tab with the run link.

### Legal and data source

Data sources: the public job-board APIs of [Greenhouse](https://developers.greenhouse.io/job-board.html), [Lever](https://github.com/lever/postings-api), [Ashby](https://developers.ashbyhq.com/docs/public-job-posting-api) and Recruitee, which companies use to publish their own open positions. Each row links to the original posting (`jobUrl`, `applyUrl`). Job texts belong to the publishing companies.

This actor is independent and not affiliated with or endorsed by Greenhouse, Lever, Ashby, Recruitee or any listed company. Data is provided as is, without warranty.

### Changelog

- 0.4 (Nov 2026): automated test suite (unit, integration, input and schema tests, 100% line coverage); fixes: company figures such as "$20M–$50M" are no longer read as salaries, Lever salary interval now reads "year" instead of "year-salary", the same board entered twice is fetched once, a truncated API response is retried; input form grouped into sections with examples; documented output schema with three views; monitor mode starts switched off in the form so test runs always show results.
- 0.3: **Max rows on first run** (default 100) caps the first baseline; all jobs are still remembered.
- 0.2: Recruitee, Lever EU, `UPDATED` change type, salary parsing, more filters, parallel companies.
- 0.1: first release with Greenhouse, Lever, Ashby, NEW and CLOSED.

### Related actors

More free monitors by the same author, built on official APIs:

- [ClinicalTrials.gov Scraper & Trial Monitor](https://apify.com/heaped_qualm/clinicaltrials-monitor): ClinicalTrials.gov scraper and trial monitor
- [SEC EDGAR Scraper: 8-K, 10-K, Form 4 Filings](https://apify.com/heaped_qualm/sec-edgar-monitor): SEC EDGAR scraper for 8-K, 10-K, 10-Q and Form 4 filings
- [PubMed Scraper & New Article Monitor](https://apify.com/heaped_qualm/pubmed-monitor): PubMed scraper and new article monitor
- [Hacker News Scraper & Keyword Monitor](https://apify.com/heaped_qualm/hackernews-monitor): Hacker News scraper and keyword monitor

### Support

Open an issue on the **Issues** tab of this page. Please include the run link and what you expected to see. Feature requests are welcome.

**More monitors from the same author:** [ClinicalTrials.gov Scraper & Trial Monitor](https://apify.com/heaped_qualm/clinicaltrials-monitor) · [SEC EDGAR Filings Monitor](https://apify.com/heaped_qualm/sec-edgar-monitor) · [PubMed Scraper & New Article Monitor](https://apify.com/heaped_qualm/pubmed-monitor)

***

<a name="deutsch"></a>

### 🇩🇪 Deutsch: Jobs-Monitor für Greenhouse, Lever, Ashby & Recruitee

Überwacht Karriereseiten auf **Greenhouse, Lever, Ashby und Recruitee** über deren **offizielle öffentliche APIs** und liefert **nur neue, geänderte und geschlossene Stellen** seit dem letzten Lauf. Gehälter normalisiert (Min, Max, Währung).

**Funktionen**

- Offizielle Job-Board-APIs, kein Login, keine Proxys
- Vier ATS, Karriere-URL genügt (ATS wird erkannt)
- Änderungserkennung: NEW, UPDATED, CLOSED
- Filter nach Stichwort, Ort, Remote, Abteilung, Gehalt, Datum
- Höflich: max. 2 Anfragen/s pro ATS

**Anwendungsfälle**

- Vertrieb: Einstellungssignale bei Zielkunden
- Recruiter: neue Rollen bei 50 Firmen
- Jobsuche: Remote-Jobs ab Gehaltsgrenze
- Marktforschung: Einstellungstempo und Gehälter

**Schnellstart**

1. **Start** klicken. Das Formular ist vorausgefüllt, der erste Lauf funktioniert ohne Änderungen.
2. Eigene Karriere-URLs oder ATS-Slugs eintragen und Filter anpassen.
3. Ergebnisse im Tab **Output** ansehen oder als JSON, CSV oder Excel exportieren.
4. Für die Überwachung **Only changes since last run (monitor mode)** einschalten, einen **Monitor-Namen** vergeben, als Task speichern und einen **Zeitplan** anlegen. Ab dem zweiten Lauf kommen nur noch Änderungen.

**Eingabebeispiel:** siehe JSON oben (vier Firmen, Engineering- und Data-Jobs in Berlin, Amsterdam oder remote).

**Ausgabe:** eine Zeile pro Stelle. `changeType` zeigt den Grund: BASELINE (erster Lauf), NEW, UPDATED, CLOSED, SEEN (nur ohne Monitor-Modus).

**Preis:** $0.75 pro 1.000 Stellen plus $0.003 pro Lauf, Plattformkosten inklusive. Beispiel: täglicher Monitor für 20 Firmen mit etwa 15 Änderungen pro Tag ≈ $0.43 im Monat. Den aktuellen Preis zeigt der Tab **Pricing**.

**Integrationen:** Zeitpläne, Webhooks, Slack, E-Mail, Zapier, Make, n8n, Google Sheets, API (Python, JavaScript, HTTP) und MCP für KI-Agenten (`https://mcp.apify.com?tools=heaped_qualm/job-board-monitor`).

**Grenzen:** nur Greenhouse, Lever, Ashby und Recruitee; bis 10.000 Stellen pro Firma und Lauf.

**Rechtliches und Datenquelle:** Quelle: öffentliche Job-Board-APIs von Greenhouse, Lever, Ashby und Recruitee; Link zur Originalanzeige je Zeile; Stellentexte gehören den Unternehmen. Kein Bezug zu und keine Unterstützung durch Greenhouse, Lever, Ashby, Recruitee or any listed company. Daten ohne Gewähr.

**Support:** Tab **Issues** auf dieser Seite, bitte mit Link zum Lauf.

# Actor input Schema

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

One per line: a careers URL (https://boards.greenhouse.io/stripe, https://jobs.lever.co/palantir, https://jobs.ashbyhq.com/ramp, https://bunq.recruitee.com, or a company careers page that embeds one), a prefixed slug (greenhouse:stripe, lever:palantir, ashby:ramp, recruitee:bunq) or a bare slug (all ATS tried).

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

Keep jobs whose title contains ANY of these (case-insensitive), e.g. engineer, data. Empty = all.

## `keywordsInDescription` (type: `boolean`):

Search keywords in the job description too, not only the title.

## `excludeKeywords` (type: `array`):

Drop jobs whose title contains any of these words, e.g. Senior, Intern. Case-insensitive.

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

Keep jobs whose location contains ANY of these, e.g. Berlin, Germany, London, Remote. Empty = all.

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

Keep only jobs flagged or described as remote.

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

Keep jobs whose department/team contains ANY of these, e.g. Engineering, Sales.

## `employmentTypes` (type: `array`):

Keep jobs whose employment type matches ANY of these, e.g. Full-time, Part-time, Contract, Intern. Matched against the value the ATS publishes.

## `minSalary` (type: `integer`):

Keep only jobs whose published salary range reaches at least this amount, in the currency of the job. Jobs that publish no salary are dropped when this is set.

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

Keep only jobs first published in the last N days. Leave empty or 0 for any date. Lever and Ashby publish a posting date; Greenhouse uses its first-published date.

## `onlyNewSinceLastRun` (type: `boolean`):

Emit only NEW and UPDATED jobs plus CLOSED records for jobs that disappeared. First run emits everything as BASELINE. It starts switched off in the form, so a manual test run always shows results. Switch it on in the task you schedule. API calls that omit this field get monitor mode on.

## `reportClosed` (type: `boolean`):

In monitor mode, emit a CLOSED record when a previously seen job is unpublished.

## `maxResults` (type: `integer`):

Caps how many BASELINE jobs the first run emits (and you pay for), across all companies. All open jobs are still remembered, so later runs report only real NEW/UPDATED/CLOSED changes. 0 = no cap (full export). Change rows are never capped.

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

Cap on matching jobs emitted per company in one run. Higher values are allowed up to 10,000.

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

Runs with the same monitor name share memory of seen jobs. Use a different name for each separate watch list.

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

Adds the full job description as plain text (larger output).

## `maxConcurrency` (type: `integer`):

How many companies are fetched in parallel. Each ATS host is still limited to 2 requests per second.

## Actor input object example

```json
{
  "companies": [
    "https://boards.greenhouse.io/stripe",
    "lever:palantir",
    "ashby:ramp",
    "https://bunq.recruitee.com"
  ],
  "keywords": [
    "engineer",
    "data"
  ],
  "keywordsInDescription": false,
  "excludeKeywords": [
    "senior",
    "intern"
  ],
  "locations": [
    "Berlin",
    "Germany",
    "Remote"
  ],
  "remoteOnly": false,
  "departments": [
    "Engineering"
  ],
  "employmentTypes": [
    "Full-time"
  ],
  "minSalary": 100000,
  "postedWithinDays": 7,
  "onlyNewSinceLastRun": true,
  "reportClosed": true,
  "maxResults": 100,
  "maxJobsPerCompany": 500,
  "stateKey": "berlin-engineering",
  "includeDescription": false,
  "maxConcurrency": 4
}
```

# Actor output Schema

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

All new/changed job postings found in this run (default dataset, all fields).

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

Default dataset, 'overview' view.

## `changes` (type: `string`):

Default dataset, 'changes' view.

## `salaries` (type: `string`):

Default dataset, 'salaries' view.

## `runSummary` (type: `string`):

JSON summary of this run (counts, first-run flag, finishedAt) in the default key-value store record RUN_SUMMARY. Monitoring state persists separately in the named key-value store job-board-monitor-state under STATE-<stateKey>.

# 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": [
        "greenhouse:stripe",
        "lever:palantir",
        "ashby:ramp"
    ],
    "onlyNewSinceLastRun": false,
    "maxResults": 100,
    "maxJobsPerCompany": 20,
    "stateKey": "default",
    "maxConcurrency": 4
};

// Run the Actor and wait for it to finish
const run = await client.actor("heaped_qualm/job-board-monitor").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": [
        "greenhouse:stripe",
        "lever:palantir",
        "ashby:ramp",
    ],
    "onlyNewSinceLastRun": False,
    "maxResults": 100,
    "maxJobsPerCompany": 20,
    "stateKey": "default",
    "maxConcurrency": 4,
}

# Run the Actor and wait for it to finish
run = client.actor("heaped_qualm/job-board-monitor").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": [
    "greenhouse:stripe",
    "lever:palantir",
    "ashby:ramp"
  ],
  "onlyNewSinceLastRun": false,
  "maxResults": 100,
  "maxJobsPerCompany": 20,
  "stateKey": "default",
  "maxConcurrency": 4
}' |
apify call heaped_qualm/job-board-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,heaped_qualm/job-board-monitor"
        }
    }
}
```

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/2k4iA8wMjTTihdCzh/builds/2mODlKXbFXhyF2P7V/openapi.json
