# Job Scraper | Greenhouse, Lever, Ashby & More (`peerless_columbine/multi-ats-jobs-scraper`) Actor

Scrape jobs from Greenhouse, Lever, Ashby, Recruitee, SmartRecruiters and Personio. Export descriptions and locations, track new or closed postings, and generate company hiring reports.

- **URL**: https://apify.com/peerless\_columbine/multi-ats-jobs-scraper.md
- **Developed by:** [tingyou333 zhuang](https://apify.com/peerless_columbine) (community)
- **Categories:** Jobs, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.70 / 1,000 delivered jobs

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
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?

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

## Job Scraper | Greenhouse, Lever, Ashby & More

Scrape jobs from Greenhouse, Lever, Ashby, Recruitee, SmartRecruiters and Personio. Export descriptions and locations, track new or closed postings, and generate company hiring reports.

**Useful for:** Monitor selected company career boards; build a job feed; compare hiring activity across ATS providers.

**Why choose this Actor:** Use one input format for six ATS providers. Collect jobs or company hiring reports, with persistent change tracking and source coverage for repeat runs.

### Try a small sample

1. Click **Try for free**, then open **Input** and switch to JSON.
2. Paste the example below and click **Start**. It uses a small result limit.
3. Open the dataset to inspect results, then export JSON, CSV or Excel. Source restrictions can still cause partial or failed runs.

```json
{
  "companies": [
    {
      "ats": "greenhouse",
      "company": "stripe"
    }
  ],
  "maxJobsPerCompany": 5,
  "includeDescriptions": true,
  "outputProfile": "full"
}
```

### Cost at a glance

Primary billing unit: **Delivered job**. Rates below are per **1,000** primary events.

| Free plan | Starter / Bronze | Scale / Silver | Business / Gold and higher |
|---:|---:|---:|---:|
| $1 | $0.9 | $0.8 | $0.7 |

Platform compute, proxy, transfer and storage are additional. Other event types, where enabled, are listed in the **Pricing** tab. “Try for free” uses available account credits; it does not make usage unlimited or unmetered.

**Coverage to know:** Use an explicit provider and company slug for your first run. Closed-job detection requires complete successful snapshots; it is not a full-market jobs database.

For Workday career sites, see our [companion Actor](https://apify.com/peerless_columbine/workday-public-jobs-data?utm_source=actor_readme\&utm_medium=referral\&utm_campaign=storefront_round23). It runs and bills separately.

### Guides and full reference

Collect public Greenhouse, Lever, Ashby, Recruitee, SmartRecruiters and Personio job feeds, or produce one hiring report per company. Supports scheduled monitoring with explicit source coverage and delivery reports.

Omit `ats` to probe all six providers. Empty auto-detected feeds do not establish company identity. The verified Recruitee `tellent` board maps to `careers.tellent.com` regardless of slug letter case; supplied company labels and global-ID casing are preserved for compatibility. Use consistent slug spelling across scheduled runs and avoid submitting case variants as separate targets; arbitrary custom hosts are not supported yet. Personio supports `.de` and `.com` XML feeds. Source failure is reported in `OUTPUT.failedTargets`, not represented as a successful empty board.

### Inputs and output

`titleKeyword` and `locationKeyword` apply case-insensitive substring filters before `maxJobsPerCompany`. `remoteOnly` includes only jobs classified as fully remote; hybrid does not qualify. Zero `maxJobsPerCompany` removes the output cap. Disabling `includeDescriptions` removes both HTML and plain text; raw source blobs are not exposed through an alternate field. `outputProfile` accepts `full`, `compact` (no HTML), or `minimal` (core fields).

The four explicit `presetLists` are:

- `top-tech`: Stripe, Spotify, OpenAI.
- `ai-ml`: Anthropic, OpenAI.
- `devtools`: Postman.
- `fintech`: Stripe, Ramp.

These starter bundles are not exhaustive rankings and may differ from other Actors' memberships. Combine them with explicit companies; duplicate ATS/company pairs merge. Pin explicit companies if you need a fixed target universe.

### Persistent changes

`onlyNewSinceLastRun` compares complete successful source snapshots. New and closed observations carry stable `global_id`, `is_new`, and `is_closed` fields. Pending new jobs survive output caps and refresh from the latest source data before delivery. Failed or incomplete fetches do not close jobs or replace snapshots. State scope separates company/provider/filter combinations.

Stateful runs use a server-managed lease. Overlapping runs fail with `STATE_BUSY`. An aborted or crashed owner can leave its lease held for up to 300 seconds after the last renewal, even when Console already shows a terminal run. The error report recommends `retryAfterSeconds: 300`; if another owner is still running, wait for it as well. Do not delete the lock to force access. A crashed owner's lease expires. Snapshot chunks are immutable and hash-checked; the manifest changes only after every chunk is saved. Old snapshots are readable during migration.

Dataset delivery and snapshot acknowledgement are not a single transaction. A crash between them can replay a delivered event; consumers must deduplicate. Disabled descriptions are not retained in new snapshot records, so historical closed-job descriptions may be unavailable after changing that setting. A write-ahead journal allows the next successful snapshot write to reclaim interrupted chunk uploads. Committed obsolete chunks are deleted after the new manifest commits; failed deletions remain retryable. Historical orphan chunks created before journaling are not automatically enumerated. Dataset delivery still has the replay boundary described above.

### Hiring reports

Set `reportMode: true` for one report per company over the complete filtered snapshot, independent of the job output cap. Reports include role families, departments, location labels, remote share, seniority mix, keyword counts, publication dates and observed changes. `keywords` selects whole-word title matches; `recentWindowDays` defaults to 30.

First observations have unknown growth. Publication-window counts and changes since the prior observed snapshot are separate. The score is `min(100, round(20 * log2(1 + open_roles)))`: a transparent posting-volume index, not a forecast or a reproduction of an undisclosed competitor formula. Region fields group fully remote roles as `remote`, otherwise use explicit country fields or an unambiguous country name in the location text. Unmapped or ambiguous locations are `other`; city/state abbreviations are not guessed as countries. `location_signals` preserves the original location breakdown. These explicit rules do not reproduce an undisclosed competitor classifier. Suggested outreach is draft guidance only; this Actor sends no messages.

### Pricing and current limits

Pay-per-event pricing applies for delivered job records and delivered hiring reports. Each report incurs one event, not one event per summarized job. Per 1,000 events: FREE $1.00; BRONZE $0.90; SILVER $0.80; GOLD/PLATINUM/DIAMOND $0.70. Platform usage is additional and depends on source size, descriptions, retries and state activity. There is no custom start event. Failed targets have no result/report event charge. See the Pricing tab for current rates. Stateful job delivery has passed cloud event-count checks, including a second run that delivers pending jobs without repeating the previous result. These checks validate owner event accounting, not customer revenue or all report billing scenarios.  No total-cost superiority is claimed.

Broader detail coverage and total-cost comparisons remain workload-dependent. Snapshot fault recovery and six-provider pagination samples have bounded evidence; process-level exactly-once delivery is not claimed. Inspect the run output and source coverage for your workload. Do not infer full parity or reliability from successful sample runs.

### Field interpretation

`compensation` preserves structured salary tiers/ranges where published. `salary` contains the published summary or the original currency, bounds and period. No currency conversion or invented salary estimates are applied. Recognized native seniority codes take priority over title patterns; `seniority_evidence` and `source_seniority` explain the choice. Unmapped codes remain available, with title inference as a fallback.

`parse_confidence` is an eight-field structural coverage score: ID, title, URL, location, publication date, department, employment type and recognized remote type. Each contributes equally. `field_coverage` exposes the checks and `parse_confidence_method` names the formula. This is not a calibrated probability, a description-completeness score, or proof that the source's claims are accurate. Optional absent fields can lower the score even when parsing succeeded.

### API example

Set `APIFY_TOKEN` in your own environment and run an asynchronous request:

```bash
curl --request POST \
  "https://api.apify.com/v2/acts/peerless_columbine~multi-ats-jobs-scraper/runs" \
  --header "Authorization: Bearer $APIFY_TOKEN" \
  --header "Content-Type: application/json" \
  --data '{"companies":[{"ats":"ashby","company":"ashby"}],"maxJobsPerCompany":10}'
```

The returned `data.id` identifies the run. Poll `GET /v2/actor-runs/{runId}` until terminal. On success, download `GET /v2/datasets/{defaultDatasetId}/items?format=json`. Also read `GET /v2/key-value-stores/{defaultKeyValueStoreId}/records/OUTPUT` for per-target counts and failures. Partial failures can coexist with successful dataset rows. Use your own Apify account token. Never put a personal token in shared input or public code.

### Verified output example

This excerpt was captured from a successful private cloud run; source jobs can change later. Full mode also returns descriptions and structured compensation where available.

```json
{
  "ats": "recruitee",
  "company": "tellent",
  "title": "Customer Success Manager - DACH",
  "location": "Amsterdam, Noord-Holland, Netherlands",
  "country": "Netherlands",
  "remote_type": "hybrid",
  "seniority": "mid",
  "seniority_evidence": "source",
  "salary": "EUR 55000 - 60000 year",
  "job_id": "2735044",
  "global_id": "recruitee:tellent:2735044",
  "url": "https://careers.tellent.com/o/customer-success-manager-dach-4",
  "posted_at": "2026-09-07T07:42:58+00:00",
  "parse_confidence": 1,
  "parse_confidence_method": "typed_field_coverage_v1; not calibrated accuracy probability"
}
```

In the Dataset tab, select **Jobs**, **Hiring reports**, or **Field coverage** for the corresponding output. A hiring report has no job title or job ID; job rows have no report totals, so choose the view matching the run mode.

Publication dates are normalized to ISO 8601 UTC; `source_posted_at` preserves the original value. Missing dates remain null. Unparseable values carry a warning; timezone-free dates explicitly carry `publication_timezone_assumed_utc`. Recent-window statistics exclude unparseable dates and future dates.

Report snapshots retain country and remote-type fields for stable region comparisons. Older snapshots without these fields cannot prove regional expansion: `region_comparison_available` is false until a new baseline is saved.

### Viewing run reports

In the Output tab, select **Run reports and failed targets** and open or download the `OUTPUT` record from the native file list. It contains acknowledged `rowsSaved`, per-target counts, snapshot status and `failedTargets`. The `summary` run-output link now opens this record list; API clients that need JSON should request `GET /v2/key-value-stores/{defaultKeyValueStoreId}/records/OUTPUT` directly. That record path and the dataset `results` link are unchanged.

A priced job is acknowledged in incremental state only when the SDK accepts its delivery. A job rejected by the event budget stays pending for a later run. Previously delivered jobs are not re-emitted during ordinary continuation; a crash between dataset delivery and state commit can still cause a replay. An interrupted run may lack a final `OUTPUT` record, so also inspect run status and available dataset rows.

### Run limits and measured costs

The default run timeout is 3,600 seconds with 1,024 MB memory, matching the reference Actor's published run defaults. Console/API overrides take precedence. A longer timeout permits large source and detail requests to finish; it does not guarantee success or limit fees.

`maxJobsPerCompany` limits delivered job rows, not source work. Every selected board is collected completely before filtering and state comparison. In particular, SmartRecruiters with descriptions enabled requests one detail per source job, even when the output cap is small. For an initial metadata check, choose one explicit company/provider and disable descriptions.

Measured private samples, modeled at public Starter/Bronze resource tariffs and current event rates:

| Workload | Event fees | Run resources | Marginal total |
|---|---:|---:|---:|
| First stateful run, 1 delivered job | $0.000900 | $0.000881 | $0.001781 |
| Same-scope continuation, 2 delivered jobs | $0.001800 | $0.001364 | $0.003164 |
| One report over 618 source jobs | $0.000900 | $0.000689 | $0.001589 |

These samples used 512 MB explicit test overrides and disabled descriptions. They are not estimates for every default run. Tiny incremental outputs still pay full-snapshot and lease overhead; lower result prices do not establish lower total cost. Figures exclude monthly plan commitment, taxes, post-run exports and time-based retention. They are quantity-based models, not customer invoices. See [Apify resource pricing](https://apify.com/pricing).

Lever salary output uses published plain text when available, strips markup from an HTML-only summary, and otherwise formats the native `salaryRange` currency, bounds and interval. A single bound is labeled “from” or “up to”; absent or invalid ranges stay unknown. The original structured range remains in `compensation`. Lever's published country code is retained for report geography instead of guessing a country from a city. See the [official Lever fields](https://github.com/lever/postings-api).

Geographic change detection versions its interpretation rules. After an upgrade that changes country extraction or region classification, the first successful report records a fresh geography baseline and sets `region_comparison_available: false` with no asserted `new_regions`. Existing job-ID and role-count comparisons still work. A subsequent report using the same rule version can compare regions again. This prevents newly populated country fields from being presented as business expansion.

When ATS auto-detection finds one working provider, other attempted providers may still fail or return an unconfirmed empty feed. `OUTPUT.providerIssues` preserves those attempts even if the overall company has results. `targetHandled` means a provider succeeded or was skipped at the delivery budget; consult `targets` for actual collected counts and limit flags. A 404 for an unused platform is common during auto-detection and is not proof that jobs are missing. Transport/server errors on another provider leave that provider's coverage unverified. `failedTargets` retains its original meaning: company targets with no successful provider. Inspect both arrays instead of treating a successful run as proof that every provider was collected.

All six providers validate unique source job identities before either job delivery
or hiring-report aggregation. Duplicate IDs fail that provider snapshot instead of
inflating report counts or silently choosing a conflicting record. Failed provider
snapshots do not update their persistent baseline.

### Career-link input convenience

Existing `{ "ats": "greenhouse", "company": "stripe" }` inputs remain unchanged.
Alternatively, supply `boardUrl` inside a company item:

```json
{
  "companies": [
    { "boardUrl": "https://job-boards.greenhouse.io/stripe" },
    { "boardUrl": "https://careers.tellent.com" }
  ],
  "maxJobsPerCompany": 2,
  "includeDescriptions": false
}
```

Recognized hosts are `boards.greenhouse.io`, `job-boards.greenhouse.io`,
`jobs.lever.co`, `jobs.ashbyhq.com`, `jobs.smartrecruiters.com`, company subdomains
of `recruitee.com` and `jobs.personio.de`/`jobs.personio.com`, plus the verified
`careers.tellent.com` mapping. Individual job links on these hosts can also supply
the company slug. SmartRecruiters company spelling is preserved.

This extracts the standard ATS/slug target, not an arbitrary URL fetch. Normal
provider routing applies, including Personio's `.de` feed followed by `.com` on
404\. URL query filters, locale and job-ID path suffixes do not restrict the board
collection; use the Actor's filters for output. Unsupported hosts, credentials,
ports and conflicting explicit ATS/company values are rejected before collection.
Matching URL-derived and existing slug targets merge, retaining the same IDs and
state scope. Case variants follow the existing spelling rules described above.

Report `recommended_outreach` remains an array of suggestion strings. It now considers observed net vacancy growth, newly observed departments/regions/role families, management and executive vacancies, and the top three role concentrations. First observations never assert growth; geography changes require a comparable report baseline. Empty boards produce no suggestions. The additional `outreach_signals` array links each suggestion to its count, category, or title examples. These are hiring observations and suggestions, not evidence of company revenue, allocated budgets, purchasing authority, or verified expansion of the business. Nothing is sent to contacts. Competitors already offer trend-based suggestions; the structured evidence is our inspectable implementation, not a claim of exclusive functionality.

### Output field contract

The dataset schema documents 50 job and report fields, including nullable source values, numeric ranges and array item types. Only `ats` and `company` are required across both record families. Job/report-specific fields are optional in the shared schema: absence is expected when a mode does not return them. New fields remain allowed for compatible extension. The schema documents types; it is not proof of source completeness or an automatic runtime rejection mechanism.

| Record or setting | Fields to expect |
|---|---|
| Job identity | `ats`, `company`, `global_id`, `title`, `url`; full/compact also include source `job_id` |
| Optional source data | `salary`, `posted_at`, `department`, `country` and other source fields can be null when unknown |
| Minimal jobs | Core identity, location, remote flag, seniority and date; delta flags when applicable |
| Compact jobs | Full job fields except `description_html` |
| Descriptions disabled | Neither description field is returned, including through raw source data |
| Delta jobs | Boolean `is_new` and `is_closed`; a closed observation retains available prior values |
| Hiring report | `record_type: hiring_signal_report`, counts, score method, breakdowns and observed-change objects; no job identity fields |
| Report suggestions | `recommended_outreach` strings and matching `outreach_signals` objects with structured evidence |

Remote share and structural field coverage are numbers from 0 to 1. Vacancy counts are nonnegative integers. Unknown first-observation changes remain null inside the corresponding report objects. Native compensation structures vary by provider and are preserved rather than forced into an invented common salary format. Use the Input/Output documentation and select the dataset view for the run mode; a job table will not show report-only fields.

Title-based seniority is an inference, not a verified reporting hierarchy. In particular, account, customer-success, product, project, program, engagement, partner/partnership, business-development, community and campaign “manager” titles alone do not establish people-management responsibility. Such titles retain another explicit level marker (for example, Senior) or unknown seniority, with `people_management_not_established_by_title` in `warnings`. Recognized native ATS levels still take priority. Other management/executive title patterns also remain inferences; report leadership counts describe classified postings, not a verified count of hiring managers or budget owners. This corrects the previous broad Manager-title classification; historical stored job observations are not rewritten.

### First-run delta behavior

With `onlyNewSinceLastRun: true`, `firstRunMode: "emit"` preserves the existing default: current jobs are initially new, and undelivered jobs remain pending across result limits. Choose `firstRunMode: "snapshot"` to collect and persist the first complete baseline without emitting or charging initial job results. Subsequent runs emit genuinely new and closed observations, including changes after an initially empty board. Source collection, storage and platform usage are still charged; zero result events does not mean a free run.

Snapshot mode has its own persistent state scope. Switching an existing emit-mode task to snapshot mode creates a fresh baseline; it does not overwrite or import the old task's pending events. Return to emit mode to resume that original scope. This setting is ignored in report mode and ordinary non-delta job runs. Failed/incomplete sources do not establish a baseline. `OUTPUT.targets[].initialSnapshot` indicates a successful snapshot-only initialization.

The reference Actor describes its first delta run as taking a snapshot. This explicit option supports that workflow; paid execution of the competitor has not established whether it also emits initial rows, so exact first-run runtime equivalence is not claimed.

# Actor input Schema

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

ATS and company slug, or omit ats to probe all six platforms. Optional when presetLists is set. Auto-detection does not treat an empty feed as identity confirmation. Alternatively use {"boardUrl":"https://jobs.ashbyhq.com/ramp"} to extract a known ATS and slug. Conflicting explicit ats/company values are rejected; matching URL and slug targets merge.

## `titleKeyword` (type: `string`):

Keep only jobs whose title contains this substring (case-insensitive). Empty = no filter.

## `locationKeyword` (type: `string`):

Keep only jobs whose location contains this substring (case-insensitive). Empty = no filter.

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

Keep only postings detected as fully remote.

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

Limit delivered job records after filtering; 0 means unlimited. The complete source is still fetched, so this does not cap compute, source requests or snapshot storage. Report mode ignores this job-output limit.

## `includeDescriptions` (type: `boolean`):

Return full job description (plain text + HTML). Turn off for smaller, faster output when you only need metadata. For SmartRecruiters this adds one detail request per job.

## `onlyNewSinceLastRun` (type: `boolean`):

Emit new and closed observations from complete successful snapshots; undelivered new jobs remain pending after output limits. Failed sources do not replace state. A server lease rejects overlapping stateful runs with STATE\_BUSY; retry after the prior run finishes. A crash between delivery and state commit can replay a result; deduplicate by global\_id and event state.

## `outputProfile` (type: `string`):

full = all fields incl. HTML description. compact = drops description\_html. minimal = core fields only (ats, company, title, location, remote, seniority, url, posted\_at, global\_id).

## `reportMode` (type: `boolean`):

Emit one hiring report per company over the complete filtered source, independent of maxJobsPerCompany. One delivered report incurs one report event; platform resources are additional. First-observation growth is unknown. The documented volume score is our heuristic, not a competitor formula.

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

Report mode only. Terms to score per company (e.g. AI, security, platform, sales, enterprise). Each is counted against job titles and surfaced as keyword\_hits.

## `recentWindowDays` (type: `integer`):

Report mode only. Window used to frame hiring velocity. Pair with a recurring schedule + the persisted report state for true new-roles-since-last-run velocity and expansion deltas.

## `presetLists` (type: `array`):

Verified starter bundles: top-tech (Stripe, Spotify, OpenAI); ai-ml (Anthropic, OpenAI); devtools (Postman); fintech (Stripe, Ramp). Combine with companies; duplicate ATS/company targets merged. These small bundles are not exhaustive rankings. Availability may change and failed sources are reported.

## `firstRunMode` (type: `string`):

Used only with onlyNewSinceLastRun in job mode. snapshot collects the complete source and persists a baseline without emitting initial jobs; later runs emit new/closed jobs. Platform resources still cost money. This mode has a separate state scope: switching from emit establishes a fresh baseline. Existing emit-mode state remains unchanged. Ignored in report mode.

## Actor input object example

```json
{
  "companies": [
    {
      "ats": "greenhouse",
      "company": "stripe"
    },
    {
      "ats": "lever",
      "company": "spotify"
    },
    {
      "ats": "ashby",
      "company": "ramp"
    }
  ],
  "remoteOnly": false,
  "maxJobsPerCompany": 50,
  "includeDescriptions": true,
  "onlyNewSinceLastRun": false,
  "outputProfile": "full",
  "reportMode": false,
  "keywords": [
    "AI",
    "sales",
    "security"
  ],
  "recentWindowDays": 30,
  "presetLists": [],
  "firstRunMode": "emit"
}
```

# Actor output Schema

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

No description

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

No description

# 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": [
        {
            "ats": "greenhouse",
            "company": "stripe"
        },
        {
            "ats": "lever",
            "company": "spotify"
        },
        {
            "ats": "ashby",
            "company": "ramp"
        }
    ],
    "keywords": [
        "AI",
        "sales",
        "security"
    ],
    "presetLists": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("peerless_columbine/multi-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": [
        {
            "ats": "greenhouse",
            "company": "stripe",
        },
        {
            "ats": "lever",
            "company": "spotify",
        },
        {
            "ats": "ashby",
            "company": "ramp",
        },
    ],
    "keywords": [
        "AI",
        "sales",
        "security",
    ],
    "presetLists": [],
}

# Run the Actor and wait for it to finish
run = client.actor("peerless_columbine/multi-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": [
    {
      "ats": "greenhouse",
      "company": "stripe"
    },
    {
      "ats": "lever",
      "company": "spotify"
    },
    {
      "ats": "ashby",
      "company": "ramp"
    }
  ],
  "keywords": [
    "AI",
    "sales",
    "security"
  ],
  "presetLists": []
}' |
apify call peerless_columbine/multi-ats-jobs-scraper --silent --output-dataset

```

## MCP server setup

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