# ATS Jobs by Company Domain (`omargnagy/ats-jobs-by-domain`) Actor

Resolve which applicant tracking system a company uses from its domain (Greenhouse, Lever, Ashby, Workable, SmartRecruiters, Recruitee) through the vendors' own public JSON endpoints, then return every live opening as one flat record with a stable id, location, remote flag and apply URL.

- **URL**: https://apify.com/omargnagy/ats-jobs-by-domain.md
- **Developed by:** [Omar Nagy](https://apify.com/omargnagy) (community)
- **Categories:** Jobs, Developer tools, AI
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 jobs

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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

## ATS Jobs by Company Domain

Give it a company domain. Get back every live opening that company is currently advertising, already normalised.

This Actor works out which applicant tracking system a company publishes on, then reads that vendor's own public JSON job board endpoint. It covers **Greenhouse, Lever, Ashby, Workable, SmartRecruiters and Recruitee**. There is no HTML scraping of job pages, no headless browser, no proxy and nothing to log in to.

**Up to 500 companies per run**, processed in internal batches of 20 with 4 companies resolved at a time. Every input entry gets exactly one resolution record saying what happened to it. The default cap is the first **100** entries, which is a cost guard on a pay-per-event Actor: set `maxCompanies` to 500 when you want the whole list.

### Who it is for

**AI agents and job pipelines that start from a company, not from a job board.**

The usual flow is backwards: you search a job aggregator, get stale reposts, and never know which company is really hiring. Here you start from a list of companies you already care about, a target account list, a portfolio, a set of competitors, and the Actor tells you where each one posts and what is open right now, straight from the source the company itself publishes.

Concretely:

- An agent asked "is Stripe hiring backend engineers in Dublin" can answer from the company's own board in one call.
- A recruiting or sales tool enriches a CRM account list with live headcount signals.
- A job board or newsletter pulls fresh listings for a curated set of companies without scraping anyone.
- A founder tracking competitors sees which teams a rival is staffing up.

### What it does

1. Turns each input into board slug candidates. `https://www.stripe.com/jobs` becomes `stripe`.
2. Probes the six ATS endpoints in a fixed order until it finds a board with live openings.
3. If nothing is found, reads the company's own `/careers` page and homepage and follows the job board link it finds there. This is what resolves companies whose board slug is not their domain: `oysterhr.com` publishes on Ashby as `oyster`, `a.team` as `a-team`.
4. Pages through the winning board deterministically and returns one flat record per job.
5. Writes one resolution record per input entry so nothing disappears silently.

Companies it cannot place come back as records with a `status`, a `reason` and the list of boards that were tried, not as a failed run.

### Input

```json
{
  "mode": "jobs",
  "companies": ["stripe.com", "spotify.com", "posthog.com"],
  "maxJobsPerCompany": 25,
  "includeDescription": true,
  "descriptionMaxChars": 600,
  "includeResolutionRecords": true
}
```

| Field | Type | Default | Notes |
|-------|------|---------|-------|
| `mode` | `jobs` or `resolve` | `jobs` | Required. `resolve` only reports which ATS each company uses and how many jobs it has. Any other value is rejected. |
| `companies` | array of strings | | Required in both modes. Domain, full URL or plain company name. Up to 500 per run, the first 100 by default. |
| `maxCompanies` | integer 1 to 500 | `100` | Safety cap on how many entries are processed. The default is a cost guard: a large pasted list gets a bounded first run rather than a surprise bill. Raise it to 500 for the whole list; it can lower the ceiling but never raise it past 500. |
| `maxJobsPerCompany` | integer 1 to 1000 | `200` | Newest jobs first, counted after any role filter. Ignored in `resolve` mode. |
| `includeDescription` | boolean | `true` | Adds plain text descriptions. |
| `descriptionMaxChars` | integer 0 to 20000 | `1200` | Only used when `includeDescription` is on. `0` keeps the full text. |
| `includeResolutionRecords` | boolean | `true` | Writes the `type: "resolution"` rows. |
| `employmentTypeFilter` | array of strings | none | Keep only these employment types, for example `["Contract", "Freelance", "Part-time"]`. |
| `titleRegex` | string | none | Keep only titles matching this case-insensitive regular expression. Max 200 characters. |
| `resolveViaCareersPage` | boolean | `true` | Read the careers page when slug guessing finds nothing. Turn it off for the lowest latency. |

Both filters are off unless you set them, so an unfiltered run returns everything.

#### Filtering for contract, fractional and part-time roles

```json
{
  "mode": "jobs",
  "companies": ["gitlab.com", "zapier.com", "n8n.io"],
  "employmentTypeFilter": ["Contract", "Freelance", "Part-time"],
  "titleRegex": "(contract|fractional|freelance|part[- ]time|interim)",
  "maxJobsPerCompany": 50
}
```

Two rules worth knowing:

- **When both filters are set, a job is kept if it matches either one.** That is deliberate. Greenhouse's public board endpoint publishes no employment type at all, so requiring both would silently discard every Greenhouse result from a contract search.
- **Filtering happens before the per-company cut**, so `maxJobsPerCompany` counts matching jobs. `jobs_found` in the resolution record stays the board's unfiltered total, and the `reason` string says so when a filter is active.

#### When the slug does not match the domain

Careers-page discovery handles most of these automatically. When you already know the answer, skip the guessing with the `ats:slug` prefix form. Ubisoft is on SmartRecruiters as `Ubisoft2`, which no amount of guessing from `ubisoft.com` will find:

```json
{ "mode": "jobs", "companies": ["smartrecruiters:Ubisoft2", "workable:exotec", "recruitee:bunq"] }
```

Valid prefixes: `greenhouse`, `lever`, `ashby`, `workable`, `smartrecruiters`, `recruitee`. An explicit override is taken as final, so careers-page discovery does not run for that entry.

### Output

Two record types share one dataset, split by the `type` field, with a saved view for each in Apify Console.

#### A job record

Real output, run on 5 September 2026.

```json
{
  "type": "job",
  "company_input": "spotify.com",
  "company": "spotify",
  "ats": "lever",
  "ats_slug": "spotify",
  "record_key": "lever:spotify:9e8b3af7-d0f7-4aa9-ab8d-89b5bd238ccf",
  "job_id": "9e8b3af7-d0f7-4aa9-ab8d-89b5bd238ccf",
  "title": "Talent Acquisition Lead",
  "department": "People",
  "location_raw": "Singapore",
  "city": "Singapore",
  "country": "Singapore",
  "country_code": "SG",
  "is_remote": false,
  "workplace_type": "onsite",
  "employment_type": null,
  "posted_at": "2026-09-04T18:33:00.688Z",
  "updated_at": null,
  "job_url": "https://jobs.lever.co/spotify/9e8b3af7-d0f7-4aa9-ab8d-89b5bd238ccf",
  "apply_url": "https://jobs.lever.co/spotify/9e8b3af7-d0f7-4aa9-ab8d-89b5bd238ccf/apply",
  "description": "Join Spotify's Talent Acquisition team and help us find and engage the people who will shape what comes next...",
  "source_url": "https://api.lever.co/v0/postings/spotify?mode=json",
  "scraped_at": "2026-09-04T22:38:01.118Z"
}
```

`record_key` is `ats:slug:job_id` and is unique across the run, so you can upsert on it. `workplace_type` is one of `remote`, `hybrid`, `onsite`, `unknown`, and `is_remote` is always a boolean, never null.

#### A resolution record

Real output, run on 6 September 2026. This is the careers-page path: the slug `oysterhr` has an abandoned Workable board, and the real one is Ashby under `oyster`.

```json
{
  "type": "resolution",
  "company_input": "oysterhr.com",
  "company": "oyster",
  "status": "resolved",
  "ats": "ashby",
  "ats_slug": "oyster",
  "jobs_found": 25,
  "jobs_returned": 25,
  "reason": "Resolved to Ashby with 25 open job(s), 25 returned.",
  "probes_attempted": 7,
  "candidate_slugs": ["oysterhr", "oyster"],
  "candidates_tried": [
    "greenhouse:oysterhr", "lever:oysterhr", "ashby:oysterhr",
    "workable:oysterhr", "smartrecruiters:oysterhr", "recruitee:oysterhr",
    "ashby:oyster"
  ],
  "discovered_via": "https://www.oysterhr.com/careers",
  "source_url": "https://api.ashbyhq.com/posting-api/job-board/oyster",
  "scraped_at": "2026-09-06T06:01:35.631Z"
}
```

`status` is one of:

| Status | Meaning |
|--------|---------|
| `resolved` | A live board with at least one open job was found. |
| `board_found_empty` | A board exists under that slug but currently lists no jobs. This is a weak signal, not proof the company uses that ATS. `candidates_tried` shows everything that was ruled out first. |
| `not_found` | No public board answered on any of the six systems, for any candidate slug, and the careers page carried no board link. |
| `input_error` | The entry or the run input was not usable. The reason says why. A valid domain never lands here. |

A sample of both record types is in `examples/sample-output.json` in the source.
Note that description text comes verbatim from the company, so it can contain any punctuation the company used.

### Use it from an agent (Standby)

Standby keeps the Actor warm so an agent can call it mid task with no container start. The Actor's Standby base URL is shown on its Apify page; the published example task exposes one too.

```
GET  {standbyUrl}/                                       service description and limits
GET  {standbyUrl}/resolve?companies=stripe.com,spotify.com
GET  {standbyUrl}/jobs?companies=stripe.com,posthog.com&maxJobsPerCompany=25&descriptionMaxChars=600
GET  {standbyUrl}/jobs?companies=gitlab.com,zapier.com&employmentTypeFilter=Contract,Freelance&titleRegex=(contract|fractional)
```

Every input field works as a query parameter, and list fields accept a comma-separated string. Calls are authenticated the way every Apify Standby Actor is, with your token:

```
curl -H "Authorization: Bearer $APIFY_TOKEN" \
  "https://omargnagy--ats-jobs-by-domain.apify.actor/jobs?companies=stripe.com,posthog.com&maxJobsPerCompany=5"
```

A `POST` to the same paths is accepted and behaves identically; the parameters are read from the query string either way.

Response shape:

```json
{
  "ok": true,
  "mode": "jobs",
  "resolutions": [ { "type": "resolution", "...": "..." } ],
  "jobs": [ { "type": "job", "...": "..." } ],
  "count": 59,
  "tookMs": 851
}
```

Route discipline is strict, because an agent needs a wrong call to fail loudly rather than return plausible nonsense:

| Code | When |
|------|------|
| `200` | A valid call. `ok` is true. |
| `400` | A caller mistake, such as a missing `companies` or an invalid `titleRegex`. The reason is in `error`. |
| `404` | An unknown path such as `/postings`. Never data. |
| `413` | More than **50 companies in one HTTP call**. Use a batch run for up to 500. |
| `500` | A genuine internal fault. |

**Limits on the HTTP path:** 50 companies per call, because the caller is waiting on the answer. Board responses are cached in memory for 10 minutes, so a burst of calls about the same company costs the vendor one request rather than one per call. Batch runs do not cache, so a scheduled run always reads fresh.

In batch mode a bad input is written as an `input_error` record and the run still finishes successfully, so the caller gets an explanation instead of a red run with nothing in it. A value the input schema itself rejects, such as a `mode` outside the enum, is refused by the platform before the Actor starts, so that one never reaches the dataset.

### Pricing

Pay per event, no start fee, no subscription.

| Event | Price | Charged when |
|-------|-------|--------------|
| `resolution` | **$0.01** | Once per distinct ATS board that resolved to live openings. |
| `job` | **$0.002** | Once per normalised job record returned, in batch mode and through Standby. |

What is free: a domain that resolves to nothing, a board that exists but is empty, an `input_error` row, and every duplicate suppressed inside a run. You pay for boards that answered and for records you received.

Worked example. 20 companies of which 12 resolve, returning 418 job records: 12 resolutions at $0.01 plus 418 jobs at $0.002 is $0.956.

Charging happens after each internal batch of 20 companies lands in the dataset, not at the end of the run, and the checkpoint is written **before** the charge rather than after it. A run that is aborted or migrated half way is therefore charged for at most what it delivered, and a resumed run does not charge for the same board twice. The one window that remains is a migration between the checkpoint and the charge, which loses that batch's charge: the error is deliberately in your favour, never against you.

`job` is charged before `resolution`. If you set `maxTotalChargeUsd` on the run, the cap is spent on the records you came for first. A charge the platform rejects is logged and the run carries on, so you still get the whole dataset.

### Limits and honest caveats

- **500 companies per batch run, 50 per Standby HTTP call.** Entries past the cap are written as `input_error` rows with the reason, never dropped in silence.
- **Six systems only.** Greenhouse, Lever, Ashby, Workable, SmartRecruiters, Recruitee. A company on Workday, Taleo, SuccessFactors, BambooHR, Personio or a hand built careers page comes back as `not_found`.
- **Careers-page discovery only reads the served HTML** of `/careers` and the homepage, and only when slug guessing has already failed. If the board link is injected by client-side JavaScript, discovery will not see it. It reads at most 400 KB per page with an 8 second timeout, and it never runs when you give an explicit `ats:slug` override.
- **Discovery honours `robots.txt`.** It is the only path here that fetches a company's own website rather than a vendor's public job-board endpoint, so before each page it reads that origin's `robots.txt` once, with a 3 second timeout, and matches the group named `ats-jobs-by-domain` or the `*` group. A page that is disallowed is skipped and the resolution `reason` says so. A `robots.txt` that is missing, empty, unreachable or slow is treated as no restrictions, which is what the exclusion standard asks for and means a flaky host is not mistaken for a refusal.
- **An empty Ashby answer is ambiguous.** Ashby's public job board API answers `{"jobs":[]}` both for a board with no openings and for a company that has not switched that API on. Measured on `deel.com` on 6 September 2026: its own careers page carried live Ashby postings while the public endpoint returned none. Those come back as `board_found_empty` with that caveat in the `reason`, never as a confident zero.
- **SmartRecruiters answers 200 for every string**, including nonsense, so it is only ever claimed when it actually returns postings. It is never used as the "empty board" fallback.
- **`employment_type` is always null for Greenhouse.** Greenhouse's public job board endpoint does not publish it. Lever, Ashby, Workable, SmartRecruiters and Recruitee do. This is why `employmentTypeFilter` and `titleRegex` combine with OR rather than AND.
- **`company` falls back to the board slug for Lever and Ashby**, because those two endpoints carry no company display name.
- **`updated_at` is only available from Greenhouse and Recruitee.** The others publish a created or published date only.
- **Location parsing is conservative.** Greenhouse publishes one free text location string, so a multi location posting such as `SF, NYC, SEA, CHI` keeps `location_raw` and leaves `city` null rather than guessing. Two letter tokens are never read as countries in free text, because `San Francisco, CA` means California.
- **`resolve` mode ignores the role filters.** It stops before job mapping, so `jobs_found` there is always the board's full total and `employmentTypeFilter` and `titleRegex` have no effect on it. Use `mode: "jobs"` when you want a filtered count.
- **Descriptions are plain text and truncated** at `descriptionMaxChars`. Set it to `0` for the full text.
- Job postings are public data the companies publish themselves. Respect each vendor's terms and any applicable law in how you store and use it.

### What it does not do

It does not scrape LinkedIn, Indeed, Glassdoor or any job aggregator, it does not render careers pages in a browser or bypass any bot protection, it does not touch salary estimates, applicant data or anything behind a login, and it does not apply to jobs on your behalf.

### FAQ

**Does it need a proxy?**
No. Every job endpoint it calls is a public, documented, no auth JSON endpoint served by the ATS vendor.

**How do I find out which ATS a company uses without pulling all its jobs?**
Run `mode: "resolve"`. It reports the ATS, the slug and the open job count, and fetches no job records.

**Why did my company come back as `board_found_empty`?**
A board exists under that slug but has no live openings, or the vendor's public API is not switched on for it. Check `candidates_tried` to see everything that was ruled out, and use the `ats:slug` override if you know the real board.

**What happens to a list of 400 companies?**
It runs. The list is processed in batches of 20 with 4 companies resolved at a time, results are written after each batch, and every entry gets its own resolution record.

**Can I schedule it?**
Yes. Use Apify's scheduler for batch runs, or call the Standby endpoint from your own job.

**Is the output stable enough to upsert into a database?**
Yes. `record_key` is stable for as long as the posting exists on the board, and job records are deduplicated within a run.

***

Built by Omar Nagy. Part of an agent-native data-tool series on Apify.

# Actor input Schema

## `mode` (type: `string`):

jobs = resolve the ATS and return every live opening. resolve = only report which ATS each company uses and how many jobs it has, without fetching the jobs. Any other value is rejected.

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

Required in both modes. One entry per company: a domain such as stripe.com, a full URL such as https://www.spotify.com/careers, or a plain name such as PostHog. Up to 500 entries per run, processed in internal batches of 20. The default cap is the first 100; raise maxCompanies to 500 for the whole list. Entries past the cap are reported as skipped records, never dropped silently. If the board slug does not match the domain, force it with the prefix form ats:slug, for example smartrecruiters:Ubisoft2, greenhouse:stripe, lever:spotify, ashby:posthog, workable:exotec or recruitee:bunq.

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

Upper bound on job records returned for each company. Ignored when mode is resolve, because that mode returns no job records.

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

Add a plain text description to every job record. Turning this off makes runs faster and payloads smaller, and for SmartRecruiters it also removes one extra request per job.

## `descriptionMaxChars` (type: `integer`):

Maximum characters kept in each description, longer text is truncated with a trailing ellipsis. Only used when Include job description is on. Set 0 to keep the full text.

## `includeResolutionRecords` (type: `boolean`):

Write a type=resolution record for every input entry, carrying the ATS it resolved to, the slug used, how many jobs were found and a reason field for anything unresolved. Turn it off to get a dataset of job records only. Always on in resolve mode, which returns nothing else.

## `maxCompanies` (type: `integer`):

Safety cap on how many entries from the list are processed. The default is 100 and the ceiling is 500. This is a cost guard on a pay-per-event Actor: a large pasted list gets a bounded first run instead of a surprise bill. Raise it to 500 when you want the whole list. Entries past the cap are written as skipped records with the reason.

## `employmentTypeFilter` (type: `array`):

Optional. Keep only jobs whose employment type is one of these, for example Contract, Freelance, Part-time, Internship, Temporary, Full-time. Empty means no filter. Applied before the per-company limit, so the limit counts matching jobs. Greenhouse's public board endpoint publishes no employment type, so when you also set a title pattern a job is kept if it matches EITHER filter.

## `titleRegex` (type: `string`):

Optional. Case-insensitive regular expression the job title must match, for example (contract|fractional|freelance|part\[- ]time|interim). Empty means no filter. Applied before the per-company limit. Maximum 200 characters; an invalid pattern is reported as an input error instead of being ignored.

## `resolveViaCareersPage` (type: `boolean`):

When no board is found from the domain name, read the company's own /careers page and homepage and follow the job board link found there. This is what resolves companies whose board slug is not their domain. It only runs after guessing has failed, so it costs nothing for a company that resolves normally. Turn it off for the lowest possible latency.

## Actor input object example

```json
{
  "mode": "jobs",
  "companies": [
    "stripe.com",
    "smartrecruiters:Ubisoft2"
  ],
  "maxJobsPerCompany": 25,
  "includeDescription": true,
  "descriptionMaxChars": 600,
  "includeResolutionRecords": true,
  "maxCompanies": 100,
  "employmentTypeFilter": [
    "Contract",
    "Freelance",
    "Part-time"
  ],
  "titleRegex": "(contract|fractional|freelance|part[- ]time|interim)",
  "resolveViaCareersPage": true
}
```

# Actor output Schema

## `results` (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 = {
    "mode": "jobs",
    "companies": [
        "stripe.com",
        "spotify.com",
        "posthog.com"
    ],
    "maxJobsPerCompany": 25,
    "includeDescription": true,
    "descriptionMaxChars": 600,
    "includeResolutionRecords": true,
    "maxCompanies": 100,
    "resolveViaCareersPage": true
};

// Run the Actor and wait for it to finish
const run = await client.actor("omargnagy/ats-jobs-by-domain").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 = {
    "mode": "jobs",
    "companies": [
        "stripe.com",
        "spotify.com",
        "posthog.com",
    ],
    "maxJobsPerCompany": 25,
    "includeDescription": True,
    "descriptionMaxChars": 600,
    "includeResolutionRecords": True,
    "maxCompanies": 100,
    "resolveViaCareersPage": True,
}

# Run the Actor and wait for it to finish
run = client.actor("omargnagy/ats-jobs-by-domain").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 '{
  "mode": "jobs",
  "companies": [
    "stripe.com",
    "spotify.com",
    "posthog.com"
  ],
  "maxJobsPerCompany": 25,
  "includeDescription": true,
  "descriptionMaxChars": 600,
  "includeResolutionRecords": true,
  "maxCompanies": 100,
  "resolveViaCareersPage": true
}' |
apify call omargnagy/ats-jobs-by-domain --silent --output-dataset

```

## MCP server setup

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

```

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/1ifIDifWn3nF9uGbx/builds/0E83esI2RPrNnmiQ1/openapi.json
