# ATS Jobs Feed & Hiring Signals: Greenhouse, Lever, Ashby (`delightful_bloomtime/ats-jobs-feed`) Actor

Live job postings from company career pages on Greenhouse, Lever and Ashby via their official public APIs. One unified schema, salary when published, and an only-new-jobs mode for daily feeds.

- **URL**: https://apify.com/delightful_bloomtime/ats-jobs-feed.md
- **Developed by:** [Sirak Alem](https://apify.com/delightful_bloomtime) (community)
- **Categories:** Jobs, Automation, Lead generation
- **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 delivereds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## ATS Jobs Feed: Greenhouse, Lever & Ashby jobs (only new jobs mode)

👋 Hi! Give this Actor a list of companies and it brings you **their open jobs, fresh from their own career pages**:
same format for every company, salary when they publish it, and an **"only new jobs" mode** so tomorrow you
only see what was posted since today. Or switch to **company hiring signals** and get one row per company:
open roles by function (sales, SDRs, engineering...), new roles this week, remote share and an explained hiring score.

It works with any company whose careers page runs on **Greenhouse**, **Lever** or **Ashby**, which is a lot of
tech companies and startups (GitLab, Ramp, Zoox and thousands more).

#### Why I built it

I'm an independent developer from Italy who builds small automation tools. When I looked at the existing job
scrapers for these platforms, many runs failed or returned duplicates and stale data. So I kept this one simple
and boring on purpose: it reads the **official public APIs**, it does one job, and it tells you clearly when
something goes wrong. If it doesn't work for you, tell me (see the bottom of this page) and I'll fix it.

#### In short

- Data comes from the **official public job board APIs** of each ATS (the same APIs companies use to build their own career pages). No login, no proxies, no browser, no scraping of HTML.
- **Live, not a stale database**: every run reads the boards at that moment.
- **No duplicates**: every job has a stable `id` (`platform:company:jobId`).
- **One wrong slug never breaks your run**: it is reported in `RUN_SUMMARY` and the other companies are processed normally.
- **You pay only for jobs delivered.** Errors, empty boards and filtered-out jobs cost nothing.

### What you can use it for

- Daily alerts of new openings at a list of target companies (recruiters, job seekers, sales teams selling to companies that are hiring).
- Hiring signals for sales and lead generation: which companies are hiring SDRs, AEs or a VP of Sales right now, which teams are growing, which roles just closed (see [Hiring signals](#hiring-signals-one-row-per-company)).
- Salary benchmarks from Ashby and Lever postings that publish pay ranges.
- Feeding a job board, a Slack channel, a Google Sheet or a CRM through Apify integrations.

### Input

You only need the companies. The slug is the part of the career page URL after the domain:

| Platform | Career page URL | Slug |
|---|---|---|
| Greenhouse | `https://boards.greenhouse.io/gitlab` or `https://job-boards.greenhouse.io/gitlab` | `gitlab` |
| Lever | `https://jobs.lever.co/palantir` (EU: `https://jobs.eu.lever.co/...`) | `palantir` |
| Ashby | `https://jobs.ashbyhq.com/linear` | `linear` |

Not sure about the slug? Paste the career page URL into **Career page URLs** and the platform is detected for you.

Run it with an empty input and you get a small demo: 3 jobs each from GitLab, Palantir and Linear (9 jobs, about $0.01).

| Field | Type | Description |
|---|---|---|
| `greenhouse` | list of strings | Greenhouse board slugs (URLs accepted) |
| `lever` | list of strings | Lever site names (EU boards detected automatically) |
| `ashby` | list of strings | Ashby job board names |
| `careerPageUrls` | list of strings | Any mix of career page URLs, platform auto-detected |
| `outputMode` | `jobs` / `companySignals` | One row per job (default) or one row per company with hiring signals |
| `onlyNew` | boolean | Output only jobs not returned in previous runs (default `false`) |
| `stateId` | string | Name of the feed, to keep several independent feeds (default `default`) |
| `includeUnchangedCompanies` | boolean | Company signals + only new: also output companies with no change (default `false`) |
| `titleKeywords` | list of strings | Keep jobs whose title contains any of these words (case-insensitive) |
| `locationKeywords` | list of strings | Keep jobs whose location or country contains any of these words |
| `remoteOnly` | boolean | Keep only remote jobs |
| `maxJobsPerCompany` | integer | Cap per company, most recent first (0 = no cap). Jobs mode only |
| `descriptionFormat` | `text` / `html` / `none` | Job description as plain text (default), original HTML, or omitted |

#### Example input

```json
{
  "careerPageUrls": [
    "https://job-boards.greenhouse.io/gitlab",
    "https://jobs.lever.co/zoox",
    "https://jobs.ashbyhq.com/ramp"
  ],
  "titleKeywords": ["engineer"],
  "maxJobsPerCompany": 2,
  "descriptionFormat": "text"
}
```

### Output

One item per job. All platforms share the same fields; a field is `null` when the ATS does not publish it.

Real output of the example above (run on 2026-10-23, description shortened):

```json
{
  "id": "ashby:ramp:29a5da77-37f0-402a-92bc-072778e77ed8",
  "platform": "ashby",
  "companySlug": "ramp",
  "jobId": "29a5da77-37f0-402a-92bc-072778e77ed8",
  "title": "Software Engineer, Forward Deployed",
  "companyName": "ramp",
  "department": "Engineering",
  "team": "Engineering",
  "location": "New York, NY (HQ)",
  "locations": ["New York, NY (HQ)", "San Francisco, CA"],
  "country": "USA",
  "remote": false,
  "workplaceType": "hybrid",
  "employmentType": "full-time",
  "salaryMin": 168000,
  "salaryMax": 285000,
  "salaryCurrency": "USD",
  "salaryInterval": "year",
  "salaryText": "$168K - $285K",
  "url": "https://jobs.ashbyhq.com/ramp/29a5da77-37f0-402a-92bc-072778e77ed8",
  "applyUrl": "https://jobs.ashbyhq.com/ramp/29a5da77-37f0-402a-92bc-072778e77ed8/application",
  "postedAt": "2026-10-22T19:20:16Z",
  "updatedAt": null,
  "description": "ABOUT RAMP\n\nRamp is building the smart infrastructure for finance teams...",
  "isNew": null,
  "scrapedAt": "2026-10-23T19:00:47Z"
}
```

```json
{
  "id": "lever:zoox:09924dfa-16b4-415a-828e-4ae7d903ecc2",
  "platform": "lever",
  "title": "Senior Radar Engineer",
  "department": "Advanced Hardware Engineering",
  "team": "Sensor Engineering",
  "location": "Foster City, CA",
  "country": "US",
  "workplaceType": "onsite",
  "employmentType": "full-time",
  "salaryMin": 199999,
  "salaryMax": 271000,
  "salaryCurrency": "USD",
  "salaryInterval": "year",
  "salaryText": "USD 199,999 - 271,000 per year",
  "url": "https://jobs.lever.co/zoox/09924dfa-16b4-415a-828e-4ae7d903ecc2",
  "postedAt": "2026-10-22T20:15:21Z"
}
```

```json
{
  "id": "greenhouse:gitlab:8899972002",
  "platform": "greenhouse",
  "companyName": "GitLab",
  "title": "Engineering Manager, Tenant Scale: Gitaly",
  "department": "Tenant Scale Engineering",
  "location": "Remote, United Kingdom",
  "locations": ["Remote, United Kingdom", "United Kingdom"],
  "remote": true,
  "workplaceType": "remote",
  "url": "https://job-boards.greenhouse.io/gitlab/jobs/8899972002",
  "postedAt": "2026-10-23T16:05:57Z",
  "updatedAt": "2026-10-23T16:05:57Z"
}
```

#### Which fields each ATS fills

Measured on 3,615 real jobs from 12 companies (4 per platform) on 2026-10-23:

| Field | Ashby (1,144 jobs) | Lever (635) | Greenhouse (1,836) |
|---|---|---|---|
| `postedAt` | 100% | 100% | 100% |
| `department` | 100% | 51% | 100% |
| `employmentType` | 100% | 99% | not published |
| `workplaceType` | 74% | 100% | inferred from location ("remote") |
| `country` | 100% | 100% | not published |
| salary min/max | 70% | 35% | not published as a field |

#### Run summary

Each run also stores `RUN_SUMMARY` in the key-value store: status per company (`ok` / `error` / `skipped`),
number of open jobs, jobs that matched your filters, jobs delivered, and in only-new mode the number of
**new** and **closed** jobs since the previous run. Wrong slugs get a clear message, for example:

```
Board "acme" not found on ashby (check the slug in the career page URL; the company may have changed ATS)
```

### Only new jobs (feed mode)

1. Turn on **Only new jobs since the last run**.
2. The first run returns all open jobs: that is your baseline.
3. Schedule the Actor (for example daily). Each next run returns only jobs that were not there before.

The list of already-seen jobs is kept in a named key-value store called `ats-jobs-feed-state` in **your** Apify
account (the Actor needs only the standard, limited permissions). Use a different **Feed name** (`stateId`) to run
independent feeds, for example one per client. If a run stops because you reached your maximum cost per run, the
jobs that were not delivered are **not** marked as seen: you get them on the next run.

### Hiring signals: one row per company

Sometimes you don't want 700 job postings, you want to know **what a company is doing**: is it building a sales
team, hiring its first marketing lead, slowing down? Set **Output** to **Company hiring signals**
(`"outputMode": "companySignals"`) and you get **one row per company** instead of one row per job.

I added this because, for sales teams and recruiters, the job list is only the raw material: the real question is
"who is hiring SDRs right now?" or "which of my accounts just opened a VP of Sales role?". Each row tells you:

- **Open roles by function**: sales, customerSuccess, marketing, engineering, data, product, design, people, finance, legal, operations, other.
- **Role types** worth a call: SDR/BDR, account executives, sales leadership, engineering leadership, any leadership (VP, Head of, Director, C-level), internships.
- **Fresh roles**: published in the last 7 and 30 days (from the publication date the ATS gives), and which functions they are in.
- **Where**: share of remote roles, hybrid roles, top locations, countries and departments.
- **The 10 most recently published roles**, with links.
- A **hiring score from 0 to 100**, with the math next to it (see below).

Your filters still apply: with `titleKeywords: ["SDR", "BDR", "sales development"]` the row counts only those roles,
so the score answers "how hard is this company hiring SDRs?".

#### The hiring score, explained

No black box. The score is the sum of 4 parts, and every row contains `scoreBreakdown` with the points and the reason:

| Part | Max | How it is computed |
|---|---|---|
| volume | 30 | open roles: 1-4 = 5, 5-19 = 10, 20-49 = 18, 50-149 = 24, 150+ = 30 |
| freshness | 30 | share of open roles published in the last 30 days; 50% or more = 30, linear below |
| velocity | 25 | roles published in the last 7 days: 1-2 = 5, 3-5 = 10, 6-14 = 15, 15-29 = 20, 30+ = 25 |
| leadership and go-to-market | 15 | 3 points per leadership role + 1 per sales role, up to 15 |

Level: 0-24 `low`, 25-49 `moderate`, 50-74 `high`, 75-100 `very high`. Big companies naturally score higher on
volume; sort by `newLast7Days` or by a single part of `scoreBreakdown` if you care about momentum only.

Functions come from simple keyword rules on the job title (then the department, then the team). They are right most
of the time, not always: "Solutions Engineer" counts as sales (pre-sales), "Support Engineer" as customerSuccess,
"Data Engineer" as engineering. If you see a role in the wrong bucket, open an issue with the title and I'll fix the rule.

#### What changed since the last run

Turn on **Only new jobs since the last run** together with company signals, schedule it daily or weekly, and each
row also tells you:

- `newSinceLastRun` and `newRolesSinceLastRun`: roles that appeared (title, function, location, link);
- `closedSinceLastRun` and `closedRolesSinceLastRun`: roles that **disappeared** (filled, paused or pulled);
- `firstRun: true` on the first run, which is the baseline.

A company with **no change** since the last run is not output and costs nothing (you still see it in `RUN_SUMMARY`).
Want a row for every company on every run anyway? Turn on **Company signals: also output companies with no change**.
The memory for company signals is separate from the one for jobs, so you can run both modes with the same feed name.
If you change the filters, use a new **Feed name**, otherwise the next run will see the change as opened/closed roles.

#### Example

```json
{
  "greenhouse": ["gitlab", "anthropic", "stripe"],
  "lever": ["palantir", "zoox"],
  "ashby": ["ramp", "linear", "notion"],
  "outputMode": "companySignals",
  "onlyNew": true,
  "stateId": "weekly-accounts"
}
```

Real output for Ramp from a run on 2026-10-24 (lists shortened):

```json
{
  "recordType": "companySignal",
  "id": "ashby:ramp",
  "platform": "ashby",
  "companySlug": "ramp",
  "careersUrl": "https://jobs.ashbyhq.com/ramp",
  "openJobsTotal": 167,
  "jobsAnalyzed": 167,
  "hiringScore": 77,
  "hiringLevel": "very high",
  "scoreBreakdown": [
    {"part": "volume", "points": 30, "max": 30, "why": "167 open roles (1-4 = 5, 5-19 = 10, 20-49 = 18, 50-149 = 24, 150+ = 30)"},
    {"part": "freshness", "points": 17, "max": 30, "why": "28% of open roles published in the last 30 days (50%+ = full points)"},
    {"part": "velocity", "points": 15, "max": 25, "why": "11 roles published in the last 7 days (1-2 = 5, 3-5 = 10, 6-14 = 15, 15-29 = 20, 30+ = 25)"},
    {"part": "leadershipAndGtm", "points": 15, "max": 15, "why": "8 leadership roles x 3 + 55 sales roles x 1"}
  ],
  "byFunction": {"sales": 55, "engineering": 36, "customerSuccess": 17, "finance": 13, "people": 10, "product": 10, "marketing": 9, "data": 7, "legal": 4, "design": 3, "operations": 3},
  "roleTypes": {"sdrBdr": 11, "accountExecutive": 10, "salesLeadership": 1, "engineeringLeadership": 0, "leadership": 8, "internship": 5},
  "newLast7Days": 11,
  "newLast30Days": 46,
  "newLast30DaysByFunction": {"sales": 13, "engineering": 6, "customerSuccess": 5, "finance": 4, "legal": 4, "product": 4, "marketing": 3, "people": 3, "data": 2, "design": 2},
  "remoteJobs": 13,
  "hybridJobs": 137,
  "remoteShare": 0.08,
  "topLocations": [{"name": "New York, NY (HQ)", "jobs": 130}, {"name": "London, UK", "jobs": 12}, {"name": "San Francisco, CA", "jobs": 10}],
  "topDepartments": [{"name": "Sales", "jobs": 69}, {"name": "Engineering", "jobs": 37}, {"name": "People & Talent", "jobs": 14}],
  "newestPostedAt": "2026-10-23T19:41:17Z",
  "latestRoles": [
    {"title": "GTM Senior Business Systems Analyst, Marketing & Pre-Sales", "function": "sales", "location": "New York, NY (HQ)", "postedAt": "2026-10-23T17:17:16Z", "url": "https://jobs.ashbyhq.com/ramp/aa433e44-f095-484b-a46a-a330583d84b0"}
  ],
  "newSinceLastRun": null,
  "closedSinceLastRun": null,
  "firstRun": true,
  "scrapedAt": "2026-10-24T12:48:25Z"
}
```

Same run, all 9 companies in 17-70 seconds: GitLab 100, Stripe 89, Anthropic 87, Ramp 77, Zoox 75, Notion 71,
Palantir 60, Linear 57. Mistral's Lever board exists but had 0 jobs (the company may have moved to another ATS): it is reported in the row and in `RUN_SUMMARY`, not counted as an error.

### Pricing

Pay per event:

- **$1.50 per 1,000 jobs** delivered to the dataset.
- **Company hiring signals**: **$0.01 per company** ($0.0015 result + $0.0085 company signal). Companies with no change in only-new mode are free.
- A tiny Actor start fee set by Apify (default $0.00005 per run).

Empty boards, wrong slugs and filtered-out jobs are free. In only-new mode you pay only for the new jobs, and for
company signals only for the companies that changed.
Use **Max jobs per company** or the maximum cost per run in Apify to cap your spend.

### Speed and limits

- About 1 second per company page (Lever returns 100 jobs per page). 12 companies and 3,615 jobs took 20 seconds in our test.
- Requests are throttled to at most 1 per second per ATS host, with automatic retries and backoff on errors and rate limits.
- Up to 500 companies per run.
- Greenhouse boards hosted in the EU (`job-boards.eu.greenhouse.io`) are not supported: Greenhouse has no public EU Job Board API host.
- Only jobs that companies publish publicly are returned. Internal postings and unlisted Ashby jobs are excluded.

### Is it legal to use this data?

The Actor reads only the public, documented job board endpoints:

- Greenhouse Job Board API: "Job Board data is publicly available, so authentication is not required for any GET endpoints."
- Lever Postings API: "all job postings in the published state are publicly viewable. These jobs may be scraped by third parties."
- Ashby Public Job Posting API, documented at developers.ashbyhq.com.

It does not collect personal data such as recruiter names or e-mails. You are responsible for how you use the data;
respect the terms of the companies whose postings you republish.

### FAQ

**Where do I find the slug?** Open the company's careers page, click a job, and look at the URL: `boards.greenhouse.io/<slug>`, `jobs.lever.co/<slug>`, `jobs.ashbyhq.com/<slug>`. Or just paste the URL into **Career page URLs**.

**A company returns 0 jobs.** The board exists but has nothing published. The company may have moved to another ATS: check its career page URL.

**Why is a role in the wrong function?** Functions come from keyword rules on the title. Open an issue with the
title and I'll adjust the rule.

**Workday, SmartRecruiters, Workable?** Not yet. Open an issue with the companies you need.

**Found a problem or need a feature?** Open an issue on the **Issues** tab with your input (no personal data).
I read every issue myself and usually reply within 48 hours. Requests for new platforms are very welcome: they
decide what I build next.

***

Thanks for trying it! If it saves you time, a short review on the Store helps a lot. 🙏

# Actor input Schema

## `greenhouse` (type: `array`):

Board tokens (slugs) of companies on Greenhouse, e.g. gitlab from boards.greenhouse.io/gitlab or job-boards.greenhouse.io/gitlab. Full URLs are accepted too.

## `lever` (type: `array`):

Site names of companies on Lever, e.g. palantir from jobs.lever.co/palantir. EU-hosted boards (jobs.eu.lever.co) are detected automatically.

## `ashby` (type: `array`):

Job board names of companies on Ashby, e.g. linear from jobs.ashbyhq.com/linear.

## `careerPageUrls` (type: `array`):

Paste career page URLs and the platform is detected for you, e.g. https://jobs.lever.co/palantir, https://jobs.ashbyhq.com/ramp, https://boards.greenhouse.io/stripe.

## `outputMode` (type: `string`):

Jobs (default): one row per job posting. Company hiring signals: one row per company with open roles by function (sales, SDR/BDR, engineering, marketing, leadership...), roles published in the last 7 and 30 days, remote share, top locations and a transparent 0-100 hiring score. With Only new jobs on, each row also lists the roles opened and closed since the previous run.

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

Remember which jobs were already returned and output only new ones on the next runs. The first run returns all open jobs (baseline). The memory is kept in the named key-value store ats-jobs-feed-state in your account.

## `stateId` (type: `string`):

Use different names to keep independent feeds (e.g. one per schedule or per client). Only used when Only new jobs is on.

## `includeUnchangedCompanies` (type: `boolean`):

Only for Company hiring signals with Only new jobs on. By default a company is output only on the first run or when roles were opened or closed since the previous run (unchanged companies are listed for free in RUN_SUMMARY). Turn on to get a row for every company on every run.

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

Keep only jobs whose title contains at least one of these words (case-insensitive), e.g. engineer, data. Empty = all jobs.

## `locationKeywords` (type: `array`):

Keep only jobs whose location or country contains one of these words, e.g. Italy, London, Remote.

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

Keep only jobs marked remote by the ATS (Lever, Ashby) or with remote in the location (Greenhouse).

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

0 = no limit. The most recently published jobs are kept. Useful to cap costs.

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

Plain text (default), original HTML, or no description (smallest output).

## Actor input object example

```json
{
  "greenhouse": [
    "gitlab",
    "airbnb"
  ],
  "lever": [
    "palantir"
  ],
  "ashby": [
    "linear"
  ],
  "outputMode": "jobs",
  "onlyNew": false,
  "stateId": "default",
  "includeUnchangedCompanies": false,
  "remoteOnly": false,
  "maxJobsPerCompany": 0,
  "descriptionFormat": "text"
}
```

# Actor output Schema

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

No description

## `runSummary` (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 = {
    "greenhouse": [
        "gitlab",
        "airbnb"
    ],
    "lever": [
        "palantir"
    ],
    "ashby": [
        "linear"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("delightful_bloomtime/ats-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 = {
    "greenhouse": [
        "gitlab",
        "airbnb",
    ],
    "lever": ["palantir"],
    "ashby": ["linear"],
}

# Run the Actor and wait for it to finish
run = client.actor("delightful_bloomtime/ats-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 '{
  "greenhouse": [
    "gitlab",
    "airbnb"
  ],
  "lever": [
    "palantir"
  ],
  "ashby": [
    "linear"
  ]
}' |
apify call delightful_bloomtime/ats-jobs-feed --silent --output-dataset

```

## MCP server setup

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