# Workday, Greenhouse, Lever & Ashby Jobs Scraper + Delta API (`ginugokz/ats-zero-browser-gateway`) Actor

Scrape open jobs from Workday, Greenhouse, Lever and Ashby career sites, or start from a company domain and find its ATS job board. Change tracking returns only new, updated and closed jobs. No browser, no proxies.

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

## Pricing

from $1.20 / 1,000 job listings

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

## Workday, Greenhouse, Lever & Ashby Jobs Scraper + Delta API

Get every open job from company career sites running **Workday**, **Greenhouse**, **Lever** or **Ashby** — from a career-site URL or just a **company domain** like `stripe.com`. Track changes between runs and receive only **new, updated and closed jobs**. No browser, no login, no proxies.

### What you can do

- **Scrape jobs from any Workday, Greenhouse, Lever or Ashby career site** into one normalized schema.
- **Start from a company domain.** Give `stripe.com` and the Actor finds the company's job board and proves it belongs to that domain before using it.
- **Monitor hundreds of companies** on a schedule with `only_deltas: true` and receive only `ADDED`, `UPDATED` and `REMOVED` jobs.
- **Find which ATS a company uses** (`resolve_only` mode) — you pay only for domains that resolve to a verified board.

### Who uses it

- **Sales and GTM teams** — hiring signals for Clay, HubSpot or Salesforce: which accounts are hiring, for which roles, where.
- **Recruiting and staffing agencies** — new openings at target companies the day they appear.
- **Job boards and aggregators** — direct-from-employer postings with apply URLs.
- **AI agents** — live job data through the Apify MCP server.

### Supported career sites

| ATS | URL format | Example |
|---|---|---|
| Workday | `https://{company}.wd{n}.myworkdayjobs.com/{locale}/{site}` or `https://wd{n}.myworkdaysite.com/recruiting/{company}/{site}` | `https://nvidia.wd5.myworkdayjobs.com/en-US/NVIDIAExternalCareerSite` |
| Greenhouse | `https://boards.greenhouse.io/{board}` or `https://job-boards.greenhouse.io/{board}` | `https://boards.greenhouse.io/stripe` |
| Lever | `https://jobs.lever.co/{company}` | `https://jobs.lever.co/spotify` |
| Ashby | `https://jobs.ashbyhq.com/{company}` | `https://jobs.ashbyhq.com/ramp` |

**Finding the URL.** Open the company's careers page and click any job. If the address uses one of the hosts above, copy it up to the board or site name. Greenhouse boards embedded in a company page (`?for={board}`) also work.

**Don't know the URL?** Put the company domain in `domains`. The Actor finds the job board and proves it belongs to that domain before using it.

### Input

| Field | Example | Notes |
|---|---|---|
| `targets` | `["https://boards.greenhouse.io/stripe", "https://jobs.ashbyhq.com/ramp"]` | Career-site URLs. A bare board token such as `stripe` works only with `provider` set |
| `provider` | `auto` | ATS of the targets; `auto` detects it from the URL |
| `portal_url` | `https://nvidia.wd5.myworkdayjobs.com/en-US/NVIDIAExternalCareerSite` | One URL or one company domain |
| `domains` | `["stripe.com", "linear.app"]` | Company domains, resolved to their job boards |
| `mode` | `extract` or `resolve_only` | `resolve_only` returns the job board for each domain, without jobs |
| `only_deltas` | `true` | Only jobs added, updated or removed since the previous run |
| `max_jobs` | `100` | Maximum jobs per company (1–500) |
| `include_details` | `true` | Full descriptions and salary extraction |
| `full_scan` | `true` | Reads every job on the site (up to 5,000) in summary mode, without descriptions. Needed to detect closed jobs on large sites |
| `datasetId` | | Reads domains or career URLs from another Actor's dataset (filled automatically in Apify integrations) |

**Monitor a list of career sites daily**

```json
{
  "targets": ["https://boards.greenhouse.io/stripe", "https://jobs.lever.co/spotify", "https://jobs.ashbyhq.com/ramp"],
  "only_deltas": true,
  "full_scan": true
}
```

The first run returns every current job as `ADDED`; later runs return only new, edited and closed jobs.

**Start from company domains**

```json
{ "domains": ["stripe.com", "ramp.com", "linear.app"], "only_deltas": true }
```

**Only find each company's ATS**

```json
{ "mode": "resolve_only", "domains": ["stripe.com", "ramp.com", "notion.so"] }
```

### Output

**Job** (one item per job). Real item from a run on 16 October 2026; `locations` (13 entries) and `description_html` are shortened here:

```json
{
  "source_system": "workday_cxs",
  "company_tenant": "nvidia",
  "site_id": "NVIDIAExternalCareerSite",
  "requisition_id": "JR1999579",
  "title": "Senior HPC Architect, Automation and At-Scale Deployment",
  "url": "https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite/job/US-CA-Santa-Clara/Senior-HPC-Architect_JR1999579",
  "locations": ["US, CA, Santa Clara", "US, TX, Remote", "US, CO, Remote"],
  "posted_date": "2026-10-15",
  "employment_type": "Full time",
  "remote_type": null,
  "compensation_raw": "184,000 USD - 287,500 USD",
  "description_html": "<p>NVIDIA has continuously reinvented itself over two decades. …</p>",
  "delta_status": "ADDED",
  "summary_hash": "ec90f0b7cd647ca6",
  "detail_hash": "2f3177f5f974d75c",
  "scraped_at": "2026-10-16T04:50:18.026337+00:00"
}
```

`source_system` is `workday_cxs`, `greenhouse`, `lever` or `ashby`. `compensation_raw` is filled only when the posting publishes a salary; `remote_type` only when the ATS provides it. `summary_hash` and `detail_hash` fingerprint each posting for change tracking.

**Company** (`resolve_only` mode, one item per domain). Real item from the same day:

```json
{
  "domain": "stripe.com",
  "status": "RESOLVED",
  "boards": [{
    "board_key": "greenhouse:stripe",
    "provider": "greenhouse",
    "portal_url": "https://boards.greenhouse.io/stripe",
    "open_jobs": 725,
    "confidence": 0.96,
    "ownership_evidence": { "kind": "reverse_ats_payload_etld1_match", "matched_domain": "stripe.com", "evidence_score": 15 }
  }]
}
```

| `status` | Meaning | Charged |
|---|---|---|
| `RESOLVED` | A job board was found and proven to belong to the domain | Yes |
| `NOT_FOUND` | No supported job board found for the domain | No |
| `BLOCKED` | The company website blocked automated access | No |
| `AMBIGUOUS` | More than 3 candidate boards; nothing is guessed | No |
| `INVALID_DOMAIN` | The input is not a valid domain | No |

**How ownership is proven.** A board is reported only when the company's own careers page links to it, or when the board's own job data references the company's registrable domain (for example `ramp.com` never matches `liveramp.com`). A board that only shares a name with the company is never reported.

### Change tracking

| `delta_status` | Meaning |
|---|---|
| `ADDED` | New job since the previous run |
| `UPDATED` | Title, description or compensation changed |
| `REMOVED` | Job no longer listed. Reported only after a complete, consistent read of the site; if a site suddenly lists zero jobs, removal waits for a second run to confirm |
| `EXISTING` | Unchanged; skipped when `only_deltas` is `true` |

Tracking state is stored per company in a named key-value store in your own Apify account, so parallel runs on different companies never overwrite each other.

### Pricing

Pay per event. No subscription.

| Event | Price | When it is charged |
|---|---|---|
| Actor start | $0.001 | Once per run |
| Job listing | $0.0012 ($1.20 per 1,000) | Per job delivered. With `only_deltas`, only added, updated or removed jobs |
| Company resolved | $0.002 ($2 per 1,000) | `resolve_only` mode, only for domains with a verified board. Unresolved domains are free |

In `extract` mode, resolving domains is free; you pay only for the jobs you receive.

**Examples**

- 1,000 domains in `resolve_only`, 700 resolved: 700 × $0.002 + $0.001 = **$1.40**.
- 50 companies monitored daily with `only_deltas` (about 25 changes a day): about **$0.03 per day**.

### Integrations

