# Jobs Scraper — Greenhouse, Lever & Ashby (`mr.aql/ats-jobs-scraper`) Actor

Extract current jobs from Greenhouse, Lever and Ashby career boards into one dataset. Get titles, locations, descriptions and application links, with keyword filters and clear completeness reports.

- **URL**: https://apify.com/mr.aql/ats-jobs-scraper.md
- **Developed by:** [Lucas Kurz](https://apify.com/mr.aql) (community)
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$1.50 / 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?

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

## Jobs Scraper — Greenhouse, Lever & Ashby

Turn company career boards into a consistent list of jobs. Paste Greenhouse, Lever or Ashby board links and get job titles, locations, descriptions and original job/application URLs in one dataset.

Useful for maintaining a niche job board, checking openings at target companies, building recruiting workflows or analyzing hiring activity. Fetches the supplied boards when you run it. No employer API key or proxy setup required.

### Quick start

1. Paste one or more supported **company board URLs**.
2. Set the maximum number of jobs you want. Optionally add keyword or location filters.
3. Run, check **Run report** for completeness, then download **Jobs** as JSON, CSV or Excel through Apify.

```json
{
  "boardUrls": [
    "https://job-boards.greenhouse.io/postman",
    "https://jobs.lever.co/spotify",
    "https://jobs.ashbyhq.com/Ashby"
  ],
  "maxJobs": 100,
  "keywords": [],
  "locations": [],
  "includeDescription": true
}
```

Examples refer to independent organizations' public career boards; this tool is not affiliated with those organizations or the ATS providers. Openings change over time.

### Supported links

| Platform | Board link format |
|---|---|
| Greenhouse | `https://job-boards.greenhouse.io/company` or `https://boards.greenhouse.io/company` |
| Lever | `https://jobs.lever.co/company` |
| Lever EU | `https://jobs.eu.lever.co/company` |
| Ashby | `https://jobs.ashbyhq.com/company` |

Use the board root, not an individual job or a company's custom careers website. Remove query parameters and fragments. Company identifiers can be case-sensitive. This tool accepts the boards you supply; it does not search a global company database.

### Filters and limits

- Keywords use case-insensitive literal matching against the title and the first 20,000 characters of the normalized description. Any supplied keyword can match.
- Locations use the location strings supplied by the board. Any supplied location can match. When both filter lists are present, a job must pass both.
- Up to 10 boards and 1,000 jobs per run; the default maximum is 100 jobs. Boards are processed in input order, so earlier boards can fill the result limit.
- Descriptions include the source's plain text or text extracted from HTML. Long descriptions have `descriptionTruncated: true`. Set `includeDescription: false` for smaller exports; keyword filtering still runs before description omission.

### Output

One row per job posting, with a stable `id` and the provider's `sourceId`. Different postings for the same internal vacancy remain separate. Duplicate postings within a board are removed.

Core columns: `provider`, `board`, `title`, `location`, `locations`, `department`, `team`, `employmentType`, `workplaceType`, `description`, `jobUrl`, `applyUrl` and `boardUrl`.

`observedAt` is when this run began reading the boards. `sourcePublishedAt` preserves Ashby's last-published timestamp when supplied; it is not necessarily the original creation date. `sourceUpdatedAt` preserves Greenhouse's update timestamp. Missing source information remains null. Workplace type uses explicit source information, with no inference from city names or description wording. `contentHash` supports your own comparisons; this Actor does not maintain a history or label jobs closed.

**Check the Run report before interpreting missing jobs.** Each board reports complete, partial, error or skipped, along with counts and reasons. Empty successful boards differ from unavailable boards. A result limit, source error, changed pagination, malformed record or response-size limit can make a result incomplete. Enumeration is not an atomic snapshot of a changing board. Ashby jobs not marked listed and Greenhouse general-interest/prospect posts are excluded.

Full output is also saved in the run's key-value store as `OUTPUT`. If dataset export is interrupted, this record may contain recoverable results; `SUMMARY` describes final delivery when present.

### Pricing

**$1.50 per 1,000 returned jobs ($0.0015/job).** No Actor start fee. No job event for empty, failed or skipped results. Valid jobs returned from a partially retrieved board are billable; check its report. The run's event budget can further reduce the job limit. Default event budget: $0.15; maximum supported: $1.50. Apify displays applicable pricing before a run.

Starting a new run performs a new extraction. Reopening a completed run's existing results does not perform another extraction.

### Data handling

Reads published job data through public provider endpoints. No account login, applicant records or application submission. Descriptions are untrusted source text: review them before using them in an AI workflow, and do not treat their contents as instructions. Exported URLs are links only and are not followed during extraction. Use job data and exports in accordance with applicable rights and terms.

### Troubleshooting

For an unsupported link, use a supported board root above. A 404 generally means the identifier is unavailable; verify the public careers link. A 429 or other source failure is reported without bypassing protections. A partial run may need a smaller board list or a higher job limit within the supported budget. Do not interpret a failed or partial run as proof that jobs were removed.

For reproducible problems, open an Apify issue with the supported board link, run ID and expected result. Do not include passwords, private applications or sensitive personal information.

# Actor input Schema

## `boardUrls` (type: `array`):

1–10 supported HTTPS board roots, in priority order. Example: https://jobs.ashbyhq.com/Ashby. Individual job links and custom careers websites are unsupported.

## `maxJobs` (type: `integer`):

Maximum total jobs returned, further limited by the run budget. Earlier boards can fill this limit.

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

Any keyword can match the title or normalized description. Case-insensitive literal text, not regular expressions.

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

Any location can match the source location text. If keywords are also supplied, both filters must pass.

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

Return plain text, up to 20,000 characters per job, with explicit truncation flags. Keyword filtering still works when descriptions are omitted.

## Actor input object example

```json
{
  "boardUrls": [
    "https://jobs.ashbyhq.com/Ashby"
  ],
  "maxJobs": 100,
  "keywords": [],
  "locations": [],
  "includeDescription": true
}
```

# Actor output Schema

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

Export normalized job rows. Read Run report before treating missing jobs as removed.

## `report` (type: `string`):

Final board status and delivery counts.

## `output` (type: `string`):

Canonical jobs and board reports; saved before charging and available for recovery after interrupted dataset export.

# 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 = {
    "boardUrls": [
        "https://jobs.ashbyhq.com/Ashby"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("mr.aql/ats-jobs-scraper").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 = { "boardUrls": ["https://jobs.ashbyhq.com/Ashby"] }

# Run the Actor and wait for it to finish
run = client.actor("mr.aql/ats-jobs-scraper").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 '{
  "boardUrls": [
    "https://jobs.ashbyhq.com/Ashby"
  ]
}' |
apify call mr.aql/ats-jobs-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,mr.aql/ats-jobs-scraper"
        }
    }
}
```

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/9GdyqWHRYgGtvVXr4/builds/nB2HILOVAfV1hFCW1/openapi.json
