# ATS Jobs Scraper: Greenhouse, Lever, Workday, Personio +9 (`endurable_aqueduct/ats-jobs-scraper`) Actor

Get normalized job postings from any company's career site via the public job-board APIs and feeds of 13 ATS: Greenhouse, Lever, Ashby, Workable, SmartRecruiters, Recruitee, Personio, Teamtailor, BambooHR, Breezy HR, Pinpoint, JazzHR and Workday. Salary, remote flag, filters, pay per job.

- **URL**: https://apify.com/endurable_aqueduct/ats-jobs-scraper.md
- **Developed by:** [Matija Sever](https://apify.com/endurable_aqueduct) (community)
- **Categories:** Jobs, Lead generation, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$1.50 / 1,000 job results

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

## ATS Jobs Scraper: Greenhouse, Lever, Workday, Personio and 9 more

Get every open job from any company's career site as clean, normalized JSON. Give the Actor a list of
companies (career page URLs, `ats:slug` shorthands, the company's own careers page or just a name) and it
returns one item per job with the same fields for all 13 supported applicant tracking systems (ATS):
title, locations, countries, remote/hybrid, department, employment type, **salary range**, publication date,
full description and the apply link.

It reads only the **public job-board APIs and feeds** that the ATS vendors serve for career sites (documented
APIs where they exist, otherwise the same public JSON or XML the career page itself loads).
No login, no browser, no proxies needed, no personal data. That makes it fast (hundreds of jobs per second
for API-based boards), cheap and reliable.

Only need one platform? Single-platform editions with the same engine, output and price:
[Workday Jobs Scraper](https://apify.com/endurable_aqueduct/workday-jobs-scraper),
[Greenhouse Jobs Scraper](https://apify.com/endurable_aqueduct/greenhouse-jobs-scraper),
[Lever Jobs Scraper](https://apify.com/endurable_aqueduct/lever-jobs-scraper) and
[Ashby Jobs Scraper](https://apify.com/endurable_aqueduct/ashby-jobs-scraper).

### Supported applicant tracking systems

| ATS | Example input | Public source used | Salary | Notes |
|---|---|---|---|---|
| Greenhouse | `https://boards.greenhouse.io/airbnb` | Job Board API (`boards-api.greenhouse.io`) | Yes (pay transparency ranges) | US and EU (`job-boards.eu.greenhouse.io`) boards, embedded boards |
| Lever | `https://jobs.lever.co/palantir` | Postings API, US and EU hosts | Yes | EU boards (`jobs.eu.lever.co`) detected automatically |
| Ashby | `https://jobs.ashbyhq.com/linear` | Job Posting API | Yes | Compensation tiers included |
| Workable | `https://apply.workable.com/skroutz/` | Job widget API | No | |
| SmartRecruiters | `https://jobs.smartrecruiters.com/Ubisoft2` | Posting API | No | One extra request per job for descriptions |
| Recruitee | `https://bunq.recruitee.com` | Careers Site API | Yes | Custom domains detected from the careers page |
| Personio | `https://1komma5grad.jobs.personio.de` | XML job feed | Yes, when published | Optional language (`en`, `de`, ...) |
| Teamtailor | `https://polestar.teamtailor.com` | Career site RSS feed | No | Custom domains supported |
| BambooHR | `https://switch2.bamboohr.com/careers` | Careers site JSON | Yes (parsed from text) | One extra request per job |
| Breezy HR | `https://vetsez.breezy.hr` | Careers JSON feed | Yes (parsed from text) | |
| Pinpoint | `https://skims.pinpointhq.com` | `postings.json` feed | Yes | No publication date in the feed |
| JazzHR | `https://aerotech.applytojob.com/apply` | XML job feed | No | |
| Workday | `https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite` | Career site JSON (CXS) | No | Max 2,000 results per search (Workday limit) |

A single job link from any of these boards works too; the Actor resolves it to the company board.

### What can you use it for?

- **Job boards and aggregators**: power a niche board (a country, a stack, remote-only, startups of a VC
  portfolio) directly from company ATS data instead of reposted ads.
- **Job alerts and monitoring**: schedule the Actor daily with **Only new jobs** turned on and get only
  postings that appeared since the last run (and pay only for those).
- **Sales and recruiting intelligence**: hiring is a buying signal. Track which companies open roles in
  data, security or sales, how fast teams grow and where.
- **Salary research**: Greenhouse, Lever, Ashby, Recruitee, Personio, Pinpoint and others publish pay ranges;
  the Actor returns them as numbers with currency and period.
- **AI agents (MCP)**: agents can answer "which of these 30 companies hire remote ML engineers in Europe?"
  in one call. All fields have clear names and descriptions.

### How to use it

1. Open the Actor and add companies to **Companies**, one per line. Any of these formats works:
   - a board URL: `https://jobs.lever.co/palantir`, `https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite`
   - a shorthand: `greenhouse:airbnb`, `lever:palantir`, `personio:1komma5grad`, `workday:nvidia.wd5/NVIDIAExternalCareerSite`
   - the company's own careers page: `https://www.bunq.com/careers` (the embedded ATS board is detected)
   - a bare name: `openai` (every supported ATS is probed for a board with that name)
2. Optionally set filters (keywords, locations, remote only, posted within N days, departments, employment types).
3. Click **Start**. Results appear in the **Output** tab; a per-company report is saved as `RUN_SUMMARY`.

### Input example

```json
{
    "companies": [
        "https://boards.greenhouse.io/airbnb",
        "lever:palantir",
        "https://1komma5grad.jobs.personio.de",
        "https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite"
    ],
    "keywords": ["engineer", "data"],
    "locations": ["Germany", "Remote"],
    "postedWithinDays": 14,
    "maxJobsPerCompany": 200,
    "includeDescription": true
}
```

| Field | What it does |
|---|---|
| `companies` (required) | Career page URLs, `ats:slug` shorthands, company careers pages or names. |
| `keywords` / `keywordMatch` | Whole-word match in the title (default) or title and description. `engineer*` = prefix match. |
| `excludeKeywords` | Drop titles containing any of these words (e.g. `senior`, `intern`). |
| `locations` | City, region, country name or ISO code (`DE`, `US`, `GB`), or `Remote`. |
| `remoteOnly` | Only fully remote jobs. |
| `postedWithinDays` | Only jobs published in the last N days. |
| `departments` | Department or team contains any of these terms. |
| `employmentTypes` | `full_time`, `part_time`, `contract`, `temporary`, `internship`, `apprenticeship`, `volunteer`. |
| `maxJobsPerCompany` / `maxTotalJobs` | Caps. Newest jobs first where the ATS publishes dates; on very large paginated boards (Workday, SmartRecruiters) listing stops once enough matches are found. |
| `includeDescription` / `includeDescriptionHtml` | Plain-text description (default on) and/or original HTML. |
| `onlyNewJobs` / `stateKey` | Monitoring mode: skip jobs returned by earlier runs with the same key. |

### Output example

Each job is one dataset item. Every item has the same keys; values an ATS does not publish are `null`.

```json
{
    "company": "Airbnb",
    "company_slug": "airbnb",
    "ats": "greenhouse",
    "job_id": "8318155",
    "title": "Senior Compliance Lead, EMEA",
    "url": "https://careers.airbnb.com/positions/8318155?gh_jid=8318155",
    "apply_url": "https://careers.airbnb.com/positions/8318155?gh_jid=8318155",
    "locations": ["Brussels", "Dublin, Ireland", "London, United Kingdom"],
    "countries": ["BE", "IE", "GB"],
    "is_remote": false,
    "workplace_type": "hybrid",
    "department": "Business Operations/Analysis",
    "team": null,
    "employment_type": null,
    "employment_type_raw": null,
    "salary_min": 130000,
    "salary_max": 160000,
    "salary_currency": "EUR",
    "salary_period": "year",
    "salary_text": "Belgium Annual Pay Range: EUR 130,000 - 160,000",
    "posted_at": "2026-10-23T14:32:32Z",
    "updated_at": "2026-10-23T14:32:32Z",
    "description_text": "Airbnb was born in 2007 when two hosts welcomed three guests ...",
    "source_url": "https://boards-api.greenhouse.io/v1/boards/airbnb/jobs?content=true&pay_transparency=true",
    "scraped_at": "2026-10-25T16:38:00Z"
}
```

Field notes:

- `workplace_type` is `remote`, `hybrid`, `onsite` or `null` (not stated by the company). `is_remote` is `true`
  for fully remote jobs and jobs that offer remote locations.
- `countries` holds ISO 3166-1 alpha-2 codes taken from the ATS's structured data or recognized location
  names (e.g. `Berlin` becomes `DE`). It is empty when the location is ambiguous.
- `employment_type` is normalized across vendors and languages (`Vollzeit`, `FullTime`, `Permanent` all become
  `full_time`); `employment_type_raw` keeps the original value.
- `posted_at` / `updated_at` are ISO 8601 UTC. Workday dates come from the job detail; JazzHR dates are
  derived from the job's creation timestamp; Pinpoint publishes no date.

The **Output** tab offers three views: *Overview*, *Salaries* and *All fields*.

#### Run summary

The key-value store record `RUN_SUMMARY` lists every input with its status, the detected ATS and how many jobs
were found, matched and saved, so failed or empty boards are easy to spot:

```json
{
    "status": "completed",
    "message": "Saved 42 job(s) from 14/16 companies; 2 failed (see RUN_SUMMARY in the key-value store).",
    "totals": {"companies": 16, "succeeded": 14, "failed": 2, "jobs_found": 4415, "jobs_saved": 42},
    "companies": [
        {"input": "openai", "status": "ok", "ats": "ashby", "company": "OpenAI", "detected_via": "name_probe",
         "jobs_available": 793, "jobs_found": 793, "jobs_matched": 3, "jobs_saved": 3, "error": null},
        {"input": "https://boards.greenhouse.io/doesnotexist-xyz-123", "status": "error", "ats": "greenhouse",
         "error": "Greenhouse board not found (HTTP 404 (not found)). Check the URL / company identifier."}
    ]
}
```

One failing company never fails the run: requests are retried with exponential backoff (respecting
`Retry-After`), every company has a timeout, and errors are reported per company.

### Monitoring new jobs

Turn on **Only new jobs** and run the Actor on a schedule (for example daily). Each run returns only jobs that
earlier runs with the same **State key** have not returned, so you receive (and pay for) new postings only.
The list of returned job IDs is kept for 120 days in a named key-value store `ats-jobs-scraper-state` in your
account. Use a different state key for each schedule or saved task.

Tip: the first run returns all currently open jobs that match your filters. If you also set
`maxJobsPerCompany`, jobs held back by that cap count as new in later runs, so for monitoring prefer filters
(keywords, locations) over small caps. Already-known jobs are dropped before any per-job request, so
monitoring runs stay fast and cheap.

### Use with AI agents (MCP)

The Actor works well as a tool for AI agents through the [Apify MCP server](https://mcp.apify.com): the input
accepts plain company names and URLs, every field is documented, and the output is flat JSON. Tips for agents:

- Prefer URLs or `ats:slug` shorthands when you know them; bare names are resolved by probing ATS vendors.
- Set `maxJobsPerCompany` and `keywords` to keep results (and token usage) small; set `includeDescription`
  to `false` when you only need titles and links.
- Read `RUN_SUMMARY` to see which companies had no board or no matching jobs.

Example prompt: *"Use the ATS Jobs Scraper to list remote data engineering jobs posted in the last 7 days at
Airbnb, Stripe, Palantir and OpenAI, with salary ranges."*

### Use via API

Run the Actor and get the results in one call (replace `<TOKEN>` and `<ACTOR_ID>`):

```bash
curl -X POST "https://api.apify.com/v2/acts/<ACTOR_ID>/run-sync-get-dataset-items?token=<TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"companies": ["greenhouse:airbnb", "lever:palantir"], "keywords": ["engineer"], "maxJobsPerCompany": 50}'
```

Python (`apify-client` 3.x):

```python
from decimal import Decimal

from apify_client import ApifyClient

client = ApifyClient("<TOKEN>")
run = client.actor("<ACTOR_ID>").call(
    run_input={"companies": ["https://jobs.ashbyhq.com/linear"], "keywords": ["engineer"]},
    max_total_charge_usd=Decimal("1.00"),  # optional cost cap for this run
)
for job in client.dataset(run.default_dataset_id).iterate_items():
    print(job["title"], job["locations"], job["url"])
```

### Pricing

This Actor uses **pay per event**: you pay for the job postings you get, not for compute time.

| Event | Price |
|---|---|
| Job posting saved to the dataset | $1.50 per 1,000 jobs ($0.0015 per job) |
| Actor start | $0.00005 per run (Apify default) |

Examples: 10 companies with 50 matching jobs each = 500 jobs = about $0.75. A daily monitoring run over
100 companies that finds 40 new jobs costs about $0.06. Failed companies, duplicates and jobs skipped by your
filters or by **Only new jobs** are never charged. Set **Maximum cost per run** in the run options to cap
spending: the Actor stops collecting exactly when your limit is reached, and `RUN_SUMMARY` says so.

### FAQ

**Is it legal to scrape job postings?**
The Actor collects job advertisements that companies publish publicly in order to distribute them, through the
public endpoints that the ATS vendors provide for career sites and job widgets. It does not log in, bypass
access controls or collect personal data: recruiter names and contact details that some feeds contain are
dropped. You remain responsible for how you use the data (for example respecting the companies' terms and
any rules that apply to republishing). If in doubt, consult a lawyer.

**How do I find a company's ATS?**
Paste the company's careers page URL: the Actor detects embedded or linked boards from 13 vendors. You can
also paste any job link from the company; the board is derived from it.

**Why is a field `null`?**
Only data the company publishes is returned. For example, Workable and Teamtailor feeds have no salary,
Workday has no department, Pinpoint has no publication date.

**Why did a company return 0 jobs?**
Check `RUN_SUMMARY`: the board may not exist (`error`), may have no open jobs, or your filters removed
everything (`jobs_found` > 0 but `jobs_matched` = 0). SmartRecruiters returns an empty list instead of an
error for unknown company identifiers.

**Does it scrape LinkedIn, Indeed or Glassdoor?**
No. It reads company career sites (ATS boards) directly, which are the original source of those postings.

**How fresh is the data?**
Live: every run reads the boards at run time. There is no cached database in between.

**Can you add another ATS?**
Open an issue in the Issues tab with an example career page URL.

### Limitations

- Workday's career-site search returns at most 2,000 jobs per site; use `keywords` (sent to Workday's own
  search) to reach specific roles on very large tenants.
- Bare company names are matched against board identifiers, so a different company with the same identifier
  on another ATS can be picked. Use URLs for certainty; the summary marks name matches with `detected_via: name_probe`.
- Career pages that load their job board only with JavaScript from an unknown source cannot be detected;
  pass the ATS board URL instead.

# Changelog

This Actor's version history is a separate document: https://apify.com/endurable_aqueduct/ats-jobs-scraper/changelog.md

# Actor input Schema

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

One company per line. Accepted formats:

- ATS board URL, e.g. https://boards.greenhouse.io/airbnb, https://jobs.lever.co/palantir, https://jobs.eu.lever.co/quantinuum, https://jobs.ashbyhq.com/linear, https://apply.workable.com/skroutz/, https://jobs.smartrecruiters.com/Ubisoft2, https://bunq.recruitee.com, https://1komma5grad.jobs.personio.de, https://polestar.teamtailor.com, https://switch2.bamboohr.com/careers, https://vetsez.breezy.hr, https://skims.pinpointhq.com, https://aerotech.applytojob.com/apply, https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite. A single job link from the board works too.
- Shorthand 'ats:slug': greenhouse:airbnb, lever:palantir, ashby:linear, workable:skroutz, smartrecruiters:Ubisoft2, recruitee:bunq, personio:1komma5grad, teamtailor:polestar, bamboohr:switch2, breezy:vetsez, pinpoint:skims, jazzhr:aerotech, workday:nvidia.wd5/NVIDIAExternalCareerSite.
- The company's own careers page (e.g. https://www.bunq.com/careers): the page is scanned for an embedded or linked ATS board.
- A bare company name (e.g. openai): every supported ATS is probed for a board with that identifier; the first one with open jobs is used. Prefer URLs when the name is ambiguous.

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

Keep only jobs whose title (or title and description, see 'Match keywords in') contains at least one of these terms. Case- and accent-insensitive whole-word match; multi-word phrases are allowed; add \* for a prefix match (engineer\* matches engineering). Leave empty for all jobs.

## `keywordMatch` (type: `string`):

Where 'Keywords' must appear: the job title only (precise, default) or the title or the description (broader).

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

Drop jobs whose title contains any of these terms (same matching rules as 'Keywords'), e.g. senior, intern, manager.

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

Keep jobs in any of these places. Each value is matched against the job's location text and country: a city ('Berlin'), a region ('Bavaria'), a country name ('Germany') or a two-letter ISO country code ('DE', 'US', 'GB'/'UK'). Use 'Remote' to also match remote jobs. Some ATSs only publish a city, so add city names for best coverage.

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

Keep only fully remote jobs (workplace_type = remote or is_remote = true). Hybrid jobs are excluded.

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

Keep only jobs published in the last N days (0 = no limit). Uses the publication date, or the last-updated date when the ATS has no publication date; jobs with neither (Pinpoint) are excluded while this filter is on.

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

Keep jobs whose department or team contains any of these terms, e.g. Engineering, Sales. Workday's public API has no department field, so Workday jobs are excluded while this filter is on.

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

Keep only these normalized employment types. Jobs whose ATS does not publish a type (Greenhouse without custom fields, Teamtailor) are excluded while this filter is on.

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

Maximum number of matching jobs saved per company, newest first where the ATS publishes dates (0 = no limit). Each saved job is one billable result.

## `maxTotalJobs` (type: `integer`):

Stop the whole run after this many jobs are saved (0 = no limit). Useful to cap cost; the run also stops automatically at your 'Maximum cost per run'.

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

Add the full job description as clean plain text (description_text). Turn off for smaller, faster results; SmartRecruiters then also skips one request per job.

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

Also add the original description HTML (description_html), e.g. to render the posting.

## `language` (type: `string`):

Optional ISO 639-1 language code (e.g. en, de) requested from Personio feeds. When a job has no translation, the company's default language is used. Other ATSs return the language the company published.

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

Return only jobs that earlier runs with the same 'State key' have not returned yet - ideal for scheduled daily runs (you only pay for new jobs). The list of returned jobs is kept for 120 days in a named key-value store 'ats-jobs-scraper-state' in your account.

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

Name of the job list used by 'Only new jobs'. Use a different key per saved task or schedule so they don't share state.

## `detectAtsFromCareersPage` (type: `boolean`):

For URLs that are not an ATS board (e.g. https://acme.com/careers), download that one page and look for an embedded or linked job board.

## `probeCompanyNames` (type: `boolean`):

For entries that are just a name (e.g. openai), probe every supported ATS for a board with that identifier and use the first one with open jobs.

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

How many companies are processed at the same time.

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

Timeout for a single HTTP request. Failed requests are retried with exponential backoff.

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

Retries for timeouts, network errors, HTTP 429 and 5xx responses (respecting Retry-After).

## `proxyConfiguration` (type: `object`):

Not needed: all sources are public APIs/feeds. Only enable a proxy if your own network or a vendor rate-limits you.

## Actor input object example

```json
{
  "companies": [
    "https://boards.greenhouse.io/airbnb",
    "lever:palantir",
    "https://1komma5grad.jobs.personio.de",
    "https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite"
  ],
  "keywords": [
    "data engineer",
    "python",
    "machine learning"
  ],
  "keywordMatch": "title",
  "excludeKeywords": [
    "senior",
    "intern"
  ],
  "locations": [
    "Germany",
    "Berlin",
    "Remote"
  ],
  "remoteOnly": false,
  "postedWithinDays": 7,
  "departments": [
    "Engineering",
    "Data"
  ],
  "maxJobsPerCompany": 10,
  "maxTotalJobs": 0,
  "includeDescription": true,
  "includeDescriptionHtml": false,
  "language": "en",
  "onlyNewJobs": false,
  "stateKey": "default",
  "detectAtsFromCareersPage": true,
  "probeCompanyNames": true,
  "maxConcurrency": 5,
  "requestTimeoutSecs": 30,
  "maxRetries": 3,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

All saved job postings, one item per job.

## `overview` (type: `string`):

Main fields only.

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

JSON record with status (ok/error/skipped), detected ATS, jobs found/matched/saved and error messages for every input 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": [
        "https://boards.greenhouse.io/airbnb",
        "https://jobs.lever.co/palantir",
        "https://jobs.ashbyhq.com/linear",
        "https://bunq.recruitee.com"
    ],
    "maxJobsPerCompany": 10,
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("endurable_aqueduct/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 = {
    "companies": [
        "https://boards.greenhouse.io/airbnb",
        "https://jobs.lever.co/palantir",
        "https://jobs.ashbyhq.com/linear",
        "https://bunq.recruitee.com",
    ],
    "maxJobsPerCompany": 10,
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("endurable_aqueduct/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 '{
  "companies": [
    "https://boards.greenhouse.io/airbnb",
    "https://jobs.lever.co/palantir",
    "https://jobs.ashbyhq.com/linear",
    "https://bunq.recruitee.com"
  ],
  "maxJobsPerCompany": 10,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call endurable_aqueduct/ats-jobs-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,endurable_aqueduct/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/FeHFab1yhBbGx0Ouv/builds/qLpa8TDs3JeXFSx0M/openapi.json
