All platforms

Workday Jobs API

Query jobs from Workday tenants through one API — and what the Workday CXS endpoint really costs if you build the scraper yourself.

?source=workdaysource reference

Workday is the HR platform most large enterprises run on, which makes it the ATS behind a very large share of the jobs you actually want and the least pleasant one in this pool to read. Every customer is a tenant with its own host and its own career site: nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite. There is no global index, no shared API host, and no single endpoint that serves more than one company. Our queue tracks 7,586 Workday boards, and every one of them was found individually.

Try it#

BASH
curl -G https://api.hyperjobs.io/v1/jobs \
  -H "Authorization: Bearer $HYPERJOBS_KEY" \
  --data-urlencode "source=workday" \
  --data-urlencode "time_frame=7d" \
  --data-urlencode "limit=2"
Illustrative — the shape, not a live response
{
  "data": [
    {
      "id": "workday:globex_R-007754",
      "title": "Principal Firmware Engineer",
      "company": {
        "slug": "globex",
        "name": "Globex Corporation",
        "domain": "globex.com",
        "website": "https://globex.com",
        "logo": "https://globex.wd5.myworkdayjobs.com/wday/cxs/…",
        "industry": "Semiconductors",
        "employee_count": 24000
      },
      "location": {
        "remote": false,
        "countries": ["United States"],
        "cities": ["Maryville"],
        "regions": ["Tennessee"],
        "raw": ["Maryville, TN 37801, United States of America"]
      },
      "employment_type": ["FULL_TIME"],
      "work_arrangement": "On-site",
      "seniority": null,
      "salary": null,
      "skills": ["c", "rtos", "embedded"],
      "taxonomies": ["Software"],
      "apply_url": "https://globex.wd5.myworkdayjobs.com/en-US/GlobexCareers/job/Maryville/Principal-Firmware-Engineer_R-007754",
      "url": "https://globex.wd5.myworkdayjobs.com/en-US/GlobexCareers/job/Maryville/Principal-Firmware-Engineer_R-007754",
      "source": "workday",
      "posted_at": "2026-07-06T00:00:00.000Z",
      "expires_at": "2026-08-30T00:00:00.000Z",
      "updated_at": "2026-07-12T04:41:52.000Z"
    }
  ],
  "total": 2,
  "limit": 2,
  "offset": 0
}

salary being null there is not an omission — see below. Workday genuinely does not publish one.

How Workday boards actually work#

Workday's career sites are backed by a JSON API called CXS. It takes two steps, and the first one is a POST.

BASH
# 1. List — POST, paginated, no descriptions
curl -X POST "https://<host>/wday/cxs/<tenant>/<site>/jobs" \
  -H "content-type: application/json" \
  -d '{"limit":20,"offset":0,"searchText":""}'
 
# 2. Detail — GET, one per posting, this is where the description is
curl "https://<host>/wday/cxs/<tenant>/<site><externalPath>" \
  -H "accept: application/json"

Three identifiers, not one#

You cannot derive a Workday board from a company name. You need three things, and all three come out of the careers URL:

From https://nvidia.wd5.myworkdayjobs.com/en-US/NVIDIAExternalCareerSite
tenantnvidia
hostnvidia.wd5.myworkdayjobs.com — note the wd5, it varies per customer
siteNVIDIAExternalCareerSite — arbitrary, employer-chosen

The wd5 is a Workday instance number and differs between tenants; there's no rule that gets you from a company to its instance. The site name is whatever the employer typed. An optional locale segment (en-US) sits between them and has to be skipped. Miss any of the three and you have nothing — this is the "finding the board is harder than fetching it" problem in its purest form.

The list has no descriptions#

Every posting needs its own detail call. jobPostingInfo.jobDescription holds the full HTML, and it exists nowhere else. There is no content=true equivalent, no bulk mode, no feed. A tenant with 6,000 postings costs 6,000 requests plus 300 list pages, and the page size is 20.

