# Workday Jobs Scraper (`natanr123/workday-jobs-scraper`) Actor

Search Workday employers in our index by job title, country and more, or get every open job from your own list of Workday career sites (myworkdayjobs.com), with full descriptions, real posting dates and pay. Reads past Workday's 2,000-job limit. Monitoring mode for new and closed jobs.

- **URL**: https://apify.com/natanr123/workday-jobs-scraper.md
- **Developed by:** [Natan Rubinstein](https://apify.com/natanr123) (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.00 / 1,000 job with descriptions

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?

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 Jobs Scraper

**Search the Workday employers in our index by job title, country and more**, or paste a list of **Workday career sites** (the `myworkdayjobs.com` job search pages that thousands of large employers use) and get back **every open job**, live, with the full description, the real posting date, locations with country codes and pay ranges parsed from the description. Turn on monitoring and each run returns only the jobs that are **new or changed since the last run**, plus the ones that **closed**.

**Demo (1:26)**, filmed on Career Site Jobs Scraper, which has the same search, results format and monitoring:

https://www.youtube.com/watch?v=BRmWYOvqEhg

### Why this Actor

- **All the jobs, not the first 2,000.** Workday's own search stops at 2,000 results. This Actor splits big sites by country and job family and reads every slice, then removes the duplicates.
- **Real dates.** Workday's lists only say "Posted 30+ Days Ago". With descriptions on, every job gets its actual posting date.
- **Pay ranges.** US employers state pay in the description ("$137,100 - $301,400"); it is extracted into `salary.min`, `salary.max`, `salary.currency` and `salary.period`.
- **Monitoring built in.** Schedule it daily with *Only jobs that are new or changed* on. A job counts as closed only after it is missing twice in a row and a direct check confirms it is gone, so a slow Workday day never shows up as hundreds of "closed" jobs. A job counts as changed when its title, locations or listed details change.
- **Clean, consistent rows** in the same format as Career Site Jobs Scraper, so Workday and non-Workday employers can go in one spreadsheet.

### How to use it

**To search:** leave **Workday career sites** empty and fill in **Job title contains any of**, for example `data engineer`, plus any other filters. The run returns the most recently posted matching jobs across the Workday employers in our index (about 300, re-read every day), up to **Max results** (1,000 by default; 0 returns every match). With monitoring on, each run returns the matches that are new since the last one instead (any beyond *Max results* come in the next run).

**For your own Workday sites:**

1. Open the company's careers page and go to its job search. If the address contains `myworkdayjobs.com` (or `myworkdaysite.com/recruiting`), paste it into **Workday career sites**, one per line. For example:

   - `https://adobe.wd5.myworkdayjobs.com/external_experienced`
   - `https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite`

   To set how a company's name appears in the results, put it in front: `Adobe | https://adobe.wd5.myworkdayjobs.com/external_experienced`.
2. Optionally filter by title words, countries, workplace type or posting age, or cap the number of jobs per site.
3. Run it, or schedule it, and download the results as JSON, CSV or Excel, or use them from n8n, Make, Zapier or the Apify API.

For employers on Greenhouse, Lever, Ashby, Oracle and others, use **Career Site Jobs Scraper**, which reads those and Workday in one run.

### What you get

| Field | Example |
|---|---|
| `title` | Sr. Manager Risk & Controls |
| `location`, `locations[]`, `countryCodes` | San Jose · \[{city, region, countryCode}] · \["US"] |
| `postedAt`, `postedAtPrecision` | 2026-10-16T00:00:00Z · day |
| `employmentType` | full_time, part_time, … |
| `salary` | {min: 137100, max: 301400, currency: "USD", period: "year", source: "text"} |
| `requisitionId` | R172265 |
| `url` | the job's page on the employer's Workday site |
| `descriptionText` / `descriptionHtml` | the full description |
| `jobKey`, `boardKey`, `atsJobId` | stable identifiers for de-duplication |
| `changeType`, `firstSeenAt`, `lastSeenAt` | monitoring runs: new / updated / unchanged / closed |
| `source.fetchedAt`, `source.fromCache` | when the job was read from its career site, and whether it came from our daily index |

Each run also writes an **OUTPUT** record with the status of every career site you listed.

### Pricing

| Event | Price |
|---|---|
| Job with description | $1.50 per 1,000 jobs |
| Job without description | $0.60 per 1,000 jobs |
| Career site checked | $0.003 per site read live |

Sites taken from our daily index are not charged as checked. Closed-job rows are free. Set a maximum cost per run in the run options and the Actor stops cleanly when it is reached.

**Try it free:** Apify's free plan includes $5 of usage every month, with no credit card needed. That covers about 3,300 jobs with descriptions, or about 8,000 without.

### Speed

Workday returns 20 jobs per page and one description per request, and this Actor asks each site politely (about one request per second). A site with 300 jobs takes about 5–6 minutes with descriptions and under a minute without. Several sites run in parallel. Sites our daily index has read in the last 30 hours (*Max data age*) come from it instead, in seconds; set *Max data age* to 0 to read every site live.

### How it collects data, and the rules it keeps

- It reads only **public job postings**, from the same endpoints the employer's own career site uses. No logins.
- It **obeys robots.txt** on every request and identifies itself in its user agent.
- It **never works around a block**: no CAPTCHA solving, no proxy rotation. A site that refuses is reported in OUTPUT.
- **No personal data**: emails and phone numbers are removed from descriptions.

You are responsible for how you use the data. Respect each site's terms and the law where you work.

*This Actor is not affiliated with or endorsed by Workday, Inc. The name is used only to say which career sites it can read.*

# Actor input Schema

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

Jobs whose title contains at least one of these words or phrases (not case-sensitive), for example data engineer, product manager, nurse. With no companies listed, this searches every Workday employer in our index.

## `countries` (type: `array`):

Two-letter country codes, for example US, GB, DE, IL. Jobs whose country cannot be determined are left out when this is set.

## `workplaceTypes` (type: `array`):

Keep only these. Jobs whose career site does not say are left out when this is set.

## `employmentTypes` (type: `array`):

Keep only these. Jobs whose career site does not say are left out when this is set (a title like "Intern" counts as saying).

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

Keep only jobs posted in the last N days. Without descriptions, Workday shows only "Posted 30+ Days Ago" for older jobs, so those count as 30 days old.

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

Leave empty to search every Workday employer in our index with the filters above. Or list Workday career sites, one per line: the address of the company's job search page, for example https://adobe.wd5.myworkdayjobs.com/external_experienced or https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite. Any job page from the site works too. Short form: workday:adobe.wd5/external_experienced. Optionally put the company's name in front, separated by a bar, to control how it appears in the results: Acme Corp | https://adobe.wd5.myworkdayjobs.com/external_experienced.

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

The most jobs this run returns, all companies together (closed-job rows don't count). A search without companies returns the most recently posted matches. 0 means no limit.

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

On: every job comes with its full description, real posting date and pay parsed from the text (one extra request per job). Off: list fields only (title, location, relative posting date, requisition id), much faster on big sites.

## `descriptionFormat` (type: `string`):

Plain text keeps headings and bullet points as lines. HTML is the employer's original markup. Emails and phone numbers are removed from both.

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

Stop after this many jobs from each company. 0 means all of them.

## `onlyNewJobs` (type: `boolean`):

Remembers which jobs earlier runs returned (per monitor name, in a key-value store in your account) and outputs only jobs that are new or whose details changed. Schedule the Actor daily to get a feed of new openings.

## `monitorName` (type: `string`):

Separate monitors keep separate memories. Use a different name for each company list or search you schedule. Lowercase letters, digits and hyphens.

## `emitExistingOnFirstRun` (type: `boolean`):

On: the first run of a monitor outputs every current job. Off: the first run only remembers them, so later runs output just what is new.

## `reportClosedJobs` (type: `boolean`):

Adds a row with changeType "closed" for every job that disappeared from its career site. A job counts as closed after it is missing from two runs in a row, so a hiccup on the career site never reports open jobs as closed. Closed rows are free.

## `maxDataAgeHours` (type: `integer`):

Our index re-reads every company it covers each day. Companies it read within this many hours are taken from it: fast, and not charged as "Company checked". Other companies are read live from their career sites. 0 means always read live.

## `maxConcurrency` (type: `integer`):

How many career sites to read at the same time, up to 10: Workday's sites share its servers, so a higher number reads no faster. Each site is still asked politely, at most about one request per second.

## Actor input object example

```json
{
  "titleKeywords": [
    "data engineer"
  ],
  "countries": [
    "US"
  ],
  "maxResults": 100,
  "includeDescription": true,
  "descriptionFormat": "text",
  "maxJobsPerCompany": 0,
  "onlyNewJobs": false,
  "monitorName": "default",
  "emitExistingOnFirstRun": true,
  "reportClosedJobs": false,
  "maxDataAgeHours": 30,
  "maxConcurrency": 10
}
```

# Actor output Schema

## `jobs` (type: `string`):

One item per job (or per closed job in monitoring mode), in the dataset's Overview, Details and Changes views.

## `summary` (type: `string`):

The OUTPUT record: for every input line, its status (ok, empty, not_found, blocked, unsupported...), jobs listed and jobs output.

# 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 = {
    "titleKeywords": [
        "data engineer"
    ],
    "countries": [
        "US"
    ],
    "maxResults": 100
};

// Run the Actor and wait for it to finish
const run = await client.actor("natanr123/workday-jobs-scraper").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 = {
    "titleKeywords": ["data engineer"],
    "countries": ["US"],
    "maxResults": 100,
}

# Run the Actor and wait for it to finish
run = client.actor("natanr123/workday-jobs-scraper").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 '{
  "titleKeywords": [
    "data engineer"
  ],
  "countries": [
    "US"
  ],
  "maxResults": 100
}' |
apify call natanr123/workday-jobs-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,natanr123/workday-jobs-scraper"
        }
    }
}
```

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/A1G6jEmfTLXyndiSB/builds/8hAUGzdmSs79imyfk/openapi.json
