# Greenhouse, Lever & Ashby Jobs Scraper with AI Salary & Skills (`physealabs/ats-job-enricher`) Actor

Get every open job from company career boards on Greenhouse, Lever, Ashby and Workable, with AI-extracted salary, seniority, remote policy, visa sponsorship, skills and tech stack as clean columns.

- **URL**: https://apify.com/physealabs/ats-job-enricher.md
- **Developed by:** [jay casey](https://apify.com/physealabs) (community)
- **Categories:** Jobs, Lead generation, AI
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $6.00 / 1,000 job enricheds

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

## Greenhouse, Lever & Ashby Jobs Scraper with AI Salary & Skills

Get every open job from company career boards on Greenhouse, Lever, Ashby and Workable, with salary, seniority, remote policy, visa sponsorship, years of experience, skills and tech stack pulled out of the job text into clean columns.

### What can ATS Job Enricher do?

Give it company names or career-board links. It reads the companies' public job-board APIs (no login, no proxies), filters by title, location and date, then has an LLM read each job description and fill in the fields that boards don't provide as data. You get one row per job, ready for a spreadsheet, CRM or job site.

| What you get | Features |
|---|---|
| 💰 Salary min/max, currency, period, plus the exact salary text as posted | 🏢 Greenhouse, Lever, Ashby and Workable from one input |
| 🎚️ Seniority, job function, minimum years of experience | 🔎 Bare slugs like `stripe` are tried on all four boards |
| 🌍 Remote / hybrid / onsite and allowed remote regions; visa sponsorship yes/no/unknown | 🧹 Values are validated: unknown stays `unknown`, salaries are never guessed |
| 🛠️ Must-have skills, nice-to-have skills, tech stack, responsibilities, benefits, one-line summary | ⚡ 8 jobs enriched in parallel |

### Who this is for

- Recruiters and staffing agencies building lead lists of companies hiring for a role
- Sales teams using hiring as a buying signal (e.g. "companies hiring data engineers who use Snowflake")
- Job boards and newsletters that need structured salary and remote data
- Labor-market and comp research across many companies

### Real example output

From a real run on `https://jobs.ashbyhq.com/notion` (2026-10-08), description field trimmed:

```json
{
  "company": "notion",
  "ats": "ashby",
  "title": "Software Engineer, Developer Platform",
  "location": "San Francisco, California",
  "department": "Engineering",
  "workplaceType": "Hybrid",
  "url": "https://jobs.ashbyhq.com/notion/1fc309c8-da20-4ff2-84c7-8b863ece2b0a",
  "postedAt": "2026-08-24T14:44:49.699+00:00",
  "companyBoardsOpenJobs": 133,
  "seniority": "senior",
  "remotePolicy": "hybrid",
  "salaryMin": 213000,
  "salaryMax": 320000,
  "salaryCurrency": "USD",
  "salaryPeriod": "year",
  "salaryText": "$213,000 - $320,000 per year",
  "yearsExperienceMin": 7,
  "visaSponsorship": "unknown",
  "mustHaveSkills": ["TypeScript", "Backend development", "Frontend development", "API development", "System architecture"],
  "niceToHaveSkills": ["Developer tools", "SDK development", "AI tooling", "LLM-driven products", "Evals"],
  "techStack": ["TypeScript", "APIs", "MCP tools", "LLMs"],
  "summary": "The Developer Platform engineer will build APIs, tools, and infrastructure to make Notion extensible, connected, and scalable for developers and end-users.",
  "jobFunction": "Software engineering",
  "aiEnriched": true
}
```

Same run, other boards: Stripe (Greenhouse, 728 open jobs), Palantir (Lever, 313, salary $135k-$200k extracted), Hugging Face (Workable, remote EMEA/US detected).

### Input

| Field | Required | What it does | Example |
|---|---:|---|---|
| `companies` | Yes | Board URLs or company slugs. Prefix `ashby:`, `lever:`, `greenhouse:` or `workable:` to force a board | `https://jobs.ashbyhq.com/notion`, `stripe` |
| `titleKeywords` | No | Keep titles containing any of these words | `["engineer"]` |
| `locationKeywords` | No | Keep locations containing any of these words | `["remote", "new york"]` |
| `postedWithinDays` | No | Only jobs posted in the last N days, 0 = any | `14` |
| `maxJobsPerCompany` | No | Cap per company, after filters | `25` |
| `aiEnrichment` | No | Off = raw board fields only, cheaper | `true` |
| `includeDescription` | No | Keep the full plain-text description | `true` |

```json
{
  "companies": ["https://jobs.ashbyhq.com/notion", "stripe"],
  "titleKeywords": ["engineer"],
  "maxJobsPerCompany": 10
}
```

### Pricing

Pay per event, no subscription:

- `job-enriched`: **$0.006** per job with AI fields ($6 per 1,000)
- `job-listed`: **$0.0015** per job without AI fields (AI switched off, or it failed for that job)

The sample input (2 companies, 10 jobs each) costs about **$0.12**. You can cap spend with the run's maximum charge setting; the Actor stops cleanly at the limit.

### Limits and honest notes

- Only public boards hosted on Greenhouse, Lever, Ashby and Workable. Companies on Workday, iCIMS, SmartRecruiters or their own site are not covered yet; the run reports "no public job board found" for them and charges nothing.
- A bare slug must match the company's board name (`stripe`, `notion`). If it doesn't, paste the board URL.
- AI fields come from Gemini 3.1 Flash-Lite and only reflect what the posting says. Missing data stays `null` or `unknown`; check `salaryText` against `salaryMin`/`salaryMax` when it matters.
- Ashby boards expose their own salary summary in `atsSalary` when the company publishes one.

### Use it from code or an AI agent

```bash
curl -X POST "https://api.apify.com/v2/acts/PhyseaLabs~ats-job-enricher/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"companies":["stripe","notion"],"titleKeywords":["data"],"maxJobsPerCompany":20}'
```

Also available as a tool through the Apify MCP server (`https://mcp.apify.com`).

Built by Physea Labs.

# Actor input Schema

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

Career board URLs (boards.greenhouse.io/stripe, jobs.lever.co/palantir, jobs.ashbyhq.com/notion, apply.workable.com/huggingface) or bare company slugs (stripe, notion). Slugs are tried on all four ATSs.

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

Keep jobs whose title contains any of these words. Empty = all jobs.

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

e.g. remote, new york, london. Empty = any location.

## `postedWithinDays` (type: `integer`):

0 = any date.

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

After filters.

## `aiEnrichment` (type: `boolean`):

Off = raw listings only, charged at the cheaper job-listed price.

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

Include full job description text.

## Actor input object example

```json
{
  "companies": [
    "https://jobs.ashbyhq.com/notion",
    "stripe"
  ],
  "titleKeywords": [
    "engineer"
  ],
  "locationKeywords": [],
  "postedWithinDays": 0,
  "maxJobsPerCompany": 10,
  "aiEnrichment": true,
  "includeDescription": true
}
```

# Actor output Schema

## `jobs` (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 = {
    "companies": [
        "https://jobs.ashbyhq.com/notion",
        "stripe"
    ],
    "titleKeywords": [
        "engineer"
    ],
    "maxJobsPerCompany": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("physealabs/ats-job-enricher").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": [
        "https://jobs.ashbyhq.com/notion",
        "stripe",
    ],
    "titleKeywords": ["engineer"],
    "maxJobsPerCompany": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("physealabs/ats-job-enricher").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": [
    "https://jobs.ashbyhq.com/notion",
    "stripe"
  ],
  "titleKeywords": [
    "engineer"
  ],
  "maxJobsPerCompany": 10
}' |
apify call physealabs/ats-job-enricher --silent --output-dataset

```

## MCP server setup

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

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/eU4Oo8xyoTSR3yCZF/builds/MQmczbOD3yR4WBw4k/openapi.json
