# Career Site Jobs Scraper: Greenhouse, Lever, Workday + 7 ATS (`nat-labs/ats-jobs-scraper`) Actor

Live jobs from company career sites on Greenhouse, Lever, Ashby, Workable, Workday, Recruitee, Personio, Teamtailor, BambooHR and Breezy. Paste URLs, domains or names; ATS auto-detected. One clean schema, salaries, filters that work, new/closed job alerts. No proxy, no login.

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

## Pricing

from $0.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.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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 Scraper: Greenhouse, Lever, Workday + 7 ATS

Get every open job from the companies you choose, **live from their own career sites**, as clean JSON, CSV or Excel. Works with **Greenhouse, Lever, Ashby, Workable, Workday, Recruitee, Personio, Teamtailor, BambooHR and Breezy HR**. Paste job-board links, websites or just company names: the job board is detected for you.

One schema for all of them: title, company, city / region / country, remote or hybrid, department, employment type, **salary range** when published, posted date, description, apply link. Filters run **before** billing, and a scheduled run can return **only new, changed and closed jobs**.

**$0.80 per 1,000 jobs. No start fee, no proxy cost, no compute bill on top. Not-found companies, failed boards and closed-job alerts are free.**

### Why this career-site jobs scraper

| | This Actor | Job-database actors (e.g. fantastic-jobs career-site API) | Other live ATS scrapers |
|---|---|---|---|
| Data | **Live** from the company's ATS at run time | Pre-indexed database, 1–3 h delay, only companies they crawl | Live |
| Companies | **Any company** on 10 ATS: URL, `ats:slug`, website or name | Search by keyword across their index | Usually URL or slug only |
| Price | **$0.80 / 1,000 jobs**, no start fee | $12 / 1,000 + $0.01 start (FREE tier) | $1.15–4 / 1,000, often + start fee |
| Filters | Title, keywords, location (country / state / city / remote / EMEA), workplace, employment type, department, posted-since, has-salary — **applied before billing, unit-tested** | Open issues: "Filters are no longer working", "identical static results regardless of input" | Title and location text at most |
| Location | Parsed into city, region, ISO country for every location of a job | Derived by AI | Raw text |
| Salary | ATS salary field (Ashby, Greenhouse, Lever, Recruitee) **plus** pay ranges written in the description, with `salary.source` | AI-extracted | Rarely |
| New / closed jobs | **Built in, closed alerts free** | Expired jobs are a separate $20/month actor | Some |
| Hiring signals | One row per company: open jobs, posted in 7 / 30 days, remote share, top departments and countries | LinkedIn enrichment | Rarely |
| Duplicates | Deduplicated in-run before billing; optional collapse of re-posts | "Duplicates" complaints | Varies |
| Failures | Per-company status row (`not-found`, `not-supported`, `robots-disallowed`, `error`); the run fails loudly if every board errored | — | Silent empties |

### What you can do

- **Recruiters and sourcers**: watch a list of target companies and get only the new roles each morning.
- **Sales and RevOps**: use hiring as a buying signal. The company row tells you who is hiring engineers, how fast, and where.
- **Job boards and newsletters**: pull fresh jobs from employers' own boards with apply links, and expire the ones that close.
- **Market research**: salaries, remote share and team growth across a peer group.
- **AI agents**: call it from Claude, ChatGPT or Cursor through Apify's AI integrations; inputs like `"remote"`, `"Germany"`, `"7d"`, `"full time"` just work.
- **Your own stack**: run it from the Apify API, the Python or JavaScript client, Make, Zapier, n8n, Google Sheets, Slack, webhooks or a schedule; export JSON, CSV or Excel.

### Quick start

#### 1. All jobs from a few companies

```json
{ "companies": ["https://boards.greenhouse.io/stripe", "lever:palantir", "jobs.ashbyhq.com/ashby"] }
```

#### 2. Mix of URLs, websites and names, with filters

```json
{
    "companies": ["notion.so", "Hugging Face", "https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite", "bunq.recruitee.com"],
    "titleKeywords": ["engineer", "developer"],
    "titleExcludes": ["intern"],
    "locations": ["Germany", "Netherlands", "remote"],
    "postedWithin": "30d"
}
```

#### 3. Remote jobs with a published salary

```json
{ "companies": ["ashby:ashby", "greenhouse:gitlab"], "workplaceTypes": ["remote"], "hasSalary": true, "descriptionFormat": "none" }
```

