# Find Companies Using Greenhouse, Lever, Ashby & Workable (`wickfeed/ats-company-discovery`) Actor

Find companies using Greenhouse, Ashby, Workable, Recruitee or Lever (EU) and see who's hiring right now — live open-role counts across ~1,990 companies. Filter by keyword. $2 per 1,000 companies.

- **URL**: https://apify.com/wickfeed/ats-company-discovery.md
- **Developed by:** [WickFeed](https://apify.com/wickfeed) (community)
- **Categories:** Jobs, Lead generation, MCP servers
- **Stats:** 2 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$2.00 / 1,000 company founds

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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
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.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use 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

## Find Companies Using Greenhouse, Lever, Ashby & Workable

**$2 / 1,000 verified-live companies · $0 to start · open-role counts included · re-verified at query time**

**Live status:** https://api.apify.com/v2/key-value-stores/WickFeed~wickfeed-public-status/records/status.json — real self-test results for every wickfeed scraper (pass/fail plus 30-day success rate), updated hourly, no login needed. Opens as raw data (JSON).

**Choose the right WickFeed tool**  
- Mixed hiring systems, or unsure which one a company uses → **[ATS Job Aggregator](https://apify.com/wickfeed/ats-job-aggregator)**  
- Greenhouse only → **[Greenhouse Jobs Scraper](https://apify.com/wickfeed/greenhouse-jobs-scraper)**  
- Ashby only, including published salary ranges → **[Ashby Jobs Scraper](https://apify.com/wickfeed/ashby-jobs-scraper)**  
- Workable only → **[Workable Jobs Scraper](https://apify.com/wickfeed/workable-jobs-scraper)**  
- Need companies and live job counts, not job rows → **[ATS Company Discovery](https://apify.com/wickfeed/ats-company-discovery)** (this tool)

**Ask "who uses Greenhouse — or Lever, Ashby, Workable, Recruitee — and who has open job postings right now?" and get back a clean list of real companies.** Each one is **re-checked live the moment you run it**, with a current open-role count, sample job titles, and direct links to its careers page and public jobs API.

**Your first result in four steps:**

1. **Try for free** and sign in to Apify.
2. Leave the demo unchanged and click **Start**.
3. When the run finishes, open **Dataset** to see one row per company.
4. Click **Export** for CSV, Excel, or JSON.

*Unofficial — not affiliated with or endorsed by Greenhouse, Lever, Ashby, Workable, or Recruitee.*

**Sources:** Greenhouse · Lever (EU) · Ashby · Workable · Recruitee — **5 sources.** (US Lever isn't covered — see below.)

**In plain English — this is step one.** It hands you a clean list of real companies that have open job postings right now. From there you do one of two things: reach out to them yourself (recruiting or sales), or hand the same list straight to the [ATS Job Aggregator](https://apify.com/wickfeed/ats-job-aggregator) to pull and track their open jobs for you. **Greenhouse, Ashby, Workable, and Recruitee results feed directly into the ATS Job Aggregator; EU-Lever results are discovery-only for now.** Find the companies here; do the outreach or the job-tracking next.

### Quick start

1. **Pick the hiring systems** you care about in the **ATS** dropdown — or choose **All sources**. The form starts on Greenhouse.
2. **Set "Minimum open jobs"** to focus on companies with real hiring volume (the form prefills 5), and optionally add **job-title keywords** like `engineer` or `nurse` to keep only companies hiring for that role.
3. **Press Start.** With **Re-verify live** on (the default), the run checks up to 25 high-volume candidates and returns the subset that is live and matches your filters — each with a current open-role count and sample titles — usually in well under a minute. Keyword searches, dead boards, and minimum-job filters can produce fewer than 25 results.

At **$2 per 1,000 companies**, a 25-company first run costs about **$0.05**, and **your first runs are free on the Apify free plan**. Export the results to **CSV, Excel, JSON, or Google Sheets** straight from the Apify Console — no code.

### What it's for

- **Recruiting / sales lead-gen** — build a target list of companies that are actively hiring on a specific ATS.
- **Job-board & aggregator seeding** — get a verified list of live boards to pull jobs from (feed it straight into a job scraper, including our sibling actor **[ATS Job Aggregator](https://apify.com/wickfeed/ats-job-aggregator)**).
- **Market mapping** — see who uses which hiring platform, and who has the most open job postings right now.

**A 20-second worked example.** Run it for Greenhouse with the keyword `engineer`. Back comes a row like `Anduril Industries — 2,171 open jobs — careers link — jobs-API link`. Open the careers page to inspect the company's public jobs — this actor does not provide contact details. A data team drops that row's `ats` + `token` into the [ATS Job Aggregator](https://apify.com/wickfeed/ats-job-aggregator) and pulls every posting. Same row, either job done.

### Why this one

The closest existing actor ([igolaizola/greenhouse-companies](https://apify.com/igolaizola/greenhouse-companies)) is **Greenhouse-only** and returns a **static, pre-collected list** with no guarantee a board is still live. This actor is different on the things that matter:

| | This actor | Greenhouse-only benchmark |
|---|---|---|
| Sources | **5** (Greenhouse, EU-Lever, Ashby, Workable, Recruitee) | 1 (Greenhouse) |
| Freshness | **Verified live at query time** | Static list (may be stale) |
| Live open-job count | **Yes** (measured this run) | No |
| Sample job titles | **Yes** (≤5 per company) | No |
| "Companies hiring X" keyword filter | **Yes** (matches live job titles) | No |
| Provenance | **Honest** (public Common Crawl + our own live checks) | — |

### What "verified" means (read this)

- **Bundled base list.** The actor ships ~**1,990 company boards**. **How we built it:** we queried the public **Common Crawl** URL index (the `CC-MAIN-2026-25` and `CC-MAIN-2026-21` monthly crawls) for each vendor's board host pattern (e.g. `boards.greenhouse.io/*`, `jobs.ashbyhq.com/*`), extracted the board tokens, then **confirmed every board live ourselves** against its own public JSON API on **2026-07-15**, keeping only boards returning at least one open job. No third-party curated company list was copied or repackaged — only the public archive index plus our own live checks. Breakdown: Greenhouse 872 · Workable 380 · Ashby 373 · Recruitee 300 · EU-Lever 65.
- **Live re-verification (default ON).** With `reverify: true`, the actor re-hits each company's **own public API** during the run and returns the **fresh** status, **current** open-job count, and **current** sample titles. A board that has since gone dead or dropped below your minimum is **excluded** from the results (it is not returned as a stale row) — so every returned company had open job postings live *at the moment you ran it*.
- **Not magic.** "Verified" means we successfully fetched that company's public jobs endpoint and it returned at least `minJobs` open jobs, right now. It does **not** mean anything about private/internal reqs or data the company doesn't publish.

### Is this allowed?

Everything this actor reads is **public** — the same job data a company already publishes on its own careers page, fetched from each vendor's **public JSON API**. No login, no password-walled data, no HTML scraping, no private or candidate information. We don't make any legal claim about how you use the results — but there's no gated or hidden data involved here, only what employers put out in the open. Use the company lists for hiring research and lead-gen, not spam.

### Input

```json
{
  "ats": ["greenhouse"],
  "minJobs": 5,
  "keywords": ["engineer"],
  "maxResults": 25,
  "reverify": true,
  "discoverFresh": false
}
````

| Field | Type | Default | Notes |
|---|---|---|---|
| `ats` | string\[] | `["greenhouse"]` | Which hiring systems to search. The form and a bare API call with no input both start on **Greenhouse**; choose **All sources** or pass `["all"]` to search every platform. Any of `greenhouse`, `lever-eu`, `ashby`, `workable`, `recruitee`, or `all` (as array items). **US Lever (`jobs.lever.co`) isn't offered** — it's robots-blocked in the archive we build from, so there's no clean bundled list; use `lever-eu` for Lever. If every system you name is unsupported, the run stops with a message listing the valid choices. |
| `minJobs` | integer | `5` | Only return companies with at least this many open jobs posted **right now**. The form and a bare API call both start at 5; lower it to 1 to include any company with at least one open posting. |
| `keywords` | string\[] | `[]` | Keep only companies with at least one open job whose **title** contains any of these words (case-insensitive) — e.g. `["engineer"]` → companies hiring engineers. **Needs `reverify: true`** (titles are read during verification): setting keywords with `reverify: false` **stops the run** with a plain-English message rather than billing you for an unfiltered snapshot list. |
| `maxResults` | integer | `25` | **How many boards this run checks — not a scan of all ~1,990.** It takes the **top N boards by their stored (build-date) job count** and checks only those live. So a keyword search searches only those top N boards: a zero result means "none of the top N matched", not "nobody matches" — raise this to search deeper. It is also your cost ceiling ($0.002 per **returned** company: 25 ≈ $0.05, 1,000 ≈ $2, 5,000 max ≈ $10). The run returns the subset of those boards that is live and matches your filters, so keyword searches, dead boards, and minimum-job filters can produce fewer results than N. |
| `reverify` | boolean | `true` | **ON:** re-check every company live now (fresh status, count, titles). **OFF:** return the bundled list from our **stored snapshot** — instant, no network, but the counts are the build-date snapshot, each row reports `live: null` and `verifiedAt: null` (not re-checked this run) with `snapshotAt` set to the build date, and there are no titles or keyword filtering. |
| `discoverFresh` | boolean | `false` | **ON:** also search the public Common Crawl archive at runtime for company boards **not** already in the bundle, then verify them live. Finds newer companies but is **slower** and depends on the archive being reachable. Needs `reverify: true`. |

**Default first run** (form prefill and a bare API call with no input): `ats: ["greenhouse"]`, `minJobs: 5`, `maxResults: 25`, `reverify: true` → the run checks up to 25 high-volume candidates and returns the subset that is live and matches your filters, each with a live job count, usually in well under a minute. Keyword searches, dead boards, and minimum-job filters can produce fewer than 25 results.

### Output

One record per verified-live company, pushed to the dataset. Real example (from a live run):

```json
{
  "ats": "greenhouse",
  "token": "andurilindustries",
  "company": "Anduril Industries",
  "jobCount": 2171,
  "live": true,
  "apiUrl": "https://boards-api.greenhouse.io/v1/boards/andurilindustries/jobs",
  "careersUrl": "https://boards.greenhouse.io/andurilindustries",
  "sampleTitles": [
    "2026 Early Career Electrical Engineer",
    "2026 Early Career Engineering Finance Associate",
    "2026 Early Career Finance Coordinator",
    "2026 Early Career Flight Test Engineer, Altius",
    "2026 Early Career Manufacturing Engineer"
  ],
  "verifiedAt": "2026-07-15T19:22:01.680Z",
  "snapshotAt": null,
  "schemaVersion": 1
}
```

| Field | Type | Meaning |
|---|---|---|
| `ats` | string | Which system, one of: `greenhouse`, `lever`, `lever-eu`, `ashby`, `workable`, `recruitee`. |
| `token` | string | The company's board id in that system (the slug/subdomain). Pass this + `ats` straight into the [ATS Job Aggregator](https://apify.com/wickfeed/ats-job-aggregator)'s `sources` to pull the jobs. |
| `company` | string or null | Best-effort display name. Falls back to a title-cased token when the API exposes no name — always for Lever, sometimes for Ashby. |
| `jobCount` | integer | Open jobs on the board. Measured live **as of `verifiedAt`** when re-verified; the **build-date snapshot count as of `snapshotAt`** when `reverify: false`. |
| `live` | boolean or null | Whether the board is live **now**. With `reverify: true` every returned record is `true` (dead boards are dropped). With `reverify: false` it is **`null`** — we didn't re-check it this run, so we don't claim it's live now (`snapshotAt` says when it last was). |
| `apiUrl` | string | The board's public JSON jobs endpoint — GET this to pull the actual postings (or feed it to a job scraper). |
| `careersUrl` | string | The human-facing public careers page. |
| `sampleTitles` | string\[] | Up to 5 current open-job titles (empty when `reverify: false`). |
| `verifiedAt` | ISO string or null | When this company was verified **this run**. **`null` when `reverify: false`** (nothing was re-checked this run — see `snapshotAt`). |
| `snapshotAt` | ISO string or null | The build date our bundle last confirmed this board live. **Set when `reverify: false`**; `null` on rows re-verified live this run. |
| `schemaVersion` | number | The output-record schema version (currently `1`). See **Output contract** below. |

#### Output contract (schema stability)

Your pipeline shouldn't break because we shipped an update, so here's the promise in writing:

- **We never rename or remove an existing field within a major version.** The fields above — `ats`, `token`, `apiUrl`, `careersUrl`, and the rest — keep their names and their meaning. Build on them safely.
- **A `null` is honest, not missing.** Where a system exposes no display name, `company` is present and set to `null` (or a title-cased token) — never dropped, so a field you read is always there.
- **New fields are added, never forced.** We may *add* a field over time; adding one never touches the fields you already read, so your existing code keeps working untouched.
- **A breaking change is loud, not silent.** If a field ever had to be renamed or removed, that's a major version bump, called out in the **Changelog** (and the Issues tab) before it lands — never a surprise on your next scheduled run.
- **`schemaVersion` is a breaking-contract version, not a shape fingerprint.** Every record carries it (it's in `RUN_SUMMARY` too). It bumps **only** when an existing field is renamed, removed, or its type changes — the things that would break code you already wrote. **Adding a new field does *not* bump it** (adding one never touches the fields you read). So `schemaVersion` doesn't promise "the exact set of fields is frozen"; it promises "everything you already read keeps its name and meaning until this number changes." It's `1` today.
- **One honest note for `reverify: false` (snapshot mode).** As of **0.1.12**, snapshot rows report `live: null` and `verifiedAt: null` (they were **not** re-checked this run) and carry `snapshotAt` (the build date they last were live) — the earlier build reported a misleading `live: true` / this-run timestamp here. If your pipeline consumed `reverify: false` output and assumed `live` was always a boolean, add a `null` check. This corrects a mislabeling; the default `reverify: true` contract is unchanged (`live` is always `true`, `verifiedAt` always an ISO string there), so `schemaVersion` stays `1`.

#### Run summary

The **dataset holds company records only.** A run summary is saved to the **key-value store** as `RUN_SUMMARY` (candidate counts, how many were re-verified, and how many were dropped and why — dead, below-minimum, no keyword match, error). Records that fail internal validation go to `REJECTS` with reasons — never silently dropped.

### Pricing

**Pay-per-event:**

- `company-found` — per company returned: **$2.00 per 1,000 companies**. **Starting a run costs $0.** With **Re-verify live on** (the default) you pay only for companies confirmed live with open job postings at query time — dead or below-minimum boards are dropped and never billed. With **Re-verify live off** you pay the same rate for each **stored-snapshot** row returned (build-dated, `snapshotAt` marks the date, not re-checked this run) — cheaper on time, but not re-verified, so prefer the default when you're paying to know a board is live *now*.

**First run free.** The default 25-company run costs about **$0.05**, and Apify's free plan covers your early runs — so trying it is effectively free.

**Price anchor.** The closest thing on the shelf ([igolaizola/greenhouse-companies](https://apify.com/igolaizola/greenhouse-companies)) is **Greenhouse-only** and returns a **static** list. Its price is tiered by your Apify plan: **$1.00 per 1,000 on the Free tier**, dropping to **$0.70 per 1,000 only on the Gold tier and above**, **plus a per-run start fee** (prices checked 2026-07-16). This actor is **$2 per 1,000 with $0 to start at every tier** — because a record here is a **live-verified company with a current open-role count** (who to sell to, who to aggregate) across **5 sources**, not a single static row, and dead boards are never billed.

**If a charge ever looks wrong,** open an issue on the **Issues tab** with your run ID — we investigate the same day, and if we over-billed you we file a credit request with Apify to put it back on your account. (Apify pays seller compensation as account credit rather than cash, so that's what we can promise and what we'll do — not a guarantee about how Apify itself handles it.)

### Limitations (read this)

- **Public boards only.** This reads the job data a company chooses to publish. No private/authenticated ATS data, internal reqs, or candidate data.
- **Company discovery is bundle-first.** No ATS lets you enumerate every customer. The bundled ~1,990 verified boards are the base list; `discoverFresh` adds a best-effort live search of the public archive for boards not yet in the bundle.
- **US Lever is not covered.** `jobs.lever.co` is robots-blocked in Common Crawl, so there is no license-clean bundled list. EU-Lever (`jobs.eu.lever.co`) **is** covered.
- **`maxResults` is a top-N-boards budget, not a full-corpus scan.** A run checks only the top `maxResults` boards by their stored (build-date) job count (biggest first) and returns the live subset — it does **not** search all ~1,990 boards. So a keyword search's zero result means "none of the top N matched," not "nobody matches" — raise `maxResults` to search deeper. Each returned company is $0.002, so cost scales with it: 200 ≈ $0.40, 1,000 ≈ $2, the 5,000 maximum ≈ $10.
- **`discoverFresh` shares the budget so fresh boards aren't starved.** Newly-crawled boards have no stored job count, so a plain biggest-first cut would sort them behind every sized bundled board and never check them. To give them a fair shot, a fresh-discovery run **reserves up to half of `maxResults`** for fresh boards (whichever side has fewer donates its slack to the other). The total still never exceeds `maxResults`, so your cost ceiling is unchanged.
- **Boards churn by the minute.** A board can be live when the run starts and gone an hour later — that's exactly why `reverify` exists. `verifiedAt` records the moment we checked.
- **Rate & politeness.** ≤2 requests/second **per host**, with retries and backoff; hosts run in parallel. Large runs take proportionally longer by design.
- **Memory & response size.** Runs on a 512 MB floor; each HTTP response is capped at 30 MB (env `ATS_MAX_RESPONSE_BYTES`). An over-size or unreachable board is dropped for that one company, never the whole run.

### FAQ

**What do I get back, exactly?** One row per company that has **open job postings live right now** on the ATS you picked — with its board `token`, a current open-role count, up to five sample job titles, and links to its careers page and public jobs API. Not job rows; *companies*.

**How is this different from a job scraper?** A job scraper needs to know *which* companies to pull from. This actor answers that first question — *who uses this hiring system and has open job postings right now* — and hands you the list. Point a scraper at that list next (see the chaining recipe below).

**Do I need proxies or a login?** No. These are public vendor APIs — the actor calls them directly, the same request a browser makes when it loads a careers page. No account, no password-walled data.

**How fresh is the list?** Every returned company is re-checked **the moment you run it** (`reverify: true`, the default), so the status, count, and titles are live. The bundled base list was last confirmed live on **2026-07-15**; turn `reverify` off for an instant, network-free snapshot of that build instead.

**How do I find companies hiring for a specific role?** Add **job-title keywords** (e.g. `["engineer"]`) with `reverify` on — you'll get back only companies that have at least one open job whose title matches.

**Why isn't US Lever covered?** `jobs.lever.co` is robots-blocked in the public Common Crawl archive we build the base list from, so there's no license-clean way to ship US Lever boards. **EU-Lever** (`jobs.eu.lever.co`) is covered.

**I got few or zero companies back — is it broken?** Almost never. Companies are dropped (and never charged) when a board has since gone dead, fell below your "Minimum open jobs", or had no open title matching your keywords. First, widen the net: lower "Minimum open jobs", clear the job-title keywords, or add more hiring systems — then check the run's **Status** and **Logs**. If it still looks wrong, **open an Issue and include your run link**. *Advanced diagnostics:* the full breakdown — how many boards were dead, below-minimum, no-keyword, or errored — is written to the run's key-value store as `RUN_SUMMARY`.

**Can AI agents use this?** Yes — it's callable through Apify's hosted MCP server at `mcp.apify.com` by Claude Desktop, Cursor, n8n, LangGraph, CrewAI and other MCP clients. See [Use with AI agents (MCP)](#use-with-ai-agents-mcp) — including a ready-made recipe to chain it into the job aggregator.

### Use with AI agents (MCP)

This actor is callable as a tool by AI agents — **Claude Desktop, Cursor, VS Code, n8n, LangGraph, CrewAI**, or any MCP-compatible client — through **Apify's hosted MCP server** at `mcp.apify.com`. Agents find it with `search-actors`, inspect its input with `fetch-actor-details`, and run it with `call-actor`. Nothing to install or host on our side.

Point your MCP client at Apify's server:

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com"
    }
  }
}
```

That form uses OAuth sign-in. To pass a token instead, add a header:

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com",
      "headers": { "Authorization": "Bearer <YOUR_APIFY_TOKEN>" }
    }
  }
}
```

The output is flat, stable JSON built for machine consumption, and agentic pay-per-event billing is enabled.

#### Chain it: discover companies → pull their jobs (agent recipe)

This actor is the **front door** to the whole toolkit. It emits one record per company carrying an **`ats`** and a **`token`** — exactly the two fields the sibling **[ATS Job Aggregator](https://apify.com/wickfeed/ats-job-aggregator)** reads in its `sources` input. So an agent can run a two-step pipeline with no human in the middle:

1. **Discover** — `call-actor` `wickfeed/ats-company-discovery`:

   ```json
   { "ats": ["greenhouse"], "minJobs": 5, "keywords": ["engineer"], "maxResults": 50 }
   ```

   Each returned record looks like `{ "ats": "greenhouse", "token": "andurilindustries", "jobCount": 2171, ... }`.

2. **Pull the jobs** — take the `ats` + `token` from each company and pass them straight into the aggregator's `sources`, then `call-actor` `wickfeed/ats-job-aggregator`:

   ```json
   {
     "sources": [
       { "ats": "greenhouse", "token": "andurilindustries" },
       { "ats": "greenhouse", "token": "stripe" }
     ],
     "maxResultsPerSource": 50
   }
   ```

   Turn on the aggregator's `diffMode` and put it on a daily Apify Schedule to monitor those same companies — paying only for **new** postings, and **$0 on a quiet day**.

**One honesty note for the chain.** Greenhouse, Ashby, Workable and Recruitee tokens map 1:1 — drop them straight into `sources`. **EU-Lever (`lever-eu`) is discovery-only**: the aggregator's Lever reader uses the US endpoint (`api.lever.co`), so skip `lever-eu` rows when you hand the list over. The aggregator bundles 4 of those 5 systems (Greenhouse, Ashby, Workable, Recruitee); its Lever is bring-your-own for now.

### Changelog

- **0.1.13 — 2026-07-16** — Added a public live-status link (real self-test results, no login).
- **0.1.12 — 2026-07-16** — **Honest snapshot mode, machine-readable output schema, and clearer controls.** `reverify: false` (snapshot) rows now report `live: null` and `verifiedAt: null` (they were **not** re-checked this run) and carry a new **`snapshotAt`** field (the build date they last were live) — replacing the earlier misleading `live: true` / this-run timestamp; the default `reverify: true` contract is unchanged, so `schemaVersion` stays `1`. Setting **job-title keywords with `reverify: false` now stops the run** with a plain-English message instead of billing you for an unfiltered snapshot list. Fresh discovery (`discoverFresh`) now **reserves up to half of `maxResults`** for newly-crawled boards so they aren't starved behind the big bundled ones (total still capped at `maxResults`). A real **machine-readable dataset field schema** was published so generated clients can discover the row shape. Docs: `schemaVersion` is described as a breaking-contract version (additive fields don't bump it), the `maxResults` control is labeled a **top-N-boards** budget (a keyword search scans only the top N boards, so a zero result isn't "nobody matches"), the provenance note is now plain text (no dead relative link), the array-form `"ats": ["all"]` example is used everywhere, and the competitor price anchor is dated and tier-labeled.
- **0.1.9 — 2026-07-16** — **Fail-closed input, aligned defaults, and honest wording.** An input where every hiring system you chose is unsupported (for example only US `lever`) now stops the run with a plain-English message listing the valid choices, instead of quietly widening the search to all five systems — a list with at least one usable system still runs on that system and just notes the ones it ignored. A bare API call's **Minimum open jobs** default is now **5**, matching the Console form (both were already Greenhouse-only). Copy updates: "open job postings right now" replaces the older "confirmed hiring" wording, the Console/API defaults are stated exactly, the result cap is described accurately (the run checks up to 25 candidates and returns the live subset that matches your filters, so you can get fewer than 25), the discovery→aggregator handoff notes up front that EU-Lever is discovery-only, and a four-line first-result path plus a tool chooser were added near the top. No change to the output record.
- **0.1.8 — 2026-07-16** — **Output contract + safer first API call.** Every company record now carries a `schemaVersion` field (currently `1`), and this README documents the **output contract** (existing fields are never renamed or removed within a major version; `null` means honestly-absent, not missing) — additive, so existing pipelines are unaffected. A bare `{}` API call now mirrors the Console prefill (searches Greenhouse, the prefilled system) instead of quietly searching all five — the returned count is still capped by *Max results* (default 25 ≈ $0.05), so a first call can't surprise-bill. To search every system, pass `"ats": ["all"]` (or pick the systems you want); an explicitly-cleared selection still means all sources.
- **0.1.0** — Initial build. Five-source live verification (Greenhouse, EU-Lever, Ashby, Workable, Recruitee), ~1,990 bundled verified boards, query-time re-verification with live job counts + sample titles, keyword ("companies hiring X") filter, optional Common Crawl fresh discovery, per-host rate limiting + retries/backoff, schema validation with `REJECTS`, run summary, pay-per-event charging.

***

**Worth re-running.** Boards go live and dead constantly, and companies cross your "minimum open jobs" line week to week. Re-run this on a schedule to catch newly-hiring companies and drop the ones that have gone quiet — every run re-checks live, so your list never silently goes stale. (One-off research needs a single run; an always-current target list is the reason to schedule it.)

Got your company list? Pull every open job with the [ATS Job Aggregator](https://apify.com/wickfeed/ats-job-aggregator) ($1/1k jobs) — and turn on its **diff mode** to **track only new postings** across those boards on a daily schedule, paying only for what changes (and $0 on a quiet day). A 2-step, pay-as-you-go pipeline: discover the companies here, then monitor their jobs there.

**Need help? Open an Issue with your run link.** If a run misbehaves or a charge looks off, open the **Issues tab** with your run ID and we'll look into buyer-facing breakages the same day.

# Actor input Schema

## `ats` (type: `array`):

Which applicant-tracking systems to search for companies on. Pick one or more, or choose "All sources". "EU-Lever" covers Lever boards hosted on the European endpoint (jobs.eu.lever.co). Note: US Lever (jobs.lever.co) has no bundled boards — it is robots-blocked in the public web archive we build the list from — so it isn't offered here; use EU-Lever for Lever coverage.

## `minJobs` (type: `integer`):

Only return companies with at least this many open jobs posted right now. Default 5 — both the form and a bare API call with no input start here. Lower it to 1 to include any company with at least one open posting; raise it to focus on companies with more open jobs.

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

Optional. Keep only companies that have at least one open job whose TITLE contains any of these words (case-insensitive) — e.g. \["engineer"] finds companies hiring engineers. Requires "Re-verify live" ON (titles are read during verification): if you set keywords with Re-verify live OFF, the run stops with a plain-English message instead of billing you for an unfiltered snapshot list. Leave empty for no keyword filtering.

## `maxResults` (type: `integer`):

How many boards this run checks — it does NOT scan all ~1,990 bundled boards. It takes the top N boards by their STORED (build-date) job count and checks only those live, so this is a top-N-boards control, not a returned-matches limit. A keyword search therefore searches only these top N boards: a zero result means "none of the top N matched", not "no company anywhere matches" — raise this number to search deeper. It is also your cost ceiling, since each RETURNED company is $0.002: 25 ≈ $0.05, 200 ≈ $0.40, 1,000 ≈ $2, and the 5,000 maximum ≈ $10. The run returns the subset of those boards that is live and matches your filters, so keyword searches, dead boards, and minimum-job filters can produce fewer results than N. Prefilled at 25 for a fast, cheap first run; clearing the box falls back to 25, not a bigger number.

## `reverify` (type: `boolean`):

ON (default): re-check each company against the vendor's public API right now, returning fresh live status, current open-job count, and sample job titles. OFF: return the bundled list as-is from our stored snapshot — instant and free of network calls, but the counts are the build-date snapshot, and each row reports live: null and verifiedAt: null (not re-checked this run) with snapshotAt set to the build date. There are no sample titles and no keyword filtering in this mode.

## `discoverFresh` (type: `boolean`):

Experimental. OFF (default): use the bundled verified list only. ON: also search the public web archive (Common Crawl) at runtime for company boards NOT already in the bundle, then verify them live. This finds newer companies but is noticeably slower, is capped, and falls back to the bundle on any failure. Requires "Re-verify live" on.

## Actor input object example

```json
{
  "ats": [
    "greenhouse"
  ],
  "minJobs": 5,
  "keywords": [
    "engineer",
    "nurse",
    "sales"
  ],
  "maxResults": 25,
  "reverify": true,
  "discoverFresh": false
}
```

# 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 = {
    "ats": [
        "greenhouse"
    ],
    "minJobs": 5,
    "maxResults": 25
};

// Run the Actor and wait for it to finish
const run = await client.actor("wickfeed/ats-company-discovery").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 = {
    "ats": ["greenhouse"],
    "minJobs": 5,
    "maxResults": 25,
}

# Run the Actor and wait for it to finish
run = client.actor("wickfeed/ats-company-discovery").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "ats": [
    "greenhouse"
  ],
  "minJobs": 5,
  "maxResults": 25
}' |
apify call wickfeed/ats-company-discovery --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=wickfeed/ats-company-discovery",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Find Companies Using Greenhouse, Lever, Ashby & Workable",
        "description": "Find companies using Greenhouse, Ashby, Workable, Recruitee or Lever (EU) and see who's hiring right now — live open-role counts across ~1,990 companies. Filter by keyword. $2 per 1,000 companies.",
        "version": "0.1",
        "x-build-id": "3W74GIJVg1vyy3yXy"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/wickfeed~ats-company-discovery/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-wickfeed-ats-company-discovery",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/wickfeed~ats-company-discovery/runs": {
            "post": {
                "operationId": "runs-sync-wickfeed-ats-company-discovery",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/wickfeed~ats-company-discovery/run-sync": {
            "post": {
                "operationId": "run-sync-wickfeed-ats-company-discovery",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "properties": {
                    "ats": {
                        "title": "Hiring systems (ATS)",
                        "type": "array",
                        "description": "Which applicant-tracking systems to search for companies on. Pick one or more, or choose \"All sources\". \"EU-Lever\" covers Lever boards hosted on the European endpoint (jobs.eu.lever.co). Note: US Lever (jobs.lever.co) has no bundled boards — it is robots-blocked in the public web archive we build the list from — so it isn't offered here; use EU-Lever for Lever coverage.",
                        "items": {
                            "type": "string",
                            "enum": [
                                "greenhouse",
                                "lever-eu",
                                "ashby",
                                "workable",
                                "recruitee",
                                "all"
                            ],
                            "enumTitles": [
                                "Greenhouse",
                                "Lever (EU — jobs.eu.lever.co)",
                                "Ashby",
                                "Workable",
                                "Recruitee",
                                "All sources"
                            ]
                        }
                    },
                    "minJobs": {
                        "title": "Minimum open jobs",
                        "minimum": 0,
                        "maximum": 100000,
                        "type": "integer",
                        "description": "Only return companies with at least this many open jobs posted right now. Default 5 — both the form and a bare API call with no input start here. Lower it to 1 to include any company with at least one open posting; raise it to focus on companies with more open jobs.",
                        "default": 5
                    },
                    "keywords": {
                        "title": "Job-title keywords",
                        "type": "array",
                        "description": "Optional. Keep only companies that have at least one open job whose TITLE contains any of these words (case-insensitive) — e.g. [\"engineer\"] finds companies hiring engineers. Requires \"Re-verify live\" ON (titles are read during verification): if you set keywords with Re-verify live OFF, the run stops with a plain-English message instead of billing you for an unfiltered snapshot list. Leave empty for no keyword filtering.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "maxResults": {
                        "title": "Boards to check (top N by stored job count)",
                        "minimum": 1,
                        "maximum": 5000,
                        "type": "integer",
                        "description": "How many boards this run checks — it does NOT scan all ~1,990 bundled boards. It takes the top N boards by their STORED (build-date) job count and checks only those live, so this is a top-N-boards control, not a returned-matches limit. A keyword search therefore searches only these top N boards: a zero result means \"none of the top N matched\", not \"no company anywhere matches\" — raise this number to search deeper. It is also your cost ceiling, since each RETURNED company is $0.002: 25 ≈ $0.05, 200 ≈ $0.40, 1,000 ≈ $2, and the 5,000 maximum ≈ $10. The run returns the subset of those boards that is live and matches your filters, so keyword searches, dead boards, and minimum-job filters can produce fewer results than N. Prefilled at 25 for a fast, cheap first run; clearing the box falls back to 25, not a bigger number.",
                        "default": 25
                    },
                    "reverify": {
                        "title": "Re-verify live (recommended)",
                        "type": "boolean",
                        "description": "ON (default): re-check each company against the vendor's public API right now, returning fresh live status, current open-job count, and sample job titles. OFF: return the bundled list as-is from our stored snapshot — instant and free of network calls, but the counts are the build-date snapshot, and each row reports live: null and verifiedAt: null (not re-checked this run) with snapshotAt set to the build date. There are no sample titles and no keyword filtering in this mode.",
                        "default": true
                    },
                    "discoverFresh": {
                        "title": "Discover new boards (slower)",
                        "type": "boolean",
                        "description": "Experimental. OFF (default): use the bundled verified list only. ON: also search the public web archive (Common Crawl) at runtime for company boards NOT already in the bundle, then verify them live. This finds newer companies but is noticeably slower, is capped, and falls back to the bundle on any failure. Requires \"Re-verify live\" on.",
                        "default": false
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