What we do about the N+1

Two things. The list call reports the complete live key set first, so board diffing and expiry work off the cheap call. Then only postings we've never seen get a detail fetch. A re-scrape of an unchanged 6,000-job tenant costs the list pages and nothing else.

Department and workplace type only exist as facets#

This is the strangest part of the platform. Workday's job detail exposes no department field. But the list response carries facets[], and each job belongs to exactly one value of each facet. So you recover per-job department by re-querying the list once per facet value with appliedFacets and tagging every returned job's req id. Every job appears in exactly one partition, so this fetches the same job set again, partitioned — not more jobs, just more requests.

The department facet's parameter name is tenant-configurable. jobFamilyGroup is the common one, but tenants ship jobCategory, a custom field with Department in the name, or a bare jobFamily instead. Look for only the common one and those tenants come back with zero departments and no error.

The workplace-type facet (remoteType / isRemoteType / workplaceType) is exposed by a minority of tenants — roughly one in five — but where it exists it tags every job, which matters because the per-detail remoteType is carried by only about 30% of postings. Measured on one university tenant: On-Site 1,290, Hybrid 138, Remote 53.

What bites you#

Req ids are only unique within a tenant

Two different companies will both have an R-12345. A key of workday:<reqId> collides across tenants and silently overwrites one company's job with another's. The key has to be scoped — ours is workday:<tenant>_<reqId>, which is why the ids on this source look like workday:globex_R-007754.

Pagination that stops at 200 will silently truncate. The page size is 20 and the loop must run to end-of-list — a short page, or offset + limit >= total. A cap set for safety becomes a data bug: a tenant with ~6,000 postings quietly yielding 200 of them, with a 200 on every request.

hiringOrganization.name is a legal entity, not a brand. Retail, staffing and grocery tenants put a per-store or per-subsidiary name there — L001 Loblaws Inc., TJMaxx of CA, LLC, one of four different PwC LLPs. Take it at face value and one employer splinters into forty. We vote across the whole tenant: adopt a proper name only when a single non-fragmenting name dominates the tenant's postings, otherwise keep the stable slug. Lowe's Home Centers, LLC survives; TJX keeps its slug.

The req id in the URL isn't the clean one. The path-derived id carries a posting-variant suffix — R-007754-2. The detail's own jobReqId is the real one, R-007754.

Locations drop their country. The display string is Maryville, TN 37801. Workday carries the country separately in country.descriptor as United States of America. We append it — but only to the primary location, since additionalLocations[] may be in other countries entirely and appending would produce London, United States of America.

There is no structured salary. At all. Workday CXS exposes nothing. Pay appears only as free text inside the description, if it appears. We deliberately don't parse it during the scrape — that's a guess, not ground truth, and this phase only stores what the platform actually declared. So salary is null on Workday postings far more often than on Ashby or Lever, and that's the platform, not us.

A minimal request body returns zero jobs rather than an error. Get the POST body wrong and you get a valid, empty, 200-status response.

Or use ours#

Workday is the clearest case on this site for not building it yourself, and the reason isn't the parsing — it's that the work is per-tenant and never ends.

Every Workday customer is a separate discovery problem, a separate host, a separate site name, and a separate N+1. We track 7,586 of them. You get the descriptions without paying a request per posting, the tenant-scoped ids that stop Globex's R-12345 from clobbering Initech's, the org-name vote that keeps one employer as one employer, and the country appended from Workday's own structured field rather than guessed from a zip code.

Workday sits in the heavy refresh tier — attempted every third cycle rather than every hour, because the N+1 makes it expensive. A new Workday posting can take several hours to appear, and a removed one takes as long to reap. Coverage is explicit about that; don't build anything that assumes uniform latency across sources.

Quickstart · Pricing

Every source above normalises onto the same schema, with the hiring company already joined onto each posting.

Get an API key