# ATS Job Boards Scraper — Greenhouse, Lever, Ashby, Workable +4 (`arthursbuisness/ats-job-boards-greenhouse-lever-ashby`) Actor

- **URL**: https://apify.com/arthursbuisness/ats-job-boards-greenhouse-lever-ashby.md
- **Developed by:** [Arthur](https://apify.com/arthursbuisness) (community)
- **Categories:** Jobs, Business, Lead generation
- **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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## ATS Job Boards Scraper — Greenhouse, Lever, Ashby, Workable, SmartRecruiters, Recruitee, Personio, Teamtailor

Pull **every open job of any company** that runs its careers page on one of eight applicant-tracking systems (ATS), straight from the ATS's **official public job-board API** — the same JSON/XML feed the company's own careers page uses. No login, no API key, no browser, no proxies, no HTML guessing. One run, many companies, one clean dataset.

Supported ATS and the endpoint used:

| ATS | example input | what we read |
|---|---|---|
| Greenhouse | `greenhouse:discord`, `https://boards.greenhouse.io/gitlab`, `https://job-boards.greenhouse.io/discord` | `boards-api.greenhouse.io/v1/boards/{slug}/jobs?content=true` |
| Lever | `lever:palantir`, `https://jobs.lever.co/spotify` (EU boards too) | `api.lever.co/v0/postings/{slug}?mode=json` |
| Ashby | `ashby:openai`, `https://jobs.ashbyhq.com/ramp` | `api.ashbyhq.com/posting-api/job-board/{slug}` (with compensation) |
| Workable | `workable:blueground`, `https://apply.workable.com/nuvei` | widget list + job detail endpoints |
| SmartRecruiters | `smartrecruiters:Ubisoft2`, `https://jobs.smartrecruiters.com/BoschGroup` | `api.smartrecruiters.com/v1/companies/{id}/postings` (+ detail) |
| Recruitee | `recruitee:deephealth`, `https://deephealth.recruitee.com` | `{slug}.recruitee.com/api/offers/` |
| Personio | `personio:demo`, `https://demo.jobs.personio.de` | `{slug}.jobs.personio.de/xml` (also `.com`) |
| Teamtailor | `teamtailor:career`, `https://career.teamtailor.com`, any Teamtailor careers domain | `{host}/jobs.rss` |

You can also pass a plain **company careers URL**: the actor looks for an embedded ATS on that page and, failing that, probes the eight ATSs with the company's domain name (e.g. `https://discord.com/careers` → Greenhouse `discord`). This is best effort — when the careers page is a JavaScript app that hides its ATS, pass `ats:slug` explicitly (the slug is visible in the URL of any job on the careers site).

Typical uses: job-board aggregation, recruiting and sales lead lists (who is hiring for what), competitor hiring intelligence, salary research, alerting on new roles at target companies, feeding LLM pipelines. Schedule the actor and deduplicate on `url` to get only new postings.

### What you get

One row per job posting (`type: "job"`), same fields for every ATS:

| field | description |
|---|---|
| `source`, `board`, `company` | ATS name, board slug, company name (Lever, Ashby and Recruitee APIs do not expose a display name → the slug is used) |
| `id`, `title`, `url`, `apply_url` | ATS job id, title, public job page, direct apply link |
| `location`, `locations`, `country` | Primary location string, all listed offices/locations, country when the ATS provides it |
| `remote`, `workplace_type` | `remote` is the ATS flag when it exists, otherwise inferred from location/title; `workplace_type` = `remote` / `hybrid` / `onsite` when known |
| `department`, `team`, `employment_type`, `seniority` | As provided by the ATS (e.g. `Full-Time`, `Mid-Senior Level`) |
| `posted_at`, `updated_at` | ISO 8601 timestamps |
| `salary_min`, `salary_max`, `salary_currency`, `salary_interval`, `salary_text` | Structured pay data when the ATS exposes it (Lever, Ashby, Recruitee, Personio); otherwise `salary_text` is a best-effort extraction of a pay range found in the description (e.g. `$180,000 to $202,000`) |
| `language`, `tags` | Posting language; ATS metadata such as function, industry, education, custom fields |
| `description` | Full job description as clean plain text (headings and `- ` bullets kept) — optional `description_html` |

Boards that cannot be read (unknown slug, private board, unsupported ATS) produce a `type: "error"` row with the reason — **free** — and the run continues.

Export as JSON, CSV or Excel, or push rows to Google Sheets, Make, Zapier, webhooks or your code via the Apify API.

### Input

| field | default | meaning |
|---|---|---|
| `boards` | — | `ats:slug` entries and/or URLs (see table above) |
| `keywords` | — | keep jobs whose title/description/department/team contains one of these |
| `excludeKeywords` | — | drop jobs whose title/description contains one of these |
| `locations` | — | keep jobs whose location/offices/country contains one of these strings |
| `departments` | — | keep jobs whose department/team contains one of these strings |
| `remoteOnly` | false | keep only remote jobs |
| `postedSince` | — | keep jobs published on/after `YYYY-MM-DD` |
| `includeDescription` | true | store the plain-text description |
| `includeHtml` | false | also store `description_html` |
| `maxItemsPerBoard` | 500 | cap per board (after filters) |
| `maxItems` | 5000 | cap for the whole run |

Example — remote engineering roles at four companies, posted this month:

```json
{
  "boards": ["greenhouse:discord", "lever:palantir", "ashby:openai", "https://jobs.smartrecruiters.com/Ubisoft2"],
  "keywords": ["engineer", "developer"],
  "remoteOnly": true,
  "postedSince": "2026-08-01",
  "maxItemsPerBoard": 200
}
```

### Pricing

Pay per event: **$0.001 per stored job** ($1 per 1,000 jobs). Only rows actually written to the dataset are charged; error rows and filtered-out jobs are free. Apify platform usage (a few seconds of compute per board) is billed separately by Apify.

### Limitations — please read

- Only companies on the eight ATSs above; Workday, SuccessFactors, iCIMS, Taleo, BambooHR and custom career sites are not supported (they have no comparable public feed).
- The ATS APIs return **currently open** postings only — no history, no closed jobs, no applicant data.
- `company` is the slug for Lever, Ashby and Recruitee (their public APIs carry no company display name).
- For Workable and SmartRecruiters descriptions require one extra request per job (≈2 requests/second, polite rate). Keyword filters for these two match title/department/location before descriptions are fetched.
- Salary: structured only where the ATS provides it; `salary_text` extraction from free text is heuristic and may miss or mis-read ranges.
- Careers-URL auto-detection cannot see inside JavaScript-only careers pages; use `ats:slug` there.
- Greenhouse boards on the EU data centre and Lever EU boards are supported via their `.eu` URLs; SmartRecruiters returns "no public postings" for unknown identifiers (the API does not distinguish).

Not affiliated with any of the ATS vendors. Uses only their public, unauthenticated job-board endpoints intended for careers-page integrations.

# Actor input Schema

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

One entry per company. Either 'ats:slug' — greenhouse:discord, lever:spotify, ashby:openai, workable:blueground, smartrecruiters:Ubisoft2, recruitee:deephealth, personio:demo, teamtailor:career — or a job-board / careers URL such as https://boards.greenhouse.io/gitlab, https://jobs.lever.co/palantir, https://jobs.ashbyhq.com/ramp, https://apply.workable.com/nuvei, https://jobs.smartrecruiters.com/BoschGroup, https://deephealth.recruitee.com, https://demo.jobs.personio.de, https://career.teamtailor.com or a company careers page (the ATS is auto-detected when the page embeds it or the company's domain name matches its ATS slug). Unknown boards produce a free error row.

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

Keep only jobs whose title, description, department or team contains at least one of these words/phrases (case-insensitive). For Workable and SmartRecruiters the match is on title/department/location (descriptions are fetched after filtering).

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

Drop jobs whose title or description contains any of these words/phrases.

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

Keep only jobs whose location, office list or country contains one of these strings, e.g. 'Remote', 'London', 'Germany', 'US'.

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

Keep only jobs whose department or team contains one of these strings, e.g. 'Engineering', 'Sales'.

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

Keep only jobs flagged remote by the ATS or whose location/title mentions remote / work from home / anywhere.

## `postedSince` (type: `string`):

Keep only jobs published on or after this date (uses the ATS publish date; jobs without a date are kept).

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

Store the full job description converted to clean plain text (headings and bullet lists preserved). Turn off for a lighter, faster dataset. Note: for Workable and SmartRecruiters this costs one extra request per job.

## `includeHtml` (type: `boolean`):

Also store the original HTML of the description (description\_html).

## `maxItemsPerBoard` (type: `integer`):

Stop after this many stored jobs per board (after filters).

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

Overall cap for the run across all boards.

## Actor input object example

```json
{
  "boards": [
    "greenhouse:discord",
    "lever:palantir",
    "ashby:ramp",
    "https://boards.greenhouse.io/gitlab"
  ],
  "remoteOnly": false,
  "includeDescription": true,
  "includeHtml": false,
  "maxItemsPerBoard": 500,
  "maxItems": 5000
}
```

# Actor output Schema

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

All items as JSON

## `resultsCsv` (type: `string`):

Same dataset as CSV — open in Excel/Sheets

# 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": [
        "greenhouse:discord",
        "lever:palantir",
        "ashby:ramp",
        "https://boards.greenhouse.io/gitlab"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("arthursbuisness/ats-job-boards-greenhouse-lever-ashby").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": [
        "greenhouse:discord",
        "lever:palantir",
        "ashby:ramp",
        "https://boards.greenhouse.io/gitlab",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("arthursbuisness/ats-job-boards-greenhouse-lever-ashby").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": [
    "greenhouse:discord",
    "lever:palantir",
    "ashby:ramp",
    "https://boards.greenhouse.io/gitlab"
  ]
}' |
apify call arthursbuisness/ats-job-boards-greenhouse-lever-ashby --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,arthursbuisness/ats-job-boards-greenhouse-lever-ashby"
        }
    }
}

```

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/w5AMDDmKAYzTlJm3I/builds/EVlbSxFPOfVhcWSmz/openapi.json
