# Workday Jobs Scraper: Open Jobs from Any Career Site (`tinlark/workday-jobs-scraper`) Actor

Open jobs from any company's Workday career site (myworkdayjobs.com): title, location, requisition id, posting date and link, optional full description. Filters, hiring signals and new-only monitoring. No login, no proxy.

- **URL**: https://apify.com/tinlark/workday-jobs-scraper.md
- **Developed by:** [Tinlark](https://apify.com/tinlark) (community)
- **Categories:** Jobs, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.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?

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: open jobs from any Workday career site

Paste the address of a company's Workday career site and get one row per open job: title, location, requisition id, posting date and a link to the job. Add filters, ask for full descriptions, switch to a one-row-per-company hiring summary, or schedule it to return only the jobs that are new since the last run.

Many large employers run their career pages on Workday (`<company>.wd5.myworkdayjobs.com/<site>`). Each page loads its job list from a JSON endpoint. This Actor reads that same list, politely, and returns clean records. It needs no login, no browser and no proxy.

If you need Greenhouse, Lever, Ashby and other vendors in the same run, use the multi-vendor [ATS Jobs Scraper by Tinlark](https://apify.com/tinlark/ats-jobs-hiring-signals) (see the last section before the FAQ).

### Who it is for

Recruiters and sourcers who track what large employers are hiring, sales and RevOps teams who read hiring as a buying signal, job-board and newsletter builders, and analysts of hiring trends.

### How to find a company's Workday URL

Workday addresses have three parts: the company's tenant, a data-centre number and the career-site name. Most employers publish them like this:

1. Open the company's careers page and click through to its job search.
2. Look at the address bar. If it reads `https://acme.wd5.myworkdayjobs.com/en-US/Acme_External`, you have the career site: tenant `acme`, data centre `wd5`, site `Acme_External`.
3. Copy it. The language part (`en-US`) and anything after the site name (a search or a single job) are ignored, so you can paste whatever you copied.
4. If the company has other career sites (campus, early careers, a subsidiary), repeat for each one. Each site is its own entry.

A web search for `site:myworkdayjobs.com acme` also lists the career sites of a company.

#### URL formats it accepts

| You paste | Result |
|---|---|
| `https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite` | the career site |
| `https://nvidia.wd5.myworkdayjobs.com/en-US/NVIDIAExternalCareerSite` | the same career site |
| `https://nvidia.wd5.myworkdayjobs.com/en-US/NVIDIAExternalCareerSite/job/US-CA-Santa-Clara/Senior-Engineer_JR1997186` | the same career site (not just that one job) |
| `workday:nvidia.wd5/NVIDIAExternalCareerSite` | the same, in short form: tenant, data centre and site |
| `nvidia.com` | the Actor looks for a Workday link on the company's home and careers pages, where robots.txt allows (best effort: it found one for 2 of 4 large company domains in Tinlark's tests) |
| `https://nvidia.wd5.myworkdayjobs.com` | refused: the address has no site name |
| `https://wd5.myworkdaysite.com/...` | not supported (another Workday address style) |

### What you get from Workday, and what you do not

| Field | What it holds |
|---|---|
| `title` | The job title as posted |
| `jobId` | Workday's requisition id, for example `JR1997186` or `R-277676` |
| `locations` | The location text. When a job has several locations Workday's list only says "5 Locations", and `locations` is empty until you turn on `includeDescription` |
| `postedAt` | The day the job was posted (midnight UTC), derived from "Posted Today", "Posted Yesterday" or "Posted 5 Days Ago". For "Posted 30+ Days Ago" it is empty. With `includeDescription` it is Workday's exact start date |
| `jobUrl`, `applyUrl` | The job page on the career site |
| `isRemote` | True when the location or title says remote. With `includeDescription` Workday's own remote type is used as well |
| `seniority` | A guess from the title only: `senior`, `director`, `intern` and so on |
| `descriptionText`, `employmentType`, all locations | Only with `includeDescription`: one extra page is read per returned job |
| `company` | Derived from the tenant name (`nvidia` becomes `Nvidia`). Workday does not publish a company name |

What Workday does **not** give: department or team (those fields stay empty), salary (the list has no structured pay), applicant counts, recruiter or hiring-manager names. For pay ranges, read the posting text with `includeDescription` and parse it yourself.

In a cloud run over five large career sites (5,470 jobs), 1,774 rows had no location text because the job lists several, and 2,014 had no posting date because the job is older than 30 days. For dates and full locations, turn on `includeDescription` on the shorter list that your filters leave.

### Input

| Field | What it does |
|---|---|
| `companies` (required) | Career-site URLs, `workday:tenant.wdN/site` entries or company domains |
| `mode` | `jobs` (default), `signals` (one summary row per career site) or `new-only` |
| `keywords`, `excludeKeywords` | Matched against the title, not case sensitive |
| `locations` | Text that a location must contain, for example `Germany` or `Santa Clara` |
| `remoteOnly` | Keep jobs whose location or title says remote |
| `postedWithinDays` | Keep jobs posted within N days. Jobs older than 30 days have no date in the list |
| `includeDescription` | Add the description, the exact date, all locations and the employment type |
| `maxJobsPerCompany` | Newest jobs first, default 25 |
| `maxItems` | Stop after this many billable rows, default 1,000 |
| `stateName` | A name such as `acme-watch`. Required for `new-only`. Saves what was seen so later runs report new and closed jobs |
| `emitExistingOnFirstRun`, `includeClosed` | Options for `new-only` runs |

There is no department filter, because Workday has no department to filter on.

#### Example: engineering jobs from the last week

```json
{
  "companies": ["https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite"],
  "keywords": ["engineer"],
  "excludeKeywords": ["intern"],
  "postedWithinDays": 7,
  "maxJobsPerCompany": 200
}
```

Because a filter needs every job on the site, the Actor reads the whole career site (up to 2,000 jobs, about 2 minutes) and then keeps the matches. Without filters, `maxJobsPerCompany` stops the reading early and the run is much faster.

#### Example: a daily watch for new jobs

```json
{
  "companies": ["https://adobe.wd5.myworkdayjobs.com/external_experienced",
                "https://mastercard.wd1.myworkdayjobs.com/CorporateCareers"],
  "mode": "new-only",
  "stateName": "enterprise-watch",
  "keywords": ["sales", "account"]
}
```

The first run records today's open jobs and returns none. Each later run returns only jobs that were not there before.

### Output

Results go to the default dataset (JSON, CSV, Excel, XML or the API). A run summary with counts, errors and the number of requests is saved under the `SUMMARY` key.

#### Job row (real output from a cloud run)

```json
{
  "recordType": "job",
  "atsVendor": "workday",
  "company": "Mastercard",
  "boardToken": "mastercard.wd1/CorporateCareers",
  "jobId": "R-277676",
  "title": "Sr. Technical Program Manager",
  "locations": ["Pune, India"],
  "isRemote": false,
  "seniority": "senior",
  "postedAt": "2026-10-03T00:00:00Z",
  "jobUrl": "https://mastercard.wd1.myworkdayjobs.com/CorporateCareers/job/Pune-India/Sr-Technical-Program-Manager_R-277676-1",
  "sourceUrl": "https://mastercard.wd1.myworkdayjobs.com/wday/cxs/mastercard/CorporateCareers/jobs",
  "status": "open"
}
```

#### Hiring summary (signals mode, trimmed)

```json
{
  "recordType": "signal",
  "company": "Workday",
  "boardToken": "workday.wd5/Workday",
  "openRoles": 373,
  "seniorRoles": 268,
  "remoteRoles": 4,
  "newLast7d": 99,
  "newLast30d": 256,
  "hiringSpike": true,
  "topLocations": ["USA.VA.Reston", "USA, CA, Pleasanton", "Canada, BC, Vancouver"]
}
```

On Workday `rolesByDepartment` always says "Unspecified", because there is no department. `newLast7d` and `newLast30d` count jobs by posting day, so jobs without a date are never counted as new.

### Limits and honest notes

- **Up to 2,000 jobs per career site per run.** Workday lists 20 jobs per request and the Actor reads one request per second, so 2,000 jobs take about 2 minutes. Bigger sites are cut at 2,000 and the run summary says so.
- **Some career sites are protected** and do not answer these requests. They come back as a free error row.
- **Posting dates are coarse** unless you use `includeDescription`. See the table above.
- **Workday sites on `myworkdaysite.com` are not supported**, nor are career pages that only embed a Workday widget on another domain: use the `myworkdayjobs.com` address behind them.
- **Closed jobs** are reported only against your own earlier run with the same `stateName`. If a career site suddenly returns no jobs after returning five or more, the Actor treats it as an outage and closes nothing.
- **Pace.** One request per second to each Workday host, retries with backoff on HTTP 429, 5xx and network errors, a declared `TinlarkBot` User-Agent, and robots.txt checked before each career site is read.

### Pricing

**Free during launch (until 31 October 2026).** You pay only Apify's own platform usage for your runs.

Measured platform usage: a cloud run over five large career sites (Nvidia, Adobe, Salesforce, Workday, Mastercard) returned 5,470 jobs in 105 seconds with a peak of 95 MB of memory and cost $0.033 in platform usage, about $0.006 per 1,000 jobs. A run with 25 jobs from each of three sites takes about 7 seconds. The default memory is 512 MB, and the peak in the measured runs was 62 to 137 MB.

From 1 November 2026: pay per event, $1.50 per 1,000 job rows ($0.0015 each on the Apify Free and Bronze plans; Silver $1.30, Gold $1.10 per 1,000) in `jobs` and `new-only` modes, $5.00 per 1,000 company summary rows in `signals` mode and $3.00 per 1,000 career sites found from a company domain (not charged when none is found, and not for URLs). Error rows, closed-job rows and the baseline run of a `new-only` watch are free. For example, 20 career sites with 100 jobs each is 2,000 jobs, or $3.00. Set *Maximum cost per run* to cap spending once prices are on.

### More than Workday?

This Actor is the Workday-only version of [ATS Jobs Scraper by Tinlark](https://apify.com/tinlark/ats-jobs-hiring-signals), which reads Workday, Greenhouse, Lever, Ashby, Workable, Recruitee and Personio in one run and can find a board from a company domain. Both run the same code and give the same fields. Choose this one when you only need Workday. A Greenhouse-only version is [Greenhouse Jobs Scraper](https://apify.com/tinlark/greenhouse-jobs-scraper).

### Use it from code or an AI agent

Start the Actor through the Apify API, any Apify client library or Apify's MCP server, and read the dataset items. The input and output schemas are defined, so tools can describe the fields themselves.

```bash
curl -X POST "https://api.apify.com/v2/acts/tinlark~workday-jobs-scraper/run-sync-get-dataset-items" \
  -H "Authorization: Bearer <YOUR_APIFY_TOKEN>" -H "Content-Type: application/json" \
  -d '{"companies": ["https://adobe.wd5.myworkdayjobs.com/external_experienced"], "maxJobsPerCompany": 50}'
```

### Data source, terms and your responsibility

The data comes from the public job-search endpoint of each Workday career site (`<tenant>.wd<N>.myworkdayjobs.com/wday/cxs/...`), the JSON that the career page itself loads. The Actor reads the robots.txt of the host and skips a career site whose file disallows the endpoint. It does not log in, solve challenges, use proxies or read applicant data. The postings are the employers' own advertisements and carry no personal data about applicants. The career sites belong to the employers, so check their terms and the laws that apply to you before you reuse or republish the postings. Each row carries `jobUrl` and `sourceUrl` so you can link back.

This Actor is not affiliated with Workday, Inc. or with any employer named above.

### FAQ

**How do I find the URL of a company's Workday career site?** See the first section: open the company's job search and copy the address. If it ends in `myworkdayjobs.com/<site>`, it works.

**Why is `locations` empty for some jobs?** Workday's list shows "3 Locations" instead of names for jobs with several locations. Turn on `includeDescription` to get all of them.

**Why is `postedAt` empty?** The list says "Posted 30+ Days Ago" for older jobs and gives no date. `includeDescription` adds the exact start date for the rows you keep.

**Why is `department` empty?** Workday's list does not carry it. Filter by title keywords instead.

**Is it legal to use this data? Disclaimers and legality.** The Actor reads only what employers publish on their own public career sites, through the endpoint that the career pages use, and it follows robots.txt and a slow request rate. It does not log in or get around blocks. The postings belong to the employers. You are responsible for how you use them, including the laws on unsolicited contact and on republishing content that apply to you. Tinlark is not affiliated with Workday or any employer, and this is not legal advice.

### Related Tinlark Actors

- [ATS Jobs Scraper](https://apify.com/tinlark/ats-jobs-hiring-signals): all seven vendors in one run.
- [Greenhouse Jobs Scraper](https://apify.com/tinlark/greenhouse-jobs-scraper): the same fields for Greenhouse boards.
- [Remote Jobs API](https://apify.com/tinlark/remote-jobs-feed): remote jobs from four job boards.

### Support

Something wrong or missing? Open an issue on this Actor's Issues tab with your input (the career-site URL) and what you expected.

# Actor input Schema

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

One entry per Workday career site. Paste the career-site URL (https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite) or write workday:tenant.wdN/site (workday:nvidia.wd5/NVIDIAExternalCareerSite). A company can run several career sites: add each one. A company domain such as nvidia.com also works: the Actor reads the company's home and careers pages, where robots.txt allows, to find a linked Workday career site. Career sites on myworkdaysite.com are not supported.

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

jobs: one row per open job. signals: one summary row per company (open roles, new and closed roles, hiring spike flag). new-only: only jobs not seen in earlier runs (needs a State name).

## `keywords` (type: `array`):

Keep jobs whose title, department or team contains any of these words (not case sensitive). On Workday the whole career site has to be read to filter it, up to 2,000 jobs, which takes about 2 minutes.

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

Drop jobs whose title, department or team contains any of these words.

## `locations` (type: `array`):

Keep jobs with a location containing any of these texts, for example Germany or Santa Clara. Workday's list shows '5 Locations' for jobs with several: use 'Include description text' to get and match all of them.

## `remoteOnly` (type: `boolean`):

Keep only jobs whose location or title says remote. With 'Include description text' the Actor also uses Workday's own remote type.

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

Keep jobs first published within this many days. Leave empty for no limit. Workday's list only says "Posted N days ago" up to 30 days, so older jobs have no date unless 'Include description text' is on.

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

Add the full job description as plain text, the exact start date, all locations and the employment type. Makes the output much larger and reads one extra page per returned job (about one per second).

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

Newest jobs first. Applies to jobs and new-only modes.

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

Stops the run once this many billable rows (jobs or company signals) were produced.

## `stateName` (type: `string`):

The name under which the Actor remembers what it has already seen, kept in a named storage of your account. Lowercase letters, digits and hyphens. Use the same name on every scheduled run and a different name for each watch list. Required for the new-only mode.

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

By default the first new-only run for a company only records the current jobs as a baseline and returns nothing. Turn this on to receive all current jobs the first time.

## `includeClosed` (type: `boolean`):

With a State name, add a free row (status closed) for each job that disappeared since the last run.

## Actor input object example

```json
{
  "companies": [
    "https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite",
    "workday:adobe.wd5/external_experienced",
    "https://mastercard.wd1.myworkdayjobs.com/CorporateCareers",
    "nvidia.com"
  ],
  "mode": "jobs",
  "remoteOnly": false,
  "includeDescription": false,
  "maxJobsPerCompany": 25,
  "maxItems": 1000,
  "emitExistingOnFirstRun": false,
  "includeClosed": false
}
```

# Actor output Schema

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

Job rows, newest first within each company.

## `signals` (type: `string`):

One hiring summary per company (signals mode).

## `errors` (type: `string`):

Inputs that could not be resolved to a job board.

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

Counts, errors, request totals.

# 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://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite",
        "https://adobe.wd5.myworkdayjobs.com/external_experienced",
        "https://mastercard.wd1.myworkdayjobs.com/CorporateCareers"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("tinlark/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 = { "companies": [
        "https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite",
        "https://adobe.wd5.myworkdayjobs.com/external_experienced",
        "https://mastercard.wd1.myworkdayjobs.com/CorporateCareers",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("tinlark/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 '{
  "companies": [
    "https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite",
    "https://adobe.wd5.myworkdayjobs.com/external_experienced",
    "https://mastercard.wd1.myworkdayjobs.com/CorporateCareers"
  ]
}' |
apify call tinlark/workday-jobs-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,tinlark/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/oFRRjvJgGix9qn2gb/builds/zWCwUI4cFTusUm05f/openapi.json
