# Career Site Jobs API: Greenhouse, Lever, Ashby & more (`trilliumandtide/ats-career-jobs`) Actor

Live open jobs straight from company career sites through the official public job-board APIs of Greenhouse, Lever, Ashby, SmartRecruiters, Workable and Recruitee. One normalized schema, salary where published, ATS auto-detection, keyword/location/remote filters and a new-jobs-only monitoring mode.

- **URL**: https://apify.com/trilliumandtide/ats-career-jobs.md
- **Developed by:** [TrilliumAndTide Tools](https://apify.com/trilliumandtide) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 jobs

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

## Career Site Jobs API: Greenhouse, Lever, Ashby, SmartRecruiters, Workable, Recruitee

Get **every open job, live, straight from company career sites**. Paste a list of careers-page URLs or company names and get back one clean, normalized dataset of job postings. Salary is included wherever the employer publishes it. It covers the six most common applicant tracking systems (ATS):

| ATS | Example careers page |
|---|---|
| Greenhouse | `boards.greenhouse.io/airbnb`, `job-boards.greenhouse.io/figma` |
| Lever | `jobs.lever.co/spotify` (EU: `jobs.eu.lever.co/...`) |
| Ashby | `jobs.ashbyhq.com/ramp` |
| SmartRecruiters | `jobs.smartrecruiters.com/BoschGroup` |
| Workable | `apply.workable.com/huggingface` |
| Recruitee | `bunq.recruitee.com` |

#### Why this Actor

- **Live, not a stale database.** Every run reads the employer's job board at that moment, so you see a job as soon as it is posted and stop seeing it once it closes.
- **Official public APIs only.** It uses the documented job-board APIs that each ATS publishes so that job postings can be shown elsewhere. That means no login, no proxies, no CAPTCHA tricks and no blocked runs, and results come back in seconds: 7 company boards and 918 jobs took under 5 seconds in our tests.
- **One schema for all six ATSs.** Title, department, team, locations, country, remote flag, employment type, posting date, apply link, salary (min/max/currency/interval) and a clean-text description.
- **Monitoring mode.** Turn on *Only new jobs* and schedule the Actor. Each run returns, and charges for, only postings it has not delivered before.
- **Auto-detection.** Don't know which ATS a company uses? Enter just its name (e.g. `figma`) and the Actor checks all six.
- **Cheap.** You pay per job saved, and nothing for companies that turn out to have no matching jobs.

#### Use cases

- Job boards and niche aggregators that track hundreds of companies.
- Recruiting agencies watching their target accounts for new openings.
- Sales and market-intelligence teams that treat hiring as a buying or growth signal.
- Job seekers who want an alert as soon as a dream company posts a role.
- Data and AI teams building salary or skills datasets.

### Input

| Field | What it does |
|---|---|
| **Companies** (required) | One per line. Accepts a careers-page URL, `ats:slug` (e.g. `greenhouse:airbnb`, `lever:spotify`, `smartrecruiters:BoschGroup`), or a bare slug (`figma`), which is tried on all six ATSs. EU Lever boards (`jobs.eu.lever.co/...`) are supported. |
| Title must contain | Keep jobs whose title contains any of these words (case-insensitive). |
| Title must not contain | Drop jobs whose title contains any of these words, e.g. `intern`. |
| Location contains | Match on location or country text, e.g. `London`, `Germany`, `US`. The word `remote` also matches remote jobs. |
| Remote jobs only | Keep only jobs the ATS flags as remote. |
| Posted within (days) | Keep only recent postings. |
| Only new jobs since the last run | Monitoring mode (see below). |
| Monitor ID | Separates the job memory for different saved tasks. |
| Include description (text / HTML) | Text is on by default. HTML is optional. |
| Max jobs per company | 0 means no limit. |

Example:

```json
{
  "companies": [
    "https://boards.greenhouse.io/airbnb",
    "https://jobs.ashbyhq.com/ramp",
    "lever:spotify",
    "https://jobs.smartrecruiters.com/Ubisoft2",
    "figma"
  ],
  "titleKeywords": ["engineer"],
  "locationKeywords": ["remote", "united states", "canada"],
  "onlyNewJobs": true,
  "monitorId": "eng-watchlist"
}
```

#### Finding a company's slug

Open the company's careers page and click any job. The address bar shows the ATS and the slug, e.g. `job-boards.greenhouse.io/`**`figma`**`/jobs/…` or `jobs.lever.co/`**`spotify`**`/…`. You can paste that URL as-is.

### Output

One dataset item per job. This is a real record from a test run, with the description shortened:

```json
{
  "id": "ashby:ramp:34413f8d-26bf-4bbc-8ade-eb309a0e2245",
  "ats": "ashby",
  "companySlug": "ramp",
  "jobId": "34413f8d-26bf-4bbc-8ade-eb309a0e2245",
  "companyName": null,
  "title": "Security Engineer, Cloud",
  "department": "Engineering",
  "team": "Backend",
  "location": "New York, NY (HQ)",
  "locations": ["New York, NY (HQ)", "Remote (Canada)", "Remote (US)", "Miami, FL"],
  "country": "USA",
  "isRemote": true,
  "workplaceType": "Hybrid",
  "employmentType": "FullTime",
  "postedAt": "2026-04-07T17:12:35.753Z",
  "updatedAt": null,
  "url": "https://jobs.ashbyhq.com/ramp/34413f8d-26bf-4bbc-8ade-eb309a0e2245",
  "applyUrl": "https://jobs.ashbyhq.com/ramp/34413f8d-26bf-4bbc-8ade-eb309a0e2245/application",
  "salary": { "min": 211400, "max": 290600, "currency": "USD", "interval": "year", "text": "$211.4K – $290.6K • Offers Equity" },
  "descriptionHtml": null,
  "descriptionText": "About Ramp\nRamp is building the smart infrastructure for finance teams, …"
}
```

- **Missing fields are `null`.** Each ATS publishes different fields. For example, Ashby and Lever expose salary ranges when the employer fills them in, while Greenhouse does not.
- **Run summary.** Each run also writes a summary to the `OUTPUT` record of the key-value store. It lists every company with its status (`ok`, `not-found`, `error`), the number of jobs open on the board and the number of jobs saved.
- **Export.** Download as JSON, CSV, Excel or XML, or connect to Google Sheets, Zapier, Make, Slack or a webhook.

### Monitoring mode (only new jobs)

1. Turn on **Only new jobs since the last run** and give the task a **Monitor ID**.
2. Save it as a task and add a schedule, e.g. daily at 07:00.
3. The first run returns everything currently open. Later runs return only postings that appeared since then. You pay only for those.

Jobs held back by *Max jobs per company* or by your spending limit are delivered on the next run, not lost. If you change the filters, use a new Monitor ID.

### Pricing

Pay per event: **$1.00 per 1,000 jobs saved** ($0.001 per job), plus Apify's standard small charge per run start. Filtered-out jobs, unknown companies and repeat jobs in monitoring mode cost nothing. Set *Maximum cost per run* in the run options and the Actor stops cleanly when it reaches that limit.

### Call it from your code

```js
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('<your-username>/ats-career-jobs').call({
  companies: ['greenhouse:airbnb', 'https://jobs.ashbyhq.com/ramp'],
  titleKeywords: ['engineer'],
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

### FAQ

**Is this legal?** The Actor reads the public job-board APIs that the ATS vendors provide so that published postings can be shown elsewhere. For example, Greenhouse's documentation says: "Job Board data is publicly available, so authentication is not required for any GET endpoints." The Actor never logs in or bypasses any protection, and it only collects job postings. It does not collect personal data such as recruiter names or application inbox addresses. Workable's endpoint is the public one used by its embeddable jobs widget.

**Does it cover Workday, iCIMS, LinkedIn or Indeed?** Not yet. LinkedIn and Indeed prohibit automated access. Workday and iCIMS have no comparable public API. If you'd like another ATS that has a public job-board API, open an issue.

**A company returns `not-found`.** Either the slug is wrong (copy it from a job URL) or the company uses a different ATS. Try the bare company name so the Actor checks all six.

**How fast is it?** Each board is a single API call (SmartRecruiters needs one call per 100 jobs, plus one per job for descriptions). In our tests, 7 boards and 918 open jobs took under 5 seconds.

# Actor input Schema

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

One per line. Accepts a careers-page URL (https://jobs.lever.co/spotify, https://boards.greenhouse.io/airbnb, https://jobs.ashbyhq.com/ramp, https://apply.workable.com/huggingface, https://bunq.recruitee.com, https://jobs.smartrecruiters.com/BoschGroup), an explicit ats:slug (greenhouse:airbnb) or a bare slug (airbnb), which is tried on every supported ATS.

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

Keep jobs whose title contains at least one of these words (case-insensitive). Leave empty for all jobs.

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

Drop jobs whose title contains any of these words, e.g. intern, senior.

## `locationKeywords` (type: `array`):

Keep jobs whose location or country contains one of these, e.g. London, Germany, US. The word remote also matches remote jobs.

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

Keep only jobs the ATS marks as remote.

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

Keep jobs first published in the last N days. Jobs without a date are kept.

## `onlyNewJobs` (type: `boolean`):

Monitoring mode for scheduled runs: output (and pay for) only postings not seen by a previous run with the same monitor ID. The first run returns everything currently open.

## `monitorId` (type: `string`):

Name of the job memory used by 'Only new jobs'. Use a different ID per saved task or company list.

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

Adds the full job description as clean text.

## `includeDescriptionHtml` (type: `boolean`):

Also adds the original description HTML.

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

0 means no limit.

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

How many career boards to fetch at once.

## Actor input object example

```json
{
  "companies": [
    "https://boards.greenhouse.io/airbnb",
    "https://jobs.ashbyhq.com/ramp",
    "lever:spotify"
  ],
  "remoteOnly": false,
  "onlyNewJobs": false,
  "monitorId": "default",
  "includeDescription": true,
  "includeDescriptionHtml": false,
  "maxJobsPerCompany": 0,
  "maxConcurrency": 10
}
```

# Actor output Schema

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

All jobs saved by this run, in the normalized job schema.

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

Per-company counts, ATS detected and any errors.

# 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": [
        "https://boards.greenhouse.io/airbnb",
        "https://jobs.ashbyhq.com/ramp",
        "lever:spotify"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("trilliumandtide/ats-career-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 = { "companies": [
        "https://boards.greenhouse.io/airbnb",
        "https://jobs.ashbyhq.com/ramp",
        "lever:spotify",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("trilliumandtide/ats-career-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 '{
  "companies": [
    "https://boards.greenhouse.io/airbnb",
    "https://jobs.ashbyhq.com/ramp",
    "lever:spotify"
  ]
}' |
apify call trilliumandtide/ats-career-jobs --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,trilliumandtide/ats-career-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/NYaGd1pOLNcb6AB9E/builds/8larv0hkSueH2JBFE/openapi.json
