How to export job postings from a Workday career site (myworkdayjobs.com)
Last updated: 24 October 2026 · By Quicksloth
Many large employers publish their open jobs on Workday career sites, at addresses like salesforce.wd12.myworkdayjobs.com. This guide shows two ways to get those jobs into a spreadsheet: a free Python script you run yourself, and a cloud run on Apify that adds the job details and, on a schedule, returns only the jobs that are new or changed.
Short version: each Workday career page loads its job list from a JSON address, /wday/cxs/<company>/<site>/jobs, 20 jobs per request. Read it page by page, with a pause, and save the rows as CSV.
Step 1: find the career-site address
Open the company's careers page and click any job. If the address of the job page contains myworkdayjobs.com, the part before /job/ is the career site. It has three pieces:
| Career-site address | Company (tenant) | Site |
|---|---|---|
https://salesforce.wd12.myworkdayjobs.com/External_Career_Site | salesforce | External_Career_Site |
https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite | nvidia | NVIDIAExternalCareerSite |
The wd5 or wd12 part differs from company to company: copy it as it is. A language part such as /en-US/ after the domain is not part of the site name. If the careers page is on the company's own domain, click a job: when the address changes to myworkdayjobs.com, use that one. Some companies have more than one site (for example one for students), each with its own name.
Way 1: a free Python script
The career page itself asks https://<host>/wday/cxs/<company>/<site>/jobs for its list, with a small JSON body (offset, page size and search text), and needs no login. Workday does not document this address for outside use, so it can change without notice. The script below reads every page of the list for each site, writes title, location, posting date and link to workday_jobs.csv, and first checks the site's robots.txt. It uses only Python's standard library (Python 3.8 or later).
"""Export the open jobs of Workday career sites (myworkdayjobs.com) to a CSV file."""
import csv
import json
import re
import time
import urllib.parse
import urllib.request
import urllib.robotparser
SITES = [
"https://salesforce.wd12.myworkdayjobs.com/External_Career_Site",
"https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite",
]
SEARCH_TEXT = "" # optional, for example "data engineer"
PAGE_SIZE = 20 # Workday answers HTTP 400 above 20
MAX_LISTED = 2000 # Workday stops paging at 2,000 postings per search
HEADERS = {"User-Agent": "workday-jobs-script", "Accept": "application/json", "Content-Type": "application/json"}
def split_site(address):
"""https://<tenant>.wdN.myworkdayjobs.com/<site> -> (host, tenant, site)."""
url = urllib.parse.urlparse(address)
parts = [p for p in url.path.split("/") if p]
if parts and re.fullmatch(r"[a-z]{2}(-[A-Za-z]{2,4})?", parts[0]):
parts = parts[1:] # drop a language part such as "en-US"
if not parts:
raise ValueError(f"No site name in {address}")
return url.hostname, url.hostname.split(".")[0], parts[0]
def allowed(host, path):
robots = urllib.robotparser.RobotFileParser(f"https://{host}/robots.txt")
robots.read()
return robots.can_fetch("*", f"https://{host}{path}")
def list_page(host, tenant, site, offset):
body = json.dumps({"appliedFacets": {}, "limit": PAGE_SIZE, "offset": offset, "searchText": SEARCH_TEXT}).encode()
url = f"https://{host}/wday/cxs/{tenant}/{site}/jobs"
request = urllib.request.Request(url, data=body, headers=HEADERS, method="POST")
with urllib.request.urlopen(request, timeout=60) as response:
return json.load(response)
with open("workday_jobs.csv", "w", newline="", encoding="utf-8") as out:
writer = csv.writer(out)
writer.writerow(["site", "title", "location", "posted", "url"])
for address in SITES:
host, tenant, site = split_site(address)
if not allowed(host, f"/wday/cxs/{tenant}/{site}/jobs"):
print(f"SKIPPED {tenant}/{site}: its robots.txt does not allow it")
continue
count, total, offset = 0, None, 0
while offset < MAX_LISTED:
page = list_page(host, tenant, site, offset)
if total is None:
total = page.get("total") or 0 # only the first page has it; it stops at 2000
rows = page.get("jobPostings") or []
for job in rows:
url = f"https://{host}/{site}{job['externalPath']}"
writer.writerow([f"{tenant}/{site}", job.get("title"), job.get("locationsText"), job.get("postedOn"), url])
count += 1
offset += PAGE_SIZE
if len(rows) < PAGE_SIZE or offset >= min(total, MAX_LISTED):
break
time.sleep(1) # be gentle: one page per second
note = " (Workday's 2,000-per-search cap: use SEARCH_TEXT to get the rest)" if total >= MAX_LISTED else ""
print(f"{tenant}/{site}: {count} jobs{note}")
What we got when we ran it on 24 October 2026, with the two sites above: 1,583 jobs from Salesforce and 2,000 from NVIDIA, 3,583 rows in about 6 minutes (about half of it is the one-second pause between pages). NVIDIA's site had about 2,743 open jobs that day, so the list stopped at Workday's limit of 2,000 per search (see Limits). The script printed:
salesforce/External_Career_Site: 1583 jobs
nvidia/NVIDIAExternalCareerSite: 2000 jobs (Workday's 2,000-per-search cap: use SEARCH_TEXT to get the rest)
Three rows of the CSV file:
site,title,location,posted,url
salesforce/External_Career_Site,Software Engineering SMTS,California - San Francisco,Posted Today,https://salesforce.wd12.myworkdayjobs.com/External_Career_Site/job/California---San-Francisco/Software-Engineering-SMTS_JR363487
nvidia/NVIDIAExternalCareerSite,Senior Site Reliability Engineer - Kubernetes Platform,3 Locations,Posted Today,https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite/job/US-CA-Santa-Clara/Senior-Site-Reliability-Engineer---HPC_JR2013271
To get a big site in parts, set SEARCH_TEXT to a job title or a skill: Workday's search also looks at the job text, so broad words match almost everything.
What the script does not do, to stay short: the list gives only title, the first location or a count such as "3 Locations", and a relative date such as "Posted Today" or "Posted 30+ Days Ago". Every other field (all locations, remote or hybrid, full-time or part-time, the posting date as a date, the hiring company, the description) needs one more request per job. The script also does not remember which jobs it has already seen, does not filter, and stops at the first site that fails.
Way 2: the Workday Jobs Scraper on Apify
If you would rather not keep a script running, our Workday Jobs Scraper on the Apify Store reads the same list in the cloud and covers the gaps above: it reads the details of each job (every location, work type, employment type, posting date, hiring company, apply link and, if you ask, the description), filters by search text, title, location, remote work and posting date, and has an Only new or changed jobs mode for scheduled runs. It accepts up to 1,000 career sites per run. A site that fails is reported in the run summary and the others continue.
- Open the Actor's page and start it in Apify Console (you need an Apify account; the free plan is enough to try it).
- Paste the career-site addresses in Workday career sites, one per line. Add Job title keywords and Posted in the last N days if you only care about some recent roles.
- Tick Only new or changed jobs, and give the watch list a name in History name, for example
weekly-data-roles. - Click Start once. The first run returns the matching jobs up to Max jobs in total, which the form prefills at 500 (clear it for no limit), and up to 100 jobs on Apify's free plan. With Only new or changed jobs on, the jobs left out by a limit come in the next runs.
- Schedule it: in Apify Console, click Schedules in the navigation menu, then Create new. Set how often it runs, then open Add, choose the Actor and paste the same input JSON, with the same History name (with a different one, the first scheduled run starts again from all matching jobs). New schedules start disabled: click Enable (Apify docs: Actor and task schedules, read on 13 October 2026).
This is the input we used, in JSON (the JSON view of the input form, or the body of an API call):
{
"companies": [
"https://salesforce.wd12.myworkdayjobs.com/External_Career_Site",
"https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite"
],
"titleKeywords": ["data engineer"],
"postedWithinDays": 7,
"onlyNewJobs": true,
"stateKey": "guide-test"
}
What we got on 24 October 2026, running the Actor's code (version 0.3, the one on the Apify Store) on our machine: the first run saved 6 jobs with "data engineer" in the title posted in the last 7 days (2 at Salesforce, 4 at NVIDIA). It read only the newest part of each list (460 and 480 rows): on both sites the list came newest first, so the Actor stopped where the jobs became older than 7 days. The second run, right after, saved 0. Three rows from the first run:
| Title | Company | Locations | Workplace | Posted |
|---|---|---|---|---|
| Data Engineer Senior | AI & Data | SFDC Mexico S. de R.L. de C.V. | Mexico - Mexico City | hybrid | 2026-10-19 |
| Senior Data Engineer - EDA Datacenter Analytics and Observability | NVIDIA USA | US, CA, Santa Clara; US, MA, Westford; US, TX, Austin; US, OR, Hillsboro | not given | 2026-10-23 |
| Senior Data Engineer | NVIDIA USA | US, CA, Remote; US, TX, Remote; US, CA, Santa Clara | remote | 2026-10-19 |
Each row also has the employment type, the job link and the apply link. The fields are the same as in our ATS Jobs Scraper for Greenhouse, Lever and Ashby, so the two datasets can be combined. The full list of fields is on the Actor's page.
What it costs
The Actor charges per job a run saves, plus Apify's small start charge for each run. The current prices are in the Pricing tab of the Actor's page. Sites that fail and jobs removed by your filters are not charged; with Only new or changed jobs on, a job already returned is charged again only if its title or listed location changes, or if it comes back after disappearing from the list, or if the history has forgotten it (see Limits). You can also set a maximum cost for each run in Apify Console.
Limits to know
- At most 2,000 jobs per search.
- When a site has more open jobs, Workday's list stops at 2,000, for the script and for the Actor. To get the rest, use a narrower search text (a job title), or, for monitoring, run the Actor often with Posted in the last N days and Only new or changed jobs. When the cap cuts a list, the Actor's run summary says so and gives the approximate number of jobs on the site.
- Posting dates up to 30 days back.
- Workday shows older jobs as "Posted 30+ Days Ago", so Posted in the last N days goes up to 30.
- 100 jobs per run on Apify's free plan.
- On the free plan a run saves up to 100 jobs; the run log says when this limit is reached. With Only new or changed jobs on, the jobs left out come in the next runs, as long as they are still posted.
- What counts as new or changed.
- The Actor compares the title and the location shown in the list. A change made only inside the description is not noticed, and a job with several locations counts as changed only when the number of locations changes. A job that disappears from the list and is posted again later is returned, and charged, again as new.
- The history keeps up to 10,000 jobs per career site.
- When it is full, the jobs not seen for the longest time are forgotten first; the jobs on the list the run reads are always kept. A forgotten job that comes back on the list is returned, and charged, again as new.
- Sites that do not allow it.
- The script and the Actor read each site's robots.txt and skip a site that does not allow reading it.
- No salary, department or team.
- Workday career sites do not publish them as fields. Pay written inside the description text is not extracted.
Which way to choose
- The script for a one-off export of a few sites, when title, location and link are enough. It costs nothing.
- The Actor if you need the details of each job, filters, one schema with your Greenhouse, Lever and Ashby data, only the new jobs on a schedule, or a run without your computer.
Questions and answers
- Is it allowed to read Workday career sites?
- Career sites are public pages that companies publish for job seekers, and the JSON address is what the page itself loads, without a login. Workday does not offer it as a public API, and robots.txt differs from company to company, so check it (the script does). Keep a gentle pace, and check that your use of the job texts is allowed where you are.
- Can I search jobs across all companies on Workday?
- No. Each career site is separate, and neither the script nor the Actor has a directory of companies. You start from your own list.
- Why does a site answer with an error?
- HTTP 404 or 422 usually means the company or site name in the address is wrong, or the company moved its career site: open the address in a browser and copy it again.
- What about companies on Greenhouse, Lever or Ashby?
- They have public job-board APIs: see our guide how to monitor new jobs at a list of companies, with a free script and our ATS Jobs Scraper.
Script and Actor tested by Quicksloth on 24 October 2026 on the public career sites of Salesforce and NVIDIA; job counts change every day. Apify Console names come from Apify's documentation read on 13 October 2026. Written with AI tools and checked by Quicksloth. Quicksloth is an independent developer and is not affiliated with Workday, Apify or the companies named. Workday and Apify are trademarks of their respective owners.