# Career Site Jobs Feed (`plusonets/career-site-jobs-feed`) Actor

Get jobs from company career sites on Greenhouse, Lever, Ashby, SmartRecruiters, Workable and more in one clean format. Filters, salary parsing, new/removed job tracking.

- **URL**: https://apify.com/plusonets/career-site-jobs-feed.md
- **Developed by:** [Olalekan Johnson](https://apify.com/plusonets) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $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

## Career Site Jobs Feed

Normalized job listings from company career sites that run on public ATS job-board APIs. One schema for Greenhouse, Lever, Ashby, SmartRecruiters, Workable, Recruitee, Personio, BambooHR, and Breezy.

The Actor calls the official public, keyless JSON or XML endpoints those boards already expose to their own career pages. It does not log in, scrape private pages, or use a proxy.

### Use cases

- Recruiting research across a named list of companies, with the same fields no matter which ATS hosts the board.
- A scheduled feed of new and removed jobs (`changesOnly`) for a watchlist.
- Lead lists filtered by keyword, location, remote, department, or recency.
- Salary coverage when the ATS publishes a range, or when the posting text contains one.

### Supported boards

| ATS | How to address it | Public endpoint the Actor calls |
| --- | --- | --- |
| Greenhouse | `greenhouse:dropbox` or `gh:dropbox` | `GET https://boards-api.greenhouse.io/v1/boards/{token}/jobs?content=true` |
| Lever | `lever:spotify` | `GET https://api.lever.co/v0/postings/{slug}?mode=json` |
| Ashby | `ashby:ramp` | `GET https://api.ashbyhq.com/posting-api/job-board/{slug}?includeCompensation=true` |
| SmartRecruiters | `smartrecruiters:ServiceNow` or `sr:ServiceNow` | `GET https://api.smartrecruiters.com/v1/companies/{id}/postings` and one detail request per returned job |
| Workable | `workable:huggingface` | `GET https://apply.workable.com/api/v1/widget/accounts/{slug}?details=true` |
| Recruitee | `recruitee:helloprint` | `GET https://{slug}.recruitee.com/api/offers/` |
| Personio | `personio:personio` | `GET https://{slug}.jobs.personio.de/xml` |
| BambooHR | `bamboohr:flyio` or `bamboo:flyio` | `GET https://{slug}.bamboohr.com/careers/list` and one detail request per returned job |
| Breezy | `breezy:remote` | `GET https://{slug}.breezy.hr/json` |

A careers-page URL works too. The Actor reads the public page for a known board link (Greenhouse, Lever, Ashby, and the hosts above). If the HTML has no board link, it takes a brand from the hostname and checks the public APIs. `https://boards.greenhouse.io/dropbox` resolves as Greenhouse token `dropbox` without a brand probe.

SmartRecruiters identifiers are case-sensitive. An unknown SmartRecruiters identifier returns HTTP 200 and an empty list, so that company is counted as a success with a note in the run summary. The other boards return HTTP 404 for an unknown token, and that company is recorded as a failed input. One bad company does not fail the run.

### Input

The input schema lives in [`.actor/input_schema.json`](.actor/input_schema.json). The Store's daily automated test uses the prefill: `greenhouse:dropbox`, `maxItems` 5, descriptions on. That pull is one small board and finishes in a few seconds.

```json
{
  "companies": [
    "greenhouse:dropbox",
    "lever:spotify",
    "https://jobs.ashbyhq.com/ramp"
  ],
  "keywords": ["account executive", "engineer"],
  "excludeKeywords": ["intern"],
  "location": "United States",
  "remote": false,
  "department": "Sales",
  "postedWithinDays": 14,
  "changesOnly": false,
  "stateStoreName": "ats-jobs-monitor",
  "includeDescription": true,
  "maxItems": 100
}
```

| Field | What it does |
| --- | --- |
| `companies` | Required. `ats:token`, a board URL, or a company / careers URL. A string with commas or newlines is accepted as well as an array. |
| `keywords` | Keep a job when the title, department, team, location, employment type, or description contains any keyword. Empty means keep every job. |
| `excludeKeywords` | Drop a job that contains any of these. |
| `location` | Case-insensitive substring against locations. |
| `remote` | Keep jobs the ATS marks remote. Hybrid and unknown are dropped. |
| `department` | Substring against department or team. |
| `postedWithinHours` | Drop jobs older than this, and jobs with no posted date. |
| `postedWithinDays` | Used only when hours is empty. One day is 24 hours. |
| `changesOnly` | Return only new and removed jobs since the previous run with the same state store and the same filters. |
| `stateStoreName` | Named key-value store for that snapshot. Default `ats-jobs-monitor`. |
| `includeDescription` | Include description text and HTML. Salary is parsed before the text is dropped. SmartRecruiters and BambooHR skip their detail requests when this is off. |
| `maxItems` | Stop after this many jobs have been written. Default 100. Prefill 5. |

Jobs that filters drop, jobs past `maxItems`, and unchanged jobs in changes-only mode are not written and are not charged.

The first changes-only run treats the current matching jobs as `new`, up to `maxItems`. Jobs that do not fit stay unseen and come back as new on the next run. Removed jobs are reported only after they were actually returned. Changing filters starts a new baseline. State is not written when `changesOnly` is off.

### Output

Every item uses the same fields. The dataset schema is [`.actor/dataset_schema.json`](.actor/dataset_schema.json), with overview, salary, and changes views. The default key-value store record `RUN_SUMMARY` is a per-company report (succeeded, failed, seen, returned, and whether the run stopped on `maxItems` or the charge limit). Dataset items are jobs only. Errors stay in the summary so they are not billed as results.

| Field | Meaning |
| --- | --- |
| `title`, `company`, `companySlug`, `companyWebsite` | Title, display name, board token, and the website URL when you passed one. |
| `ats`, `jobId` | Source and that source's job id. |
| `url`, `applyUrl` | Public posting and application links. |
| `locations` | Location strings from the posting. |
| `remote`, `workplaceType` | `remote` is true only for remote jobs. `workplaceType` is `remote`, `hybrid`, or `onsite`. |
| `department`, `team`, `employmentType` | From the ATS. Greenhouse's public board payload has no employment type, so that field is null there. |
| `postedAt`, `updatedAt`, `firstSeenAt` | UTC ISO-8601 (`Z`). `firstSeenAt` is set in changes-only mode. |
| `descriptionText`, `descriptionHtml` | Plain text and HTML. Null for Breezy, whose public JSON has no description, and null when descriptions are turned off. |
| `salaryMin`, `salaryMax`, `salaryCurrency`, `salaryPeriod`, `salaryText` | Parsed pay. Period is `hour`, `day`, `week`, `month`, or `year`. Null when the posting has no amount. |
| `changeStatus` | `active` on a normal run. `new` or `removed` on a changes-only run. |

Salary prefers structured fields (Ashby compensation, Lever `salaryRange`, Greenhouse metadata, Recruitee, BambooHR, Breezy). Otherwise the first currency range in the description is used. A nearby `CAD`, `AUD`, `NZD`, or `SGD` code replaces a bare `$`. Amounts written as billions or millions, and bare years, are ignored. When a posting has several ranges, the first one is kept.

#### Example

This item is from a local run of `greenhouse:dropbox` on 23 Oct 2026. The description fields below are shortened. The dataset contains the full text and HTML.

```json
{
  "title": "Account Executive",
  "company": "Dropbox",
  "companySlug": "dropbox",
  "companyWebsite": null,
  "ats": "greenhouse",
  "jobId": "8159652",
  "url": "https://jobs.dropbox.com/listing/8159652?gh_jid=8159652",
  "applyUrl": "https://jobs.dropbox.com/listing/8159652?gh_jid=8159652",
  "locations": ["Remote - US: Select locations", "Canada", "US"],
  "remote": true,
  "workplaceType": "remote",
  "department": "Direct Sales - AMER (Sub Team)",
  "team": null,
  "employmentType": null,
  "postedAt": "2026-09-01T13:20:17Z",
  "updatedAt": "2026-09-08T15:46:44Z",
  "firstSeenAt": null,
  "descriptionText": "Role Description\nAs an Account Executive on the Growth team, you will help Dropbox expand how we bring our Product Suite and emerging AI products to new customers.",
  "descriptionHtml": "<h2>Role Description</h2><p>As an Account Executive on the Growth team...</p>",
  "salaryMin": 146500,
  "salaryMax": 198300,
  "salaryCurrency": "USD",
  "salaryPeriod": "year",
  "salaryText": "$146,500-$198,300",
  "changeStatus": "active"
}
```

The same run also returned a Breezy posting for Remote, "REMOTE REAL ESTATE OPERATIONS ASSISTANT", at `$400–$500` per month (`salaryMin` 400, `salaryMax` 500, `salaryCurrency` USD, `salaryPeriod` month). Breezy's public JSON has no description, so those fields were null. A Personio posting on `personio:personio` came back as "Staff Software Engineer, Data Platform" in Product and Tech, on-site in Munich and Berlin.

A run that mixed a missing Greenhouse token with Breezy, Personio, Workable, and `https://boards.greenhouse.io/dropbox` finished with this summary: `Returned 12 jobs from 4/5 companies. 1 company failed and was skipped. Stopped because maxItems was reached.` Exit code 0.

A second changes-only run against `breezy:remote` and `personio:personio`, after the first run had stored them, returned `0 new, 0 removed` and wrote no extra dataset items.

### Pricing

Pay per event. Prices to paste into the Actor's Publication → Monetization settings are in [`.actor/pay_per_event.json`](.actor/pay_per_event.json). Apify does not read that file on push. You set the events in Console.

| Event | Price | When it is charged |
| --- | --- | --- |
| `apify-actor-start` | $0.005 | Once per run, by the platform. The Actor does not call `Actor.charge` for it. |
| `job-result` | $0.0015 | Once per job written to the dataset. $1.50 per 1,000 jobs. |

A run that returns 1,000 jobs costs $1.505. A run that returns nothing costs $0.005.

`job-result` is the primary event. The Actor writes each job with `Actor.push_data(item, charged_event_name="job-result")`. When the user's max charge is reached, the item is not stored and the run stops. Further companies are skipped. The run still exits 0 and the summary sets `chargeLimitReached`.

Before publishing:

1. Choose **Pay per event**. Leave **pay per event + usage** off. This Actor's memory stays at 128–512 MB, so platform compute is the smaller part of the bill. Adding usage pricing on top of events lowers the Store quality score.
2. Create `job-result` at $0.0015 and mark it as the primary event. Set `apify-actor-start` to $0.005.
3. Delete the synthetic event `apify-default-dataset-item`. The SDK also attempts to charge that event on every push. If you leave it priced, users pay twice for each job. After you delete it, the platform price is $0. You may see a log line about an unknown `apify-default-dataset-item` event. That line is harmless when the event is gone.
4. Set the minimum max total charge to about `$0.01` (one start plus one job is $0.0065).
5. Do not add a second custom "actor start" event. `apify-actor-start` is already charged by the platform. Charging a name that starts with `apify-` from code raises an error.
6. Raising the start event above Apify's default does not add free compute time. Memory for this Actor is capped at 512 MB, under the 1 GB step, so the start event is charged once.

Local runs are not on pay-per-event pricing. The log line `Ignored attempt to charge for an event` is expected there, and the jobs are still saved.

Price changes for an Actor that already has paying users take effect after 14 days and can be made once a month. Until there are paying users, a price change applies on the next publish.

### Reliability

- Each request retries up to 4 times on HTTP 429, 500, 502, 503, 504, and on network errors. Backoff is `0.5 * 2^attempt` seconds plus jitter, capped at 15 seconds. A `Retry-After` header of 0–30 seconds is honored.
- Calls to the same host are spaced at least 0.25 seconds apart. There is no proxy and no attempt to look like a browser beyond a stated User-Agent: `CareerSiteJobs/1.0 (+https://apify.com; public ATS job-board client)`.
- A failure while resolving or downloading one company is logged, stored on that company in `RUN_SUMMARY`, and the run continues.
- Detail-request failures (SmartRecruiters, BambooHR) keep the job and leave the description empty.
- The run status message is the same sentence stored on `RUN_SUMMARY.message`.

### FAQ

**Can I pass a company website instead of a board token?**\
Yes. Paste the careers URL or the homepage. A known board URL is the most reliable form. A custom careers site that never links to its ATS, and whose hostname does not match the board's company name, needs an `ats:token`.

**Why did SmartRecruiters return zero jobs with no error?**\
That API returns HTTP 200 and `totalFound: 0` for an identifier it does not know, and also for a real company with no open jobs. The summary note says so. Check the identifier's case against the company's SmartRecruiters URL.

**Are removed jobs charged?**\
Yes, when changes-only mode returns them. They are results. Unchanged jobs are not returned and are not charged.

**Does the remote filter include hybrid jobs?**\
No. Hybrid is `remote: false`. Jobs with an unknown remote flag are excluded when `remote` is true.

**Where is the monitor state?**\
In the named key-value store from `stateStoreName`, one key per board: `board-{ats}-{token}`. The value stores job ids and a hash of the filters. It does not store descriptions.

**What does the daily Store test run?**\
The input prefill: one Greenhouse board (`dropbox`) and at most 5 jobs, with descriptions. A local run of that input returned 5 jobs in about a second.

### Run locally

Python 3.12 or newer. Docker is only required for `apify run` / `apify push`.

```bash
python3 -m pip install -r requirements.txt -r requirements-dev.txt
mkdir -p /tmp/career-site-jobs/key_value_stores/default
cat > /tmp/career-site-jobs/key_value_stores/default/INPUT.json << 'EOF'
{
  "companies": ["greenhouse:dropbox", "breezy:remote", "personio:personio"],
  "maxItems": 10,
  "includeDescription": true
}
EOF
APIFY_LOCAL_STORAGE_DIR=/tmp/career-site-jobs python3 -m src.main
```

Jobs are written to `/tmp/career-site-jobs/datasets/default/`. The summary is the key-value record `RUN_SUMMARY`.

With the Apify CLI installed, `apify run` builds the Dockerfile and reads `storage/key_value_stores/default/INPUT.json` in the project directory.

```bash
python3 -m pytest -m "not integration"
python3 -m pytest tests/test_integration.py
```

The integration test calls the live public boards (Dropbox, Spotify, Ramp, ServiceNow, Hugging Face, Helloprint, Personio, Fly.io, and Remote). Set `ATS_SKIP_LIVE=1` to skip it.

### Deploy

The account owner publishes this. Nothing in this repo pushes to Apify or stores a token.

1. Install the CLI: `npm install -g apify-cli`, then `apify login`.
2. From this directory, `apify push`.
3. In Console, leave the permission level at **Limited permissions**. Memory is already limited in `.actor/actor.json` to 128–512 MB, default 512 MB.
4. Set pay-per-event prices as described under Pricing. Delete `apify-default-dataset-item`.
5. Run once with the default input and confirm the dataset has jobs and the run finishes well under 5 minutes.
6. Add a logo and the Store description in Console, then publish.

### Sources that are not included

- **Workday.** The career-site API is undocumented, uses relative dates, and often returns placeholder locations. It is out of scope.
- **Recruitee's authenticated Careers Site API and BambooHR's authenticated ATS API.** Both need a customer token. The Actor uses only the public careers JSON (Recruitee offers feed, BambooHR `/careers/list`).
- **Breezy job-detail pages.** `https://{slug}.breezy.hr/json` is public JSON and is included. The per-job `.json` URL returns HTML or a redirect, so descriptions are left null rather than scraped.

### Open risks

- A vendor can change a public payload without notice. Fixture tests lock the current field names. A live integration test covers one real board per ATS.
- A company can move off an ATS. The old token then 404s and is reported on that company only. Auto-detect can also match the wrong board when a hostname brand is a substring of another company's name. Two confident matches produce an error asking for `ats:token`.
- Custom career sites that do not mention their ATS in the HTML depend on the brand probe (up to nine public requests). `stripe.com/careers` is the case the probe is there for: the page does not contain the string "greenhouse".
- Greenhouse `content=true` downloads the whole board even when `maxItems` is small. Dropbox is a few dozen jobs. A large Greenhouse or Ashby board is a multi-megabyte response. SmartRecruiters and BambooHR then do one detail request per job that will actually be returned, spaced 0.25 seconds apart on that host.
- The first changes-only run can return, and charge for, the whole matching backlog up to `maxItems`.
- Salary text parsing keeps the first range. Zone tables with several bands (the Dropbox example has a second US band) are only partly represented. `salaryText` still shows the matched phrase.
- Personio's public XML sometimes has empty `jobDescriptions`. The job is still returned, with a null description.
- BambooHR's list often has null locations. Location and description are filled from the detail request when descriptions are on.
- If `apify-default-dataset-item` is left enabled in Console, each job is billed twice. See Pricing.
- `Actor.push_data` does not send an idempotency key. A resurrected run can charge again for items it pushes a second time. Keys on `Actor.charge` expire after about 3 minutes anyway.

### For maintainers

- `src/main.py` is the Actor entry. `src/ats_jobs/runner.py` is the run loop and is what the unit tests call, with a fake HTTP client and a fake push.
- One module per ATS under `src/ats_jobs/sources/`. Each source implements `probe`, `fetch_list`, and `normalize`. `needs_detail` plus `enrich` covers SmartRecruiters and BambooHR.
- Recorded fixtures live in `tests/fixtures/`. `tests/fixtures/personio_description_schema.xml` is a schema sample with a description and a salary, because the live Personio XML captured for tests had an empty description. The live XML is `personio.xml`.
- Add a source only when it has a public, keyless, documented or obviously public careers endpoint. Follow redirects only when the final host is that company's own board host.

# Actor input Schema

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

Board tokens or URLs. Use ats:token (greenhouse:dropbox, lever:spotify, ashby:ramp, smartrecruiters:ServiceNow, workable:huggingface, recruitee:helloprint, personio:personio, bamboohr:flyio, breezy:remote) or paste a board URL or a company careers URL. A careers URL is scanned for an ATS link, then the hostname is checked against the public board APIs.

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

Keep a job when its title, department, team, location, or description contains any keyword. Case-insensitive. Leave empty to keep every job. Jobs that do not match are not charged.

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

Drop a job when its title, department, team, location, or description contains any of these words.

## `location` (type: `string`):

Case-insensitive substring matched against the job's locations. Example: London, Paris, United States.

## `remote` (type: `boolean`):

Return only jobs the ATS marks as remote. Hybrid and on-site jobs are dropped and not charged. Unknown remote flags are dropped too.

## `department` (type: `string`):

Case-insensitive substring matched against department and team. Example: Engineering.

## `postedWithinHours` (type: `integer`):

Drop jobs posted longer ago than this. Use 24 for the last day. Jobs with no posted date are dropped while this filter is set, and they are not charged. Removed jobs in changes-only mode ignore this filter.

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

Used only when Posted within the last N hours is empty. One day is 24 hours.

## `changesOnly` (type: `boolean`):

Return only jobs that are new or removed since the previous run that used the same state store and the same filters. The first run treats current jobs as new, up to the max items cap. Jobs that do not fit in this run stay unseen and come back as new next time. Unchanged jobs are not charged.

## `stateStoreName` (type: `string`):

Named key-value store for changes-only snapshots. Use the same name on every scheduled run. State is not written when Changes only is off.

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

Include description text and HTML. Turn this off for a smaller dataset. SmartRecruiters and BambooHR need one extra request per returned job when this is on. Salary is still parsed from the description before it is dropped.

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

Stop after this many jobs have been written. The daily Store test uses a small cap so it stays fast. Raise it for a full pull. Filtered-out jobs do not count.

## Actor input object example

```json
{
  "companies": [
    "greenhouse:dropbox"
  ],
  "remote": false,
  "changesOnly": false,
  "stateStoreName": "ats-jobs-monitor",
  "includeDescription": true,
  "maxItems": 5
}
```

# Actor output Schema

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

Normalized job listings. Each item uses the same fields for every ATS. See the dataset schema for field definitions.

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

Table of title, company, ATS, location, workplace, department, dates, and URL.

## `salary` (type: `string`):

Jobs with parsed salary minimum, maximum, currency, and period.

## `changes` (type: `string`):

Change status for changes-only runs: new, active, or removed.

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

Per-company counts, failures, and whether the run stopped on maxItems or the charge limit. Record key RUN_SUMMARY.

# 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": [
        "greenhouse:dropbox"
    ],
    "changesOnly": false,
    "stateStoreName": "ats-jobs-monitor",
    "includeDescription": true,
    "maxItems": 5
};

// Run the Actor and wait for it to finish
const run = await client.actor("plusonets/career-site-jobs-feed").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": ["greenhouse:dropbox"],
    "changesOnly": False,
    "stateStoreName": "ats-jobs-monitor",
    "includeDescription": True,
    "maxItems": 5,
}

# Run the Actor and wait for it to finish
run = client.actor("plusonets/career-site-jobs-feed").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": [
    "greenhouse:dropbox"
  ],
  "changesOnly": false,
  "stateStoreName": "ats-jobs-monitor",
  "includeDescription": true,
  "maxItems": 5
}' |
apify call plusonets/career-site-jobs-feed --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,plusonets/career-site-jobs-feed"
        }
    }
}
```

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/pfkFE4Pe3xLs200Bf/builds/D2jI24GWndVdBkaVy/openapi.json