- **Clay** — add a column with *Run Apify Actor*, or an HTTP API column calling `https://api.apify.com/v2/acts/ginugokz~ats-zero-browser-gateway/run-sync-get-dataset-items`.
- **n8n, Make, Zapier** — use the Apify app. A ready-made n8n workflow is in the [GitHub repository](https://github.com/ginugokz/ats-zero-browser-gateway).
- **Chain after any Actor** — add this Actor in another Actor's *Integrations* tab. It reads the `domain`, `website` or career-URL fields from that run's dataset automatically.
- **AI agents (MCP)** — `https://mcp.apify.com/?tools=ginugokz/ats-zero-browser-gateway`.

### Limits

- Supported: Workday (`myworkdayjobs.com`, `myworkdaysite.com`), Greenhouse, Lever, Ashby.
- Not supported: SmartRecruiters (its robots.txt disallows automated access), iCIMS, Taleo, SuccessFactors and in-house career sites.
- Domain resolution works best for technology companies. If a careers page renders its job list with JavaScript and the board name differs from the domain, the domain may return `NOT_FOUND`.
- Workday exposes at most 2,000 jobs per career site. On sites at that limit, `REMOVED` is not reported, so a job is never marked closed by mistake.

### FAQ

**Is this legal?** It reads the public job-board endpoints that the companies' own career pages use. No login is used. It returns job postings, not personal profiles, and removes recruiter emails and phone numbers from descriptions.

**Do I need proxies?** No.

**How fresh is the data?** Live: every run reads the career site at that moment.

### Changelog

- **3.7** — Company-domain resolution with ownership proof; `resolve_only` mode; per-company tracking state; Workday 2,000-job guard; reads domains from upstream datasets in integrations; SmartRecruiters removed.
- **3.6** — Workday, Greenhouse, Lever and Ashby extraction with change tracking.

# Actor input Schema

## `mode` (type: `string`):

'extract' returns jobs from career-site URLs and/or company domains. 'resolve_only' only finds each company's ATS job board from its domain (charged only when a board is verified).

## `provider` (type: `string`):

Select 'auto' to detect from URLs or company domains automatically.

## `portal_url` (type: `string`):

A Workday, Greenhouse, Lever or Ashby career-site URL, or a company domain such as stripe.com.

## `domains` (type: `array`):

Company domains such as stripe.com or linear.app. Each domain is matched to its ATS job board with ownership proof. Domains that cannot be resolved are free.

## `targets` (type: `array`):

Career-site URLs to extract in one run, e.g. https://boards.greenhouse.io/stripe or https://jobs.ashbyhq.com/ramp. A bare board token (e.g. stripe) works only with ATS Provider set.

## `datasetId` (type: `string`):

Optional dataset ID from an upstream Actor run. When configuring an Apify Integration, pass {{resource.defaultDatasetId}} or rely on automatic payload detection.

## `search` (type: `string`):

Optional keyword passed to Workday's own job search, for example 'engineer'. Ignored for Greenhouse, Lever and Ashby.

## `max_jobs` (type: `integer`):

Maximum jobs returned per company (1-500). Ignored when 'Full site scan' is on.

## `include_details` (type: `boolean`):

Fetch each job's full description and extract published salary text. Turn off for faster runs with titles, locations and URLs only.

## `only_deltas` (type: `boolean`):

Skip unchanged jobs so you only receive and pay for new, edited or closed jobs since the previous run.

## `full_scan` (type: `boolean`):

Reads every job on the site (up to 5,000) in summary mode, without descriptions. Use it with change tracking on large sites so closed jobs are detected reliably. Turns off full descriptions automatically.

## `state_store_name` (type: `string`):

Named Key-Value Store used to persist ADDED/UPDATED/REMOVED state across runs.

## Actor input object example

```json
{
  "mode": "extract",
  "provider": "auto",
  "portal_url": "https://nvidia.wd5.myworkdayjobs.com/en-US/NVIDIAExternalCareerSite",
  "domains": [],
  "targets": [],
  "datasetId": "",
  "search": "",
  "max_jobs": 40,
  "include_details": true,
  "only_deltas": false,
  "full_scan": false,
  "state_store_name": "ats-delta-state"
}
```

# 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 = {
    "portal_url": "https://nvidia.wd5.myworkdayjobs.com/en-US/NVIDIAExternalCareerSite"
};

// Run the Actor and wait for it to finish
const run = await client.actor("ginugokz/ats-zero-browser-gateway").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 = { "portal_url": "https://nvidia.wd5.myworkdayjobs.com/en-US/NVIDIAExternalCareerSite" }

# Run the Actor and wait for it to finish
run = client.actor("ginugokz/ats-zero-browser-gateway").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 '{
  "portal_url": "https://nvidia.wd5.myworkdayjobs.com/en-US/NVIDIAExternalCareerSite"
}' |
apify call ginugokz/ats-zero-browser-gateway --silent --output-dataset

```

## MCP server setup

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

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/LdEc133lvV6VWwgtc/builds/ZPf225WG2L7Ub183k/openapi.json
