# Hiring Tech Stack Detector: Which ATS a Company Uses (`glitchbound/ats-finder`) Actor

Detect the hiring tech stack behind any company domain: Greenhouse, Lever, Ashby, Workable, Recruitee or Rippling, with the board URL and how many roles are open. One row per company, not per job, so enriching ten thousand domains costs ten thousand rows. No API key.

- **URL**: https://apify.com/glitchbound/ats-finder.md
- **Developed by:** [Daniel Meshulam](https://apify.com/glitchbound) (community)
- **Categories:** Jobs, Lead generation, Automation
- **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 postings

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

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

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Hiring Tech Stack Detector: Which ATS a Company Uses

**One row per company. Not one row per job.**

```json
{ "companies": ["stripe.com", "openai.com", "bunq.com"] }
```

```
stripe.com    greenhouse   stripe    548 open   hiring
openai.com    ashby        openai    754 open   hiring
bunq.com      recruitee    bunq       19 open   hiring
```

No API key. No proxy. No job postings you did not ask for.

### What this is for

This is a technographics question, not a jobs question, and it has a
different buyer. It is an ordinary tech-stack lookup pointed at the part of
the stack nothing else covers:

- **ATS and HR-tech vendors**: which accounts run a competitor's system
- **Recruiting-tool sellers**: a qualified list instead of a cold one
- **Anyone building on ATS APIs**: you need the board token before you can call
  anything, and there is no directory anywhere that maps a company to it
- **Hiring-signal research**: which systems are winning, by segment

Charging per company rather than per job is the point. Enriching ten thousand
domains should cost ten thousand rows, not the four million job postings behind
them.

### How a match is confirmed rather than guessed

The Actor derives candidate tokens from the domain and **verifies each against
the live API**. That is sound because these systems answer `404` for a board
that does not exist and `200` for one that exists with nothing open, so a `200`
is proof.

Measured across 30 companies:

| Method | Resolved |
|---|---|
| Reading the careers page for an ATS link | 50% |
| **Deriving the token and verifying it** | **90%** |

Reading the page fails on `stripe.com`, whose careers page is a JavaScript app
naming no board. Deriving and verifying finds it.

### Three different answers, and they are not the same answer

| Result | Means |
|---|---|
| `atsFound: true`, `openRoles: 548` | runs that system, hiring now |
| `atsFound: true`, `openRoles: 0` | runs that system, **not hiring today** |
| `atsFound: false` | no public board on any system checked |

The middle one is the one that gets collapsed into "not found" by tools that
only look for jobs. It is a real and useful state: `cato.net` runs Workable and
has nothing open right now. Reporting that as "no ATS" would be wrong.

And `atsFound: false` here is a **real negative**, not a failed request. Every
derived token was checked against the live API and none exists. The row says
which systems were checked and which tokens were tried, so you can see the
negative was earned.

### Only the ones actually hiring

```json
{ "companies": ["stripe.com", "cato.net"], "onlyIfHiring": true }
```

Companies with no board and companies with an empty board are skipped, **and
skipped companies are not charged**. A list of ten thousand domains costs only
the rows that came back.

### What you get

| Field | |
|---|---|
| `company` | what you passed in |
| `atsFound` | |
| `atsPlatform` | `greenhouse`, `lever`, `ashby`, `workable`, `recruitee`, `rippling` |
| `boardToken`, `boardUrl` | what you need to call the API yourself |
| `openRoles`, `isHiring` | |
| `checked`, `tokensTried` | on a negative, what was actually attempted |

### Notes

- **SmartRecruiters is deliberately not searchable here.** It answers the same
  empty response for a company that does not exist and for one with no open
  roles, and `/v1/companies/<id>` returns 404 even for `Visa`, whose postings
  endpoint returns real jobs. A derived token could never be confirmed, so
  guessing it would mean inventing a confident wrong answer.
- Sources are public APIs that companies publish deliberately so their openings
  get indexed. Nothing here is behind a login, a paywall or a bot wall.
- Want the postings themselves: **ATS Jobs Scraper**. Want only what changed
  since last time: **Job Change Monitor**.

# Actor input Schema

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

Company domains or names, one per line, e.g. stripe.com. One row comes back per company saying which applicant tracking system it runs, the board token, the board URL and how many roles are open. No job postings are returned and none are charged.

## `platforms` (type: `array`):

Check only these, one per line: greenhouse, lever, ashby, workable, recruitee, rippling. Leave empty to check all six. Workday, Personio and SmartRecruiters are deliberately not searchable by domain: on those three a board that does not exist does not answer 404, so a derived token can never be confirmed and a guess would be a confident wrong answer. To read those, use ATS Jobs Scraper, which takes an exact board for them.

## `onlyIfHiring` (type: `boolean`):

Skip companies with no board and companies whose board has nothing open. Skipped companies are not charged, so a list of ten thousand domains costs only the ones that came back.

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

A hard ceiling on rows for the entire run. The run stops cleanly when it is reached and nothing beyond that point is fetched or charged. Leave empty for no ceiling.

## `proxyConfiguration` (type: `object`):

Proxy settings. Every source is a public API a company publishes on purpose, and none of them block datacenter addresses, so the default is fine.

## Actor input object example

```json
{
  "companies": [
    "stripe.com",
    "figma.com",
    "monday.com"
  ],
  "onlyIfHiring": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

One row per company, naming the ATS it uses and the board found.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "companies": [
        "stripe.com",
        "figma.com",
        "monday.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("glitchbound/ats-finder").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "companies": [
        "stripe.com",
        "figma.com",
        "monday.com",
    ] }

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

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

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

```

## CLI example

```bash
echo '{
  "companies": [
    "stripe.com",
    "figma.com",
    "monday.com"
  ]
}' |
apify call glitchbound/ats-finder --silent --output-dataset

```

## MCP server setup

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

```

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/kfddlEjIoOvnzHU3x/builds/NO9Ta5ayc6do9lT1O/openapi.json