#### 4. Daily monitoring: only new, changed and closed jobs

```json
{
    "companies": ["greenhouse:stripe", "lever:palantir", "ashby:openai"],
    "titleKeywords": ["engineer"],
    "onlyNew": true,
    "monitorName": "eng-watch",
    "baselineMode": "silent"
}
```

Save it as a task, schedule it daily and connect Slack, email, Google Sheets or a webhook. Each run returns new jobs (`changeType: "new"`), jobs whose title, location, type or salary changed (`"changed"`), and a free `"closed"` row for every job that disappeared. `baselineMode: "silent"` makes the first run only remember what is open now instead of delivering it.

#### Company input formats

| You paste | What happens |
|---|---|
| `https://boards.greenhouse.io/stripe`, `job-boards.greenhouse.io/embed/job_board?for=stripe` | Greenhouse board `stripe` |
| `jobs.lever.co/palantir`, `jobs.eu.lever.co/acme` | Lever (US or EU instance) |
| `jobs.ashbyhq.com/openai` | Ashby |
| `apply.workable.com/huggingface` | Workable |
| `https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite` (any job link works too) | Workday tenant + site |
| `bunq.recruitee.com`, `acme.jobs.personio.de`, `acme.teamtailor.com`, `acme.bamboohr.com`, `acme.breezy.hr` | that ATS |
| `greenhouse:stripe`, `lever:palantir`, `ashby:openai`, `gh:airbnb` | that ATS and slug, no detection needed |
| `notion.so`, `https://acme.com/careers` | The page (and `/careers`, `/jobs`, `careers.` / `jobs.` subdomains) is scanned for a job-board link |
| `Hugging Face` | Slug guesses (`huggingface`, `hugging-face`) are tried on every list-based ATS; the board with the most jobs wins and the others are listed in `alternatives` |

A name guess can hit a different company with the same slug; the company row shows `detectedBy: "name-guess"` so you can check, and a URL is always exact.

### Output

#### Job row

```json
{
    "type": "job",
    "id": "ashby:ashby:390e266b-4b6c-4490-ad74-05ff5e0bb36a",
    "jobId": "390e266b-4b6c-4490-ad74-05ff5e0bb36a",
    "title": "Product Manager, Onboarding and Growth",
    "company": "Ashby",
    "companySlug": "ashby",
    "ats": "ashby",
    "url": "https://jobs.ashbyhq.com/ashby/390e266b-4b6c-4490-ad74-05ff5e0bb36a",
    "applyUrl": "https://jobs.ashbyhq.com/ashby/390e266b-4b6c-4490-ad74-05ff5e0bb36a/application",
    "location": "Remote - US; Remote - Canada",
    "city": null, "region": null, "regionCode": null,
    "country": "United States", "countryCode": "US", "countryCodes": ["US", "CA"],
    "locations": [
        { "raw": "Remote - US", "city": null, "region": null, "regionCode": null, "country": "United States", "countryCode": "US", "macroRegion": null, "isRemote": true },
        { "raw": "Remote - Canada", "city": null, "region": null, "regionCode": null, "country": "Canada", "countryCode": "CA", "macroRegion": null, "isRemote": true }
    ],
    "workplaceType": "remote",
    "isRemote": true,
    "department": "Product",
    "team": "Product",
    "employmentType": "full-time",
    "employmentTypeRaw": "FullTime",
    "seniority": null,
    "salary": { "min": 180000, "max": 260000, "currency": "USD", "period": "year", "text": "$180K - $260K", "source": "ats" },
    "postedAt": "2026-10-16T00:42:17.137Z",
    "postedAtApproximate": false,
    "updatedAt": null,
    "closesAt": null,
    "requisitionId": null,
    "descriptionText": "…",
    "boardUrl": "https://jobs.ashbyhq.com/ashby",
    "input": "jobs.ashbyhq.com/ashby",
    "scrapedAt": "2026-10-17T12:00:00.000Z"
}
```

