# Company Job Postings from Greenhouse, Lever and Ashby (`aesthetic_gewgaw/ats-job-postings`) Actor

Open jobs straight from company career boards on Greenhouse, Lever and Ashby in one schema. Filter by keyword, location, department and remote, and get only postings new since your last run.

- **URL**: https://apify.com/aesthetic_gewgaw/ats-job-postings.md
- **Developed by:** [Huskarian Geist](https://apify.com/aesthetic_gewgaw) (community)
- **Categories:** Jobs
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$1.00 / 1,000 job postings

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

## Company Job Postings from Greenhouse, Lever and Ashby

Get the open jobs of any company that hosts its careers page on Greenhouse, Lever or Ashby, in one clean schema. Give it company names or careers page links, filter by keyword, location, department, remote and posting age, and switch on monitoring mode to receive only the postings that are new since your last run. It reads the public job board APIs these platforms publish for company career sites, so there is no scraping, no login and no proxy bill. Use it for job market research, recruiting and sourcing pipelines, job aggregators, and alerts for the companies you follow.

### What you get

One record per open posting, the same fields whichever platform it came from.

| field | example |
|---|---|
| platform, company | greenhouse, stripe |
| companyName | Stripe (Greenhouse states it, the others leave it empty) |
| jobId, title | 8172510, Backend Engineer, Payments |
| department, team | Engineering, Payments |
| location, locations | London, and the full list of offices |
| country | GB (Lever and Ashby only) |
| remote, workplaceType | true, hybrid |
| employmentType | Permanent |
| salary | GBP 90,000 - 120,000 (only when the employer publishes a range) |
| postedAt, updatedAt | first published time, last update time (Greenhouse only) |
| url, applyUrl | posting page and application page |
| description | plain text, cut at your chosen length |
| isNew | true if you have not received this posting before |

Postings name the employer and the role only. Recruiter and hiring manager names and contact details are not collected.

### Example input

```json
{
  "companies": ["stripe", "lever:spotify", "https://jobs.ashbyhq.com/ashby"],
  "keywords": ["engineer"],
  "locations": ["london", "remote"],
  "postedWithinDays": 30,
  "maxItems": 100
}
```

Companies can be written three ways. A plain slug such as `stripe` is tried on all three platforms. A prefixed slug such as `lever:spotify` goes straight to one platform. A careers page link such as `https://boards.greenhouse.io/stripe` also works. The slug is the last part of the company's careers page address.

### Monitoring mode: only new postings

Turn on `onlyNew` and give the search a `stateKey`, for example `london-engineers`. The first run returns everything that matches. Every later run returns only postings that were not returned before under that name, and you pay only for those. Schedule the Actor in Apify to run daily and connect the dataset to a webhook, Slack, Make or Zapier for job alerts. The remembered list is kept in a named key-value store in your account.

### Pricing

Pay per result: $1 per 1,000 postings. No subscription. Set `maxItems` to cap your spend, and turn off `includeDescription` for a smaller dataset. A run that checks 20 companies for new postings usually costs a fraction of a cent.

### Things to know

- **Only companies that use these three platforms.** A company on Workday, SmartRecruiters or its own site will show as not found. Check the careers page address if you are not sure.
- **Boards are read in full, then filtered.** A company with a thousand openings is fetched as one request set, so filters save you money but not time.
- **Salary is only filled when the employer publishes it.** Many postings have no range.
- **Greenhouse does not say whether a role is remote.** The Actor marks it remote only if the word remote appears in the location. Lever and Ashby give a real flag.
- **Dates:** Greenhouse and Ashby give the first published time, Lever gives the creation time.
- Postings are shown as the employer publishes them. The Actor cannot confirm that a role is still being filled.

### FAQ

**How fresh is the data?** It is read live from each company's board at the time of the run.

**Is this allowed?** The Greenhouse, Lever and Ashby job board APIs are public interfaces that exist so companies can show their openings elsewhere. The Actor sends a handful of polite requests per company and needs no login.

**Can I get candidates or applicants?** No. This only returns public job postings.

**Something is missing or wrong.** Open an issue on the Actor page with the company and posting link and it will be looked at.

# Actor input Schema

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

Company job boards to read. Use a plain slug such as stripe (all three platforms are tried), a prefixed slug such as lever:spotify or ashby:notion, or paste the careers page URL, for example https://boards.greenhouse.io/stripe or https://jobs.ashbyhq.com/ashby.

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

Keep postings whose title, department or team contains at least one of these words. Not case sensitive. Leave empty to keep all.

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

Drop postings whose title or department contains any of these words, for example intern or sales.

## `searchDescription` (type: `boolean`):

Match the keywords against the full job description as well as the title, department and team.

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

Keep postings with at least one location, or the country, containing one of these words, for example London, Germany or Remote.

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

Keep postings whose department or team contains one of these words, for example Engineering or Design.

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

Keep only postings the employer marks as remote. Greenhouse does not say, so there it is guessed from the word remote in the location.

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

Keep postings first published within this many days. Use 0 for no limit.

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

Monitoring mode. The Actor remembers which postings it has returned (in a named key-value store) and returns only new ones next time. The first run returns everything that matches. You pay only for the postings returned.

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

Name for the remembered list when Only new is on. Use a different name for each search you monitor, for example london-engineers.

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

Add the posting text as plain text. Turn off for a smaller, faster dataset.

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

Cut each description at this many characters. Use 0 to leave descriptions out.

## `maxItemsPerCompany` (type: `integer`):

Stop at this many matching postings per company board. Use 0 for no limit.

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

Hard limit on postings returned (and charged) in one run, newest first.

## Actor input object example

```json
{
  "companies": [
    "stripe",
    "lever:spotify",
    "ashby:ashby"
  ],
  "keywords": [
    "engineer"
  ],
  "searchDescription": false,
  "remoteOnly": false,
  "postedWithinDays": 0,
  "onlyNew": false,
  "stateKey": "default",
  "includeDescription": true,
  "maxDescriptionChars": 4000,
  "maxItemsPerCompany": 0,
  "maxItems": 200
}
```

# Actor output Schema

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

One record per open posting, in one schema across Greenhouse, Lever and Ashby. Field definitions are in the dataset schema.

# 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": [
        "stripe",
        "lever:spotify",
        "ashby:ashby"
    ],
    "keywords": [
        "engineer"
    ]
};

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

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

```

## MCP server setup

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

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/lm7URypXVZXjm51ae/builds/h1SrEbZYfe1X5Avbj/openapi.json
