# AI/ML Hiring Signals — Track Who AI Companies Are Hiring (`progressive_task/ai-ml-hiring-signals`) Actor

Tracks open roles across 98 verified AI/ML companies (Greenhouse, Lever, Ashby) and surfaces hiring signals: new roles, closed roles, hiring velocity, and first-ever hires in a function.

- **URL**: https://apify.com/progressive\_task/ai-ml-hiring-signals.md
- **Developed by:** [Basavaraj Bhandare](https://apify.com/progressive_task) (community)
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

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

## 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

## AI/ML Hiring Signals — Track Who AI Companies Are Hiring

Monitor open roles across **98 verified AI and machine-learning companies** — foundation-model labs, inference providers, AI chip startups, agent tooling, vector databases, AI coding tools and applied-AI companies — and get back more than a job list: **hiring signals**.

Every run tells you which roles are **new since last time**, which **closed**, whether a company is **expanding or contracting**, and — the one that matters most — when a company posts its **first-ever role in a function it has never hired for**.

> A research lab posting its first Account Executive is a go-to-market signal.
> A dev-tools company posting its first Developer Advocate is an adoption push.
> A startup posting its first General Counsel is preparing for enterprise deals.
>
> None of that is visible in a flat list of job postings. This Actor surfaces it.

***

### Why this instead of a generic ATS scraper

Most job scrapers ask *you* to supply the company slugs. That's the hard part of the job, handed back to the buyer.

This Actor ships **the company universe**. You pick `foundation_model` or `ai_agents` or `vector_db` and it already knows who's in that category and where their board lives — including auto-detecting the ATS for companies you add yourself.

| | Generic ATS scraper | This Actor |
|---|---|---|
| Company list | You supply slugs | 98 curated AI/ML companies, categorised |
| Unknown company | You research the ATS | Auto-detected and cached |
| Output | Job listings | Listings **+ hiring deltas & signals** |
| Classification | Raw ATS fields | Function, seniority, region, workplace, salary band, AI tech tags |
| Repeat runs | Same data again | Diff against last run |

***

### What you get

#### Job records (`outputMode: "jobs"`)

```json
{
  "company": "Anthropic",
  "company_category": "foundation_model",
  "company_stage": "late",
  "title": "Senior Research Engineer, Interpretability",
  "function": "ml_research",
  "seniority": "senior",
  "workplace": "onsite",
  "region": "US",
  "location_raw": "San Francisco, CA",
  "salary_min": 340000,
  "salary_max": 505000,
  "salary_currency": "USD",
  "salary_midpoint": 422500,
  "tech_tags": ["jax", "pytorch"],
  "is_new": true,
  "is_first_hire_for_function": false,
  "days_open": 3,
  "posted_at": "2026-09-10T14:00:00+00:00",
  "url": "https://job-boards.greenhouse.io/anthropic/jobs/4461450009",
  "ats": "greenhouse",
  "ats_slug": "anthropic"
}
```

#### Company signals (`outputMode: "signals"`)

```json
{
  "record_type": "company_signal",
  "company": "Anthropic",
  "open_roles": 214,
  "new_roles_this_run": 6,
  "closed_roles_this_run": 3,
  "net_change_30d": 18,
  "hiring_trend": "expanding",
  "gtm_signal": true,
  "first_time_functions": ["devrel"],
  "new_role_titles": ["Developer Advocate, Claude Code"],
  "roles_by_function": {"ml_research": 61, "engineering": 44, "sales": 22},
  "closed_roles": [{"title": "ML Intern, Inference", "days_open": 41}]
}
```

***

### Who uses this

- **Sales & GTM teams** — hiring is the cleanest public buying signal. A company opening its first DevRel or RevOps role has budget and a new motion. `onlySignalCompanies: true` on a daily schedule gives you a lead feed.
- **Recruiters & talent partners** — see which AI companies are actively expanding, in which functions, in which regions, at what comp.
- **VCs & analysts** — headcount velocity by category is a leading indicator. Track `net_change_30d` across `foundation_model` vs `ai_agents` over months.
- **Job seekers & job boards** — filter 98 AI companies by function, seniority, region, remote status and salary floor in one run.
- **Competitive intelligence** — watch a rival's `roles_by_function` shift and you can see their roadmap before they announce it.

***

### Input

| Field | Type | Default | Notes |
|---|---|---|---|
| `categories` | array | `["all"]` | `foundation_model`, `ai_infrastructure`, `ai_chips`, `ai_devtools`, `ai_agents`, `ai_coding`, `ai_data`, `ai_search`, `vector_db`, `ai_application`, `ai_platform`, `ai_safety`, `ai_robotics` |
| `stages` | array | `["all"]` | `early`, `growth`, `late`, `public`, `nonprofit` |
| `outputMode` | string | `jobs` | `jobs`, `signals`, `both` |
| `functions` | array | all | `ml_research`, `ml_engineering`, `engineering`, `infrastructure`, `data`, `devrel`, `security`, `product`, `design`, `sales`, `marketing`, `recruiting`, `finance`, `legal`, `operations`, `support` |
| `seniorities` | array | all | `intern` → `executive` |
| `regions` | array | all | `us`, `uk`, `eu`, `in`, `apac`, `ca`, `latam`, `mea` |
| `workplace` | string | `any` | `remote`, `hybrid`, `onsite` |
| `titleKeywords` | array | — | Match if ANY keyword appears in title or description |
| `excludeKeywords` | array | — | Drop if ANY keyword appears |
| `onlyNewRoles` | bool | `false` | Needs ≥1 previous run |
| `onlySignalCompanies` | bool | `false` | Skip unchanged boards — ideal for daily schedules |
| `minSalary` | int | — | Filters on the top of the posted band |
| `maxCompanies` | int | `60` | Caps the registry slice (your own `extraCompanies` are never dropped) |
| `extraCompanies` | array | `[]` | `[{"name":"Acme AI"}]` — ATS and slug auto-detected |

#### Example: daily GTM lead feed

```json
{
  "categories": ["foundation_model", "ai_agents", "ai_coding"],
  "outputMode": "signals",
  "onlySignalCompanies": true
}
```

#### Example: senior remote ML roles paying $250k+

```json
{
  "categories": ["all"],
  "functions": ["ml_research", "ml_engineering"],
  "seniorities": ["senior", "staff", "principal"],
  "workplace": "remote",
  "minSalary": 250000
}
```

***

### How it works

Three public, documented job-board APIs — Greenhouse, Lever and Ashby — that companies publish specifically so their listings can be embedded elsewhere. No login, no browser, no proxies, no anti-bot evasion. A full 60-company run is a few hundred JSON requests and finishes in well under a minute at 256 MB.

Unknown companies go through a slug resolver that probes candidate slugs across all three ATSes and caches the result, so the registry heals itself over time.

Cross-run state lives in the Actor's key-value store. **The first run is a baseline** — it deliberately reports zero new roles rather than flagging all 800 as new. Signals start on run two.

#### Limits, stated plainly

- Companies on Workday, SmartRecruiters or a custom careers page aren't covered. They're skipped and counted in `RUN_SUMMARY`.
- Salary is only as good as what the company publishes. Ashby exposes it most reliably; Greenhouse sometimes via metadata; Lever rarely.
- `region` and `workplace` are inferred from location and description text. Good, not perfect.
- Function and seniority are rule-based classifiers. Expect ~90% accuracy on standard titles and misses on creative ones ("Member of Technical Staff" is mapped; "Chief Vibes Officer" is not).

***

### Development

```bash
pip install -r requirements.txt

python -m tests.test_pipeline     # 101 offline assertions on parsing/enrichment/signals
python -m tests.smoke_run         # full Actor run against mocked ATS responses
python tools/verify_registry.py   # re-verify every company's ATS + slug (needs internet)
```

Deploy:

```bash
npm install -g apify-cli
apify login
apify push
```

***

### Cost

Pure JSON over HTTPS at 256 MB RAM — a 60-company run costs a fraction of one compute unit. Comfortably inside the Apify free tier for development and scheduled personal use.

# Actor input Schema

## `categories` (type: `array`):

Which slice of the AI market to track. Leave as 'all' to cover the whole registry.

## `stages` (type: `array`):

Filter the registry by company maturity.

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

'Jobs' gives one row per open role. 'Signals' gives one row per company with the hiring deltas — this is the lead-gen view. 'Both' returns each.

## `functions` (type: `array`):

Restrict to specific role families. Leave empty for all.

## `seniorities` (type: `array`):

Restrict to specific experience levels. Derived from the job title. Leave empty for all levels.

## `regions` (type: `array`):

Derived from the posting's location text.

## `workplace` (type: `string`):

Filter by where the role is performed. Inferred from the ATS field when present, otherwise from the location and description text.

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

Case-insensitive. A role matches if ANY keyword is present.

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

Case-insensitive. A role is dropped if ANY of these appears in its title or description.

## `onlyNewRoles` (type: `boolean`):

Requires at least one previous run — the first run has no baseline to compare against.

## `onlySignalCompanies` (type: `boolean`):

Skip companies whose board is unchanged. Ideal for a daily schedule feeding a Slack digest or CRM.

## `minSalary` (type: `integer`):

Keep only roles whose posted salary band tops out at or above this figure. Roles with no published salary are excluded when this is set.

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

Caps how many companies are taken from the built-in registry. Companies you list in 'Add your own companies' are always included on top of this.

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

Safety cap for very large boards. Companies with more open roles than this are truncated.

## `concurrency` (type: `integer`):

How many company boards to fetch in parallel. Higher is faster; leave at the default unless you are tuning a large run.

## `extraCompanies` (type: `array`):

Track companies outside the built-in registry. Give at least a name; the Actor auto-detects the ATS and slug. Example: \[{"name":"Acme AI"}, {"name":"Foo","ats":"greenhouse","slug":"foo"}]

## Actor input object example

```json
{
  "categories": [
    "foundation_model",
    "ai_infrastructure",
    "ai_agents"
  ],
  "stages": [
    "all"
  ],
  "outputMode": "jobs",
  "functions": [],
  "seniorities": [],
  "regions": [],
  "workplace": "any",
  "titleKeywords": [
    "llm",
    "inference"
  ],
  "excludeKeywords": [
    "intern"
  ],
  "onlyNewRoles": false,
  "onlySignalCompanies": false,
  "maxCompanies": 60,
  "maxJobsPerCompany": 200,
  "concurrency": 6,
  "extraCompanies": []
}
```

# Actor output Schema

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

Every enriched job record and company signal row produced by this run.

## `openRoles` (type: `string`):

Job records with function, seniority, workplace, region, salary band and tech tags.

## `companySignals` (type: `string`):

One row per company: new and closed roles, 30-day hiring trend, and first-ever-hire signals.

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

Counts of companies selected, boards resolved, boards fetched, and any board that failed.

# 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 = {
    "categories": [
        "foundation_model",
        "ai_infrastructure",
        "ai_agents"
    ],
    "titleKeywords": [
        "llm",
        "inference"
    ],
    "excludeKeywords": [
        "intern"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("progressive_task/ai-ml-hiring-signals").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 = {
    "categories": [
        "foundation_model",
        "ai_infrastructure",
        "ai_agents",
    ],
    "titleKeywords": [
        "llm",
        "inference",
    ],
    "excludeKeywords": ["intern"],
}

# Run the Actor and wait for it to finish
run = client.actor("progressive_task/ai-ml-hiring-signals").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 '{
  "categories": [
    "foundation_model",
    "ai_infrastructure",
    "ai_agents"
  ],
  "titleKeywords": [
    "llm",
    "inference"
  ],
  "excludeKeywords": [
    "intern"
  ]
}' |
apify call progressive_task/ai-ml-hiring-signals --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,progressive_task/ai-ml-hiring-signals"
        }
    }
}
```

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/6DxtzHsjABDEGaetA/builds/JnX70G3wmKKu9o686/openapi.json
