# Greenhouse & Lever Job Postings API: New & Closed (`amyorkltd/ats-job-postings-api`) Actor

Live job postings from the official Greenhouse and Lever job-board APIs. Filter by title, location, country, remote, seniority and posted date. Delta mode flags new and closed jobs; closed rows are free. Every company gets a status row, so you never get a silent zero. From $1.20 per 1,000 jobs.

- **URL**: https://apify.com/amyorkltd/ats-job-postings-api.md
- **Developed by:** [Marcus](https://apify.com/amyorkltd) (community)
- **Categories:** Jobs, Developer tools, Lead generation
- **Security:** Limited permissions
- **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 rows

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 & Lever Job Postings API: New & Closed

[![Actor status](https://apify.com/actor-badge?actor=amyorkltd/ats-job-postings-api)](https://apify.com/amyorkltd/ats-job-postings-api)

### What is Greenhouse & Lever Job Postings API: New & Closed?

**Greenhouse & Lever Job Postings API** reads live job postings from the companies you name that hire with Greenhouse or Lever, through the two official public job-board APIs, and returns each published job that passes your filters, up to `maxItems`. Use it as a **Greenhouse job scraper** or a **Lever job board API**: give it board slugs or board links and get clean job data as JSON, CSV or Excel. The data is public job posts read from each employer's own Greenhouse or Lever job board. Filter by title, location, country, remote and seniority, and switch on deltaMode to get jobs not delivered to you yet plus free closed rows for delivered jobs that left the board. Every company gets a status row, so a typo never looks like zero jobs. No logins, no proxies, no personal data. This Actor is not endorsed by Greenhouse or Lever.

- **Live, not a stale index.** Each run reads each company's Greenhouse or Lever board at run time, so every row reflects the board as it stood during that run.
- **New and closed jobs.** Turn on `deltaMode` and each run returns jobs this input has not delivered yet, up to `maxItems`, plus free closed rows for delivered jobs that have left their board.
- **Honest status rows.** Every company you ask for gets a row in the `hiring_signals` dataset: `ok`, `empty`, `not_found`, `unsupported_ats`, `error`, `duplicate`, `invalid_input` or `skipped`, with open-role counts by function, country and department.

### What data do you get?

One row per open job, built from a fixed list of fields. No raw Greenhouse or Lever object is copied through (Greenhouse metadata, requisition IDs, internal IDs, data-compliance and education fields are dropped), and no person's name, email or phone number is output.

| Field | What it holds |
|---|---|
| `jobId` | Stable ID `ats:companySlug:postingId`, for deduping across runs |
| `ats`, `source` | `greenhouse` or `lever`, and the source label `Greenhouse job board` or `Lever job board` |
| `companySlug`, `companyName` | The board slug and the company name |
| `title`, `department`, `team`, `function` | Job title, the board's own department and team, and a function class (Engineering, Data & AI, Sales and so on) |
| `locationText`, `locations`, `country` | Location text from the board, all listed locations, and one ISO country code: Lever's country field first, else the first location that names a country, a US state, a Canadian province or a major city the Actor knows (such as London or Bengaluru). Null when none does |
| `remote`, `workplaceType` | true, false, or null when the board does not say, plus remote, hybrid or on-site when the board says |
| `seniority`, `seniorityInferred` | intern, entry, mid, senior, lead or executive, read from the title |
| `employmentType` | Full-time, Part-time, Contract or Intern when the board gives it |
| `compensation` | Pay range (min, max, currency, interval) from Lever's salary field or read from the job text. Greenhouse sends job text only when `includeDescription` is on, so Greenhouse rows have no pay unless it is on and the text states pay |
| `postedAt`, `postedAtSource`, `daysSincePosted`, `updatedAt` | The board's own dates: Lever createdAt; Greenhouse first_published, or updated_at when a job has no first_published (`postedAtSource` names which) |
| `jobUrl`, `applyUrl`, `boardUrl` | Links back to the job page on the employer's own board (on every row), the apply page and the board |
| `jobUrlSource` | `original` when `jobUrl` is the link the board gave for the job; `reconstructed` when it was rebuilt from the board and job id (only closed Greenhouse rows saved before 30 Oct 2026) |
| `description` | Full job text, only when `includeDescription` is on, with contact details removed |
| `changeType`, `firstSeenAt`, `lastSeenAt`, `closedDetectedAt` | Only in delta mode: `changeType` is `new` or `closed`; `lastSeenAt` and `closedDetectedAt` appear on closed rows only |
| `fetchedAt` | When the row was read |

Example row (real output, 29 Oct 2026):

```json
{
    "jobId": "greenhouse:stripe:8172487",
    "ats": "greenhouse",
    "source": "Greenhouse job board",
    "companySlug": "stripe",
    "companyName": "Stripe",
    "title": "Abuse Investigator",
    "department": null,
    "team": null,
    "function": "Security",
    "locationText": "Dublin",
    "locations": [
        "Dublin"
    ],
    "country": "IE",
    "remote": false,
    "workplaceType": "onsite",
    "seniority": "mid",
    "seniorityInferred": true,
    "employmentType": null,
    "compensation": null,
    "postedAt": "2026-09-03T17:30:34.000Z",
    "postedAtSource": "first_published",
    "updatedAt": "2026-10-29T14:42:09.000Z",
    "daysSincePosted": 56,
    "applyUrl": "https://stripe.com/jobs/search?gh_jid=8172487",
    "jobUrl": "https://stripe.com/jobs/search?gh_jid=8172487",
    "jobUrlSource": "original",
    "boardUrl": "https://job-boards.greenhouse.io/stripe",
    "atsJobId": "8172487",
    "companyInput": "greenhouse:stripe",
    "fetchedAt": "2026-10-29T22:04:14.020Z"
}
```

### How to scrape Greenhouse and Lever jobs

**Easiest way to try it:** press **Start** with the prefilled companies (`greenhouse:stripe`, `lever:spotify` and `lever:palantir`). You get up to 100 job rows plus one free status row per company.

1. Add board slugs (`spotify`, `greenhouse:stripe`) or board links (`https://job-boards.greenhouse.io/stripe`, `https://jobs.lever.co/spotify`) to **Companies**, or pick a **Preset company list** (Lever boards) and clear the Companies box.
2. Set any filters you want, and `maxItems` to cap how many job rows you pay for.
3. Click **Start**, then download the dataset as JSON, CSV, Excel or through the Apify API.

**Only the boards you name are fetched.** A bare slug is checked once on Greenhouse, and on Lever exactly as typed (US, then EU): an unknown slug takes up to 3 requests (1 Greenhouse, 2 Lever). Slugs are never guessed on either platform: type them as they appear in the board link. A company name or domain on its own is not looked up and gets a `not_found` status row ("board not found, check the slug"). Company websites are never fetched: only the Greenhouse and Lever job board APIs are asked.

**From an AI agent:** the Actor works through Apify MCP. Every input has a plain description written so an agent can fill it in without guessing.

### Job filters

All filters are input fields in this Actor's schema:

- `titleKeywords` and `excludeTitleKeywords`: keep or drop jobs by words in the title. Turn on `keywordsInDescription` to also match the description (for Greenhouse boards this needs `includeDescription`, because Greenhouse sends job text only then).
- `locations`: free-text location match, for example London or EMEA.
- `countries`: ISO codes such as `US` or `GB`. A job listed in several places is kept if any of its locations matches.
- `remote`: include, only or exclude remote jobs. Hybrid counts as not remote.
- `seniority`: intern, entry, mid, senior, lead, executive.
- `postedAfter`: a date (`2026-10-01`) or a window (`24h`, `7d`, `2w`).
- `functions` and `employmentTypes`: keep only some job functions or contract types.
- `maxJobsPerCompany`: cap the rows from any one company.

**Filter proof.** Every filter has a known true and a known false case in the unit tests (test 48, "filters: every filter has a known true and a known false case"), and each run's `RUN_SUMMARY` lists `filterCounts`: for each filter you set, how many jobs it checked, kept and dropped.

### Presets

Three ready lists of Lever companies, each slug checked live on 29 Oct 2026. There are no Greenhouse presets: name Greenhouse boards yourself.

- `ai-data`: 8 AI, data and autonomy companies
- `fintech`: 12 fintech companies
- `software`: 12 software and SaaS companies

### How much does it cost to get job postings?

You pay per job row returned. Status rows and closed-job rows are free.

| Apify plan | Price per 1,000 job rows |
|---|---|
| Free | $1.50 |
| Bronze | $1.50 |
| Silver | $1.35 |
| Gold and above | $1.20 |

Example: 10,000 job rows cost $15.00 on the Free plan, $15.00 on Bronze, $13.50 on Silver and $12.00 on Gold and above. Apify's standard Actor start event also applies. `maxItems` and your run's maximum cost both stop the run before you go over budget, and `includeDescription` does not change the price.

#### Is it free to try?

Yes. Apify's Free plan includes $5 of monthly platform usage, which covers about 3,300 job rows at $1.50 per 1,000. Status rows and closed-job rows are always free.

### New and closed jobs (delta mode)

Turn on `deltaMode` and the Actor keeps a baseline for each input (or each `stateKey`) and records which jobs it has delivered:

- Matching jobs not delivered yet come back with `changeType: "new"` and are charged as normal job rows, up to `maxItems` a run. Jobs over the cap stay waiting and come back on later runs; if more than `maxItems` jobs open between runs, the extra wait for later runs.
- A delivered job that has left its board comes back once with `changeType: "closed"` and `closedDetectedAt`. These rows are free and do not count towards `maxItems`. A job that closes before it was ever delivered gets no closed row. Lever closed rows are checked against your current filters, apart from the date filter, so a job matched only by its description text gets no closed row.
- The first run has no baseline: it saves one and delivers matching open jobs, marked new, capped at `maxItems`.
- Set `stateKey` (for example `my-watchlist`) to keep the same baseline when you change filters.

Safety rules: if a board fails to load, its baseline is kept, so a network error never looks like mass closures. If any board that had jobs suddenly shows 0, the Actor waits for a second run in a row to confirm before reporting closures.

### Input and output examples

Input:

```json
{
    "companies": ["spotify", "https://jobs.lever.co/palantir", "greenhouse:stripe", "https://job-boards.greenhouse.io/airbnb"],
    "titleKeywords": ["engineer", "data"],
    "countries": ["US", "GB"],
    "postedAfter": "30d",
    "deltaMode": true,
    "stateKey": "my-watchlist",
    "maxItems": 500
}
```

Output: job rows in the default dataset, and one status and hiring-signal row per company in the `hiring_signals` dataset. A run summary is saved in the key-value store as `RUN_SUMMARY`. The run never stops silently: when `maxItems`, your run's maximum cost or a stopped host ends it early, `RUN_SUMMARY` says so in `stopReason` (`maxItems`, `chargeLimit` or `hostStopped`) and `stopMessage`, with `rowsSkipped` (matching jobs not delivered) and `companiesNotScanned`, and the same message is written to the run log.

### Is it legal?

The data is public job posts read from each employer's own Greenhouse job board or Lever job board, through the two official public APIs. This Actor is not endorsed by Greenhouse or Lever, and Greenhouse does not endorse this Actor.

- **Greenhouse:** only GET requests to the public [Greenhouse Job Board API](https://developers.greenhouse.io/job-board.html) (`boards-api.greenhouse.io/v1/boards/{slug}/jobs`), one request per board per run, with job text only when you turn on `includeDescription`. It never calls embed pages, application forms or question sets, and never logs in.
- **Lever:** only Lever's official public [postings API](https://github.com/lever/postings-api) (api.lever.co, and api.eu.lever.co for EU boards). Lever's own documentation says published postings "are publicly viewable" and "may be scraped by third parties".
- **Polite by design:** at most 1 request per second to each host, an honest User-Agent naming this Actor and its Store page, no proxies and no IP or header cycling. On a 429 or 5xx the host is paused with exponential back-off; after 3 failures in a row, or any 403 from Greenhouse, the Actor stops calling that host for the rest of the run.
- **Only the boards you name:** no crawl of all boards, no slug discovery, and no stored job text. Delta mode keeps one record per job to spot changes: ID, title, posted date, job link and first-seen time on Greenhouse, plus department, team, location, country, function, seniority and type fields on Lever.
- **No personal data:** output is built from job fields only, and emails, phone numbers and named recruiter contact lines are removed from descriptions.
- **Removal:** any company, Greenhouse or Lever can ask for removal through the Issues tab on this Actor's Apify Store page, and the slug is blocked within 2 working days.
- **No AI training:** the output may not be resold or used as an AI-training dataset, and we sell none ourselves. We publish no bulk dump. Lever's job site also signals "ai-train=no".

This is not legal advice.

### How it compares

| | This Actor | A database-style job API |
|---|---|---|
| Freshness | Read live from Greenhouse or Lever at run time | As fresh as the last crawl |
| Closed jobs | Free closed rows in delta mode for jobs you were sent | Depends on the provider's crawl schedule |
| Missing companies | Status row says why | Depends on the provider |
| Coverage | Any company you name with a public Greenhouse or Lever board | Whatever the database has indexed |

### Integrations

Run it from the Apify API, a schedule, Make, n8n, Zapier or any Apify integration, or from an AI agent through Apify MCP. A daily schedule with `deltaMode` on returns, each day, jobs not delivered yet (up to `maxItems`) and free closed rows for delivered jobs that left their board.

### FAQ

#### Is it legal?

It reads only the official public job-board APIs: Greenhouse's Job Board API (GET requests for published jobs, no login) and Lever's postings API, which Lever documents as open to third parties. It sends at most 1 request per second to each host and one Greenhouse request per board per run, and stops a host after 3 failures in a row or any block. It outputs no names, emails or phone numbers; recruiter contact lines are stripped from descriptions. Companies can ask to be removed. Not legal advice.

#### Does it cover Ashby, Workday or other ATS?

No. It reads Greenhouse and Lever only. Ashby boards return a status row "unsupported_ats", and links to other careers sites such as Workday return "not_found". Nothing is fetched for them and nothing is charged.

#### Is it made or endorsed by Greenhouse or Lever?

No. It is an independent Actor that reads their public job-board APIs. Greenhouse does not endorse this Actor.

#### How do I find a company's board slug?

It is the word after job-boards.greenhouse.io/ or jobs.lever.co/ in the careers link. Type it exactly; the Actor does not guess slugs on Greenhouse or Lever, and a company name or domain on its own returns "not_found" (board not found, check the slug).

#### Why are some Greenhouse rows missing a description or salary?

Greenhouse only sends job text when includeDescription is on, so description keywords and salary read from the text need includeDescription for Greenhouse boards.

#### What does it cost?

See the price table. Status rows and closed-job rows are free. Apify's Free plan includes $5 of monthly platform usage, which covers about 3,300 job rows at $1.50 per 1,000.

#### Can I use it from an AI agent?

Yes, through the Apify MCP server or the Apify API. Every input has a plain description written so an agent can fill it in without guessing, and every company gets a status row the agent can read.

#### Can I use the data to train AI models?

No. The output may not be resold or used as an AI-training dataset, and we publish no bulk dump. Lever's job site also signals "ai-train=no" (https://jobs.lever.co/robots.txt, 29 Oct).

#### How do I ask for a company to be removed?

Open an issue on this Actor's Issues tab on the Apify Store page with the company and board link. The slug is blocked within 2 working days.

### Support

Found a bug or a board that does not load? Open an issue on the Issues tab and include the company slug. We aim to reply to issues within 2 working days. If this Actor saves you time, please leave a review.

# Actor input Schema

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

Greenhouse or Lever companies to fetch. Give the board slug (spotify), a prefixed slug (greenhouse:stripe or lever:palantir) or a board URL (https://job-boards.greenhouse.io/stripe, https://jobs.lever.co/spotify or https://jobs.eu.lever.co/{slug}). A bare slug is checked once on Greenhouse and on Lever exactly as typed (US, then EU), so an unknown slug takes up to 3 requests (1 Greenhouse, 2 Lever). Slugs are never guessed: company names and domains are not looked up and return a "not_found" status row. Ashby boards return a status row "unsupported_ats", and links to other careers sites (Workday and so on) return "not_found", never a silent zero.

## `preset` (type: `string`):

Instead of a company list, pick a ready list of Lever companies: "ai-data" (8 AI, data and autonomy companies), "fintech" (12 fintechs) or "software" (12 software and SaaS companies). Every slug was checked live. Ignored if companies is set. Presets hold Lever boards only; name Greenhouse boards yourself in companies.

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

Keep jobs whose title contains any of these words, case-insensitive. Example: \["engineer", "data"]. Whole words or phrases; leave empty for all jobs.

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

Drop jobs whose title contains any of these words. Example: \["senior", "manager"].

## `countries` (type: `array`):

Keep jobs located in these countries, for example \["US", "GB"]. Use ISO 3166-1 alpha-2 codes (UK works too) or English country names; some 3-letter codes such as USA also work, and an entry the Actor cannot match is skipped with a warning. A job matches when Lever's country field or any of its listed locations points to one of these countries, by country name or code, US state, Canadian province or a major city the Actor knows. A job with no country found is dropped, except a remote job, which is kept unless remote is "exclude".

## `remote` (type: `string`):

Whether to keep, require or drop remote jobs. include keeps everything, only keeps remote jobs, exclude drops them. Hybrid counts as not remote.

## `seniority` (type: `array`):

Keep these levels: intern, entry, mid, senior, lead, executive. Inferred from the title; each row shows seniorityInferred: true. Titles with no level word count as mid.

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

Keep jobs posted on or after this time, using the board's own date: Lever createdAt; Greenhouse first_published, or updated_at when that is missing. A date (YYYY-MM-DD) or a relative window such as "24h", "7d" or "2w".

## `deltaMode` (type: `boolean`):

Off: return every matching open job, up to maxItems. On: return matching jobs this input has not delivered yet (changeType "new"), up to maxItems a run, plus free closed rows (changeType "closed") for delivered jobs that have left their board. Jobs past the maxItems cap stay queued and are delivered as new in a later run; a job that closes before delivery gets no closed row. With no baseline yet, the first run records one and delivers open jobs as new, capped at maxItems. State lives in the key-value store "ats-job-postings-state", keyed by a hash of your companies and filters (or by stateKey), so different watch lists never mix. A company whose fetch fails keeps its old baseline, so a failed fetch never shows up as mass closures.

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

Optional name for the delta baseline, for example my-watchlist. By default the baseline is keyed by a hash of your companies and filters. Set a name to keep one baseline while you edit the list, or to run two separate baselines for the same input. Lowercase letters, digits and dashes.

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

Add the full job description text (bigger output, same price). Emails, phone numbers, profile links and named recruiter contacts are removed. Greenhouse sends job text only when this is on, so for Greenhouse boards keywordsInDescription and salary read from the text need it.

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

Maximum job rows to return and charge for. Company status rows and closed-job rows are free and do not count. When the limit is reached, remaining companies get a "skipped" status row. In deltaMode, matching jobs past the limit stay queued and are delivered as new in a later run.

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

When on, title keywords also match text in the job description (wider net, for example a skill like Kubernetes). For Greenhouse boards this needs includeDescription on.

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

Free-text location match, for example London, New York, EMEA. Keeps jobs whose location text contains any of these strings (case-insensitive). Use countries for country filtering.

## `functions` (type: `array`):

Keep only these job functions (classified from title, team and department). Values: Engineering, Data & AI, Product, Design, Sales, Marketing, Customer Success & Support, Operations, Finance, Legal & Compliance, People & Recruiting, Security, IT, Research & Science, Other.

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

Keep only these employment types, for example Full-time, Part-time, Contract, Intern. Jobs where the ATS gives no type are dropped when this is set.

## `includeJobsWithoutDate` (type: `boolean`):

Some boards omit dates on a few jobs. Off by default, so date filters stay strict.

## `maxDescriptionChars` (type: `integer`):

Descriptions longer than this are cut.

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

Cap the job rows output per company after filters. 0 means no cap. Greenhouse boards are read whole, so their status rows and hiring-signal counts cover the whole board. In deltaMode Lever boards are read whole too, and matching jobs past the cap stay queued for a later run. With deltaMode off, a Lever board is read in pages of 500 until the jobs that pass your filters reach the cap, so its status and hiring-signal counts cover the pages read.

## `outputSignalsOnly` (type: `boolean`):

Skip job rows and output only the per-company status and hiring-signal rows (free).

## `newRoleWindowDays` (type: `integer`):

A role counts as new in the hiring signals if it was posted within this many days.

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

How many companies are scanned at the same time. Requests to each API host still start at most once per second.

## `perHostDelayMs` (type: `integer`):

Minimum gap between request starts to the same API host. The floor is 1000 ms (at most 1 request per second per host); lower values are raised to 1000.

## `requestTimeoutSecs` (type: `integer`):

Timeout per HTTP request. Greenhouse board requests and full Lever board fetches get at least 60 seconds, and Lever probes at least 45 seconds, automatically.

## `maxRetries` (type: `integer`):

Retries for timeouts, 429 and 5xx responses, with exponential backoff that pauses the whole host (at most 2, so 3 attempts). After 3 failures in a row, or any 403 from Greenhouse, the Actor stops calling that host for the rest of the run. 404s are not retried.

## Actor input object example

```json
{
  "companies": [
    "lever:spotify",
    "greenhouse:stripe",
    "lever:zoox",
    "https://jobs.lever.co/palantir"
  ],
  "preset": "none",
  "remote": "include",
  "postedAfter": "7d",
  "deltaMode": false,
  "includeDescription": false,
  "maxItems": 100,
  "keywordsInDescription": false,
  "includeJobsWithoutDate": false,
  "maxDescriptionChars": 20000,
  "maxJobsPerCompany": 0,
  "outputSignalsOnly": false,
  "newRoleWindowDays": 7,
  "maxConcurrency": 5,
  "perHostDelayMs": 1000,
  "requestTimeoutSecs": 45,
  "maxRetries": 2
}
```

# Actor output Schema

## `jobs` (type: `string`):

Normalised job rows after your filters, up to maxItems. In deltaMode: new jobs plus free closed-job rows.

## `hiringSignals` (type: `string`):

Status (ok, empty, not_found, error, duplicate, skipped), HTTP status, jobs found and after filters, per-filter exclusions, roles by function and opened or closed counts in deltaMode.

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

Totals per status and ATS, jobs pushed, stop reason, delta totals, warnings and a status line per company.

# 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:spotify",
        "lever:palantir"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("amyorkltd/ats-job-postings-api").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:spotify",
        "lever:palantir",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("amyorkltd/ats-job-postings-api").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:spotify",
    "lever:palantir"
  ]
}' |
apify call amyorkltd/ats-job-postings-api --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,amyorkltd/ats-job-postings-api"
        }
    }
}
```

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/o0ICTfBABsgjoakE3/builds/UfAknyxrwZwdQMNs6/openapi.json