`employmentType` is one of `full-time`, `part-time`, `contract`, `internship`, `temporary`, `apprenticeship`, `volunteer`, `other` (the ATS's own wording stays in `employmentTypeRaw`). `workplaceType` is `remote`, `hybrid`, `onsite` or `null` when the board does not say. `salary.source` is `ats` (the ATS salary field) or `description` (a pay range written in the text, e.g. US pay-transparency ranges). In monitoring mode rows also carry `changeType` (`new` / `changed`) and `firstSeenAt`.

#### Closed-job row (monitoring, free)

```json
{ "type": "job", "changeType": "closed", "id": "greenhouse:stripe:8172487", "title": "Abuse Investigator", "url": "https://stripe.com/jobs/search?gh_jid=8172487", "company": "Stripe", "ats": "greenhouse", "firstSeenAt": "2026-10-10T06:00:00.000Z", "closedDetectedAt": "2026-10-17T06:00:00.000Z" }
```

#### Company row (free)

```json
{
    "type": "company",
    "input": "Hugging Face",
    "company": "Hugging Face",
    "ats": "workable",
    "slug": "huggingface",
    "boardUrl": "https://apply.workable.com/huggingface/",
    "detectedBy": "name-guess",
    "alternatives": [{ "ats": "workable", "slug": "hugging-face", "jobs": 0 }],
    "status": "ok",
    "totalJobs": 6, "matchedJobs": 5, "deliveredJobs": 5, "newJobs": 0, "changedJobs": 0, "closedJobs": 0,
    "listingComplete": true,
    "signals": {
        "openJobs": 6, "postedLast7Days": 0, "postedLast30Days": 2, "remoteJobs": 6, "hybridJobs": 0, "jobsWithSalary": 0,
        "topDepartments": [{ "name": "Open Source", "count": 2 }, { "name": "Product", "count": 2 }],
        "topCountries": [{ "name": "FR", "count": 3 }, { "name": "US", "count": 3 }]
    }
}
```

`status`: `ok`, `empty` (board exists, no open jobs), `not-found`, `no-board` (nothing detected for a name or website), `not-supported`, `robots-disallowed`, `invalid-input`, or an error code. Use the **Jobs** and **Companies** views in the Output tab.

### Pricing

Pay per event. **Platform usage is included**: no start fee, no proxy or compute bill on top.

| Event | What it is | Price |
|---|---|---|
| Job | One job row delivered (after filters and dedup) | **$0.80 / 1,000** ($0.0008) |
| Monitor check | Monitoring mode only: a company checked that had nothing new or changed | $1.00 / 1,000 ($0.001) |

Lower job rates apply automatically on higher Apify plans (down to $0.50 / 1,000); the monitor check is $0.001 on every plan. **Never charged:** company status rows, closed-job rows, companies that are not found, unsupported or failed, jobs removed by your filters, and duplicates.

Examples: all 731 Stripe jobs = **$0.58**. 200 companies × 25 matching jobs = 5,000 jobs = **$4.00**. Daily monitoring of 50 companies with ~5 new jobs a day in total ≈ 5 × $0.0008 + 45 × $0.001 ≈ **$0.05 a day**, about $1.50 a month.

The Actor checks your run's **maximum cost** before each company and stops cleanly when it is reached.

**Free Apify plan:** up to 300 jobs from up to 25 companies per run (a limit set by the developer, not by Apify). Any paid plan removes it.

### Good to know

- **You bring the companies.** This is not a search engine over all employers; it reads the boards you name. For a list of employers on an ATS, start from their career pages.
- **Supported ATS:** Greenhouse, Lever, Ashby, Workable, Workday, Recruitee, Personio, Teamtailor, BambooHR, Breezy HR. **Not supported:** SmartRecruiters (its API's robots.txt disallows all crawlers except LinkedIn's), iCIMS, Taleo, Jobvite, SuccessFactors, JazzHR. These inputs get a free `not-supported` row.
- **Workday:** one search returns at most 2,000 jobs, so very large tenants are capped there; title keywords are sent as the site search, which narrows big boards. Each delivered Workday job needs one extra request for its description and exact date, so Workday is slower (about 1 s per 20 listed jobs). In monitoring mode, already-known Workday jobs are not re-fetched, so their changes are not detected (new and closed jobs are).
- **Breezy HR** gives no descriptions in its public feed; `descriptionText` stays empty for Breezy.
- **Dates:** Workday list pages only say "Posted 3 Days Ago"; the exact date comes from the detail call. Jobs whose board gives no date are dropped when you set a posted-since filter.
- **Closed jobs** are only reported when the whole board was listed (not a keyword-narrowed Workday search), so a filter never fakes a closure.
- **Two-letter codes** in Locations are countries (`CA` = Canada, `IN` = India). Write `California` / `US-CA` for US states.
- **Proxies** are not needed and not used by default. robots.txt is read for every host and honoured, including Crawl-delay.

### Is it legal to scrape company career sites?

This Actor reads public job postings through the public job-board endpoints that the ATS vendors provide so employers can embed and syndicate their jobs (Greenhouse Job Board API, Lever Postings API, Ashby posting API, Workable widget, Recruitee careers API, Personio XML feed, Teamtailor RSS, BambooHR and Breezy career-site JSON, Workday career-site JSON). It does not log in, uses no accounts or API keys, respects each host's robots.txt (which is why SmartRecruiters is excluded), paces requests per host and does not collect personal data: it never extracts emails or phone numbers, and drops the application mailbox address Recruitee includes. Job descriptions are the employers' copyrighted text; use them for search, analysis and linking back, and check with a lawyer before republishing them in full.

### FAQ

**How is this different from the Career Site Job Listing API?** That actor queries a pre-built database by keyword. This one reads the employers you name live, so new jobs appear immediately, any company on a supported ATS works, and the price is about 15× lower. If you need to discover employers by keyword, use a database; if you know your target companies, use this.

**Can I paste my old input?** The main filters of fantastic-jobs/career-site-job-listing-api work under their own names: `titleSearch`, `titleExclusionSearch`, `locationSearch`, `locationExclusionSearch`, `descriptionSearch`, `descriptionExclusionSearch`, `organizationSearch` and `domainFilter` (used as the companies to read), `aiWorkArrangementFilter`, `aiEmploymentTypeFilter`, `hasSalary`, `datePostedAfter`, `descriptionType` and `limit`.

| Their field | Our field |
|---|---|
| `title`, `organization`, `url` | `title`, `company`, `url` |
| `date_posted`, `date_modified`, `date_valid_through` | `postedAt`, `updatedAt`, `closesAt` |
| `locations_derived`, `cities_derived`, `countries_derived` | `locations[]`, `city`, `countryCodes` |
| `employment_type`, `ai_work_arrangement` | `employmentType`, `workplaceType` |
| `ai_salary_min_value` / `max` / `currency` / `unit_text` | `salary.min` / `max` / `currency` / `period` |
| `description_text`, `description_html`, `source` | `descriptionText`, `descriptionHtml`, `ats` |

**Can I use it from Claude, ChatGPT or Cursor?** Yes, through Apify's AI integrations.

**A company I need is missing or returned `no-board`.** Paste the career-page URL instead of the name. If its ATS is not supported, open an issue with the URL; new ATS are added based on requests.

**What happens when an ATS changes something?** Every company gets a status row and the run fails loudly when every board errored, so breakages are visible and fixed fast. Issues are answered within 24 hours.

# Actor input Schema

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

One per line. Any of: a job-board link (<code>https://boards.greenhouse.io/stripe</code>, <code>jobs.lever.co/palantir</code>, <code>jobs.ashbyhq.com/ashby</code>, <code>apply.workable.com/huggingface</code>, <code>https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite</code>, <code>bunq.recruitee.com</code>, <code>acme.jobs.personio.de</code>, <code>acme.teamtailor.com</code>, <code>acme.bamboohr.com</code>, <code>acme.breezy.hr</code>), <code>ats:slug</code> (<code>greenhouse:stripe</code>, <code>lever:palantir</code>), a website or careers page (<code>notion.so</code>, <code>https://acme.com/careers</code>) which is scanned for its job-board link, or a company name (<code>Hugging Face</code>) which is tried on every supported ATS.

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

Keep jobs whose title contains any of these words (case-insensitive, matches word starts: <code>engineer</code> matches <i>Engineering</i>). Words of 3 letters or less (<code>ai</code>, <code>qa</code>) must match a whole word. On Workday these are also sent as the site search, which keeps big boards fast.

## `titleExcludes` (type: `array`):

Drop jobs whose title contains any of these words, e.g. <code>senior</code>, <code>intern</code>.

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

Keep jobs mentioning any of these words anywhere: title, department, team or full description, e.g. <code>python</code>, <code>kubernetes</code>.

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

Drop jobs mentioning any of these words in title, department or description.

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

Countries (<code>US</code>, <code>de</code>, <code>Germany</code>, <code>UK</code>), US states / Canadian provinces (<code>California</code>, <code>TX</code>, <code>US-NY</code>, <code>Ontario</code>), regions (<code>EMEA</code>, <code>Europe</code>, <code>APAC</code>), cities (<code>London</code>, <code>Berlin</code>) or <code>remote</code>. Matches every location of a multi-location job. Note: a bare two-letter code is a country (<code>CA</code> = Canada); write <code>California</code> or <code>US-CA</code> for the state.

## `locationExcludes` (type: `array`):

Drop jobs with any location matching these terms (same syntax as Locations).

## `workplaceTypes` (type: `array`):

Keep only these: <code>remote</code>, <code>hybrid</code>, <code>onsite</code>. Jobs whose board says nothing about it are dropped when this filter is set.

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

Keep only these types (lenient: <code>FULL_TIME</code>, <code>Full-time</code>, <code>intern</code> all work).

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

Keep jobs whose department or team contains any of these words, e.g. <code>engineering</code>, <code>sales</code>.

## `postedWithin` (type: `string`):

Only jobs first published in this window: <code>24h</code>, <code>7d</code>, <code>2w</code>, <code>1m</code> or a number of days. Jobs whose board gives no date are dropped when this is set (Breezy and Lever always give one).

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

Alternative to <b>Posted within</b>: a date like <code>2026-10-01</code>.

## `hasSalary` (type: `boolean`):

Keep only jobs with a salary range, from the ATS's salary field or a pay range written in the description.

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

For scheduled runs: deliver only jobs that are new (and, optionally, changed) since the previous run with the same <b>Monitor name</b>, plus a free <code>closed</code> row for each job that disappeared from the board.

## `monitorName` (type: `string`):

Separate memory per schedule, e.g. <code>fintech-engineering</code>. Runs with the same name share what has been seen.

## `includeChanged` (type: `boolean`):

Deliver a job again (<code>changeType: changed</code>) when its title, location, department, type, workplace or salary changes.

## `includeClosed` (type: `boolean`):

Add a <code>changeType: closed</code> row for each previously delivered job that is no longer on the board. Never charged.

## `baselineMode` (type: `string`):

<code>deliver</code>: the first run returns every matching job (charged as jobs). <code>silent</code>: the first run only records what is open now (one monitor check per company) and later runs deliver what is new.

## `descriptionFormat` (type: `string`):

Job description as plain text, original HTML, both, or none (smaller datasets).

## `includeCompanyRows` (type: `boolean`):

A free <code>type: company</code> row per input: which ATS and board was found, status (ok / not-found / not-supported / error), open, matched and delivered jobs, and hiring signals (jobs posted in the last 7 and 30 days, remote share, top departments and countries).

## `collapseDuplicates` (type: `boolean`):

Some employers post the same job several times. When on, a job with the same company, title and location as one already delivered in this run is skipped (and not charged). Identical job ids are always deduplicated.

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

Newest jobs first. 0 = no limit. The form starts at 20 so a first try stays cheap; raise it for full boards (API calls without this field get 1000).

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

Stop after this many jobs across all companies. 0 = no limit. The run also stops cleanly at your run's maximum cost.

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

How many companies are processed at once. Requests to one host are always paced and honour robots.txt Crawl-delay.

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

Not needed: these are public job-board APIs and work without a proxy. Only set one if a Workday tenant rate-limits you.

## Actor input object example

```json
{
  "companies": [
    "https://boards.greenhouse.io/stripe",
    "lever:palantir",
    "jobs.ashbyhq.com/ashby"
  ],
  "hasSalary": false,
  "onlyNew": false,
  "monitorName": "default",
  "includeChanged": true,
  "includeClosed": true,
  "baselineMode": "deliver",
  "descriptionFormat": "text",
  "includeCompanyRows": true,
  "collapseDuplicates": false,
  "maxJobsPerCompany": 20,
  "maxItems": 0,
  "maxConcurrency": 5,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

Job rows (type=job) and company status / hiring-signal rows (type=company).

## `statistics` (type: `string`):

Per-run counters, companies that failed with reasons, requests and bytes.

# 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/stripe",
        "lever:palantir",
        "jobs.ashbyhq.com/ashby"
    ],
    "maxJobsPerCompany": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("nat-labs/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/stripe",
        "lever:palantir",
        "jobs.ashbyhq.com/ashby",
    ],
    "maxJobsPerCompany": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("nat-labs/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/stripe",
    "lever:palantir",
    "jobs.ashbyhq.com/ashby"
  ],
  "maxJobsPerCompany": 20
}' |
apify call nat-labs/ats-jobs-scraper --silent --output-dataset

```

## MCP server setup

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