Workday is a cloud-only enterprise platform covering HCM, Financial Management, Payroll, Student, and Planning. Unlike traditional on-premise ERPs, Workday is multi-tenant SaaS with no self-hosted option. All integrations route through Workday's cloud APIs. Workday releases two major updates per year (R1 in March, R2 in September), each introducing new WWS versions. The current production version is WWS v45.2 (2025R2, January 2026). Capabilities depend on which modules are licensed — there are no traditional "editions."
| Property | Value |
|---|---|
| Vendor | Workday |
| System | Workday HCM / Financial Management |
| API Surface | SOAP (WWS), REST v1, RaaS, WQL, Graph API |
| Current API Version | WWS v45.2 (2025R2) / REST v1 / Graph API v1 |
| Editions Covered | All licensed modules — HCM, Financials, Payroll, Student |
| Deployment | Cloud (multi-tenant SaaS only) |
| API Docs | WWS Directory v45.2 |
| Status | GA (all API surfaces) |
Workday provides five distinct API surfaces, each suited to different integration patterns.
| API Surface | Protocol | Best For | Max Records/Request | Rate Limit | Real-time? | Bulk? |
|---|---|---|---|---|---|---|
| SOAP (WWS v45.2) | HTTPS/XML (WSDL) | Complex CRUD, payroll, financial transactions | Configurable (page/count) | ~10 calls/sec/tenant | Yes | Via pagination |
| REST API v1 | HTTPS/JSON | Worker data, staffing, modern integrations | Paginated (default varies) | ~10 calls/sec/tenant | Yes | No |
| RaaS | HTTPS/XML or JSON | Reporting, scheduled data extracts | 2 GB output, 30-min timeout | Shared with SOAP/REST | Semi (on-demand) | Yes (single report) |
| WQL | HTTPS/JSON | Ad-hoc queries, analytics | Paginated | ~10 calls/sec/tenant | Yes | Yes (paginated) |
| Graph API (2023R1+) | HTTPS/JSON | Modern read/write, mobile/web apps | Paginated | Per-tenant, 429 on exceed | Yes | No |
The SOAP API is Workday's most comprehensive integration surface with 55 named services as of v45.2, covering: Core HCM (Human_Resources, Staffing, Compensation, Benefits_Administration, Absence_Management, Talent, Performance_Management, Recruiting, Time_Tracking, Learning, Workforce_Planning, Scheduling), Financial (Financial_Management, Cash_Management, Revenue_Management, Settlement_Services, Professional_Services_Automation, Resource_Management), Payroll (Payroll, Payroll_Interface, Payroll_AUS, Payroll_CAN, Payroll_FRA, Payroll_GBR), Student (Student_Core, Student_Finance, Student_Records, and more), and Platform (Integrations, Identity_Management, Prism_Analytics, Workday_Extensibility, and others).
| Limit Type | Value | Applies To | Notes |
|---|---|---|---|
| API calls per second | ~10 per tenant | All API surfaces | Observed limit; not officially published. 429 returned when exceeded |
| Strategic Sourcing API | 5 requests/sec | Sourcing endpoints only | Documented per-endpoint limit |
| RaaS output size | 2 GB | Advanced custom reports | Maximum output for web-service-enabled reports |
| RaaS execution timeout | 30 minutes | All RaaS reports | Large reports (>50K rows) may timeout |
| SOAP response page size | Configurable | WWS SOAP requests | Set via Response_Filter element (Count + Page) |
| REST response page size | Default varies | REST API | Pagination via offset/limit parameters |
| Limit Type | Value | Window | Edition Differences |
|---|---|---|---|
| Per-tenant throughput | ~10 calls/sec (observed) | Per-second | Same across all subscriptions |
| Concurrent integrations | Tenant-dependent | Ongoing | May throttle during peak windows (payroll, year-end) |
| Graph API rate limit | Per-tenant (undisclosed) | Per-second | 429 + Retry-After header returned |
| RaaS concurrent reports | Limited | Per-tenant | Multiple large reports simultaneously causes queuing |
Workday does not publicly document precise rate limits for most endpoints. The ~10 calls/second limit is widely observed by integration partners. When limits are exceeded, the API returns HTTP 429 with a Retry-After header. During high-load periods (payroll processing, open enrollment, year-end close), effective throughput may drop significantly.
| Flow | Use When | Token Lifetime | Refresh? | Notes |
|---|---|---|---|---|
| OAuth 2.0 (Auth Code) | REST API, Graph API, WQL | ~60 min (configurable) | Yes (refresh token) | Register via "Register API Client for Integrations" |
| WS-Security (ISU) | SOAP (WWS) | Session-based | N/A | Username/password in SOAP header; ISU needs domain grants |
| OAuth 2.0 (Client Creds) | Server-to-server REST | Configurable | Yes | For non-interactive/daemon integrations |
| RaaS URL with ISU | RaaS report access | Per-request | N/A | ISU credentials in URL or basic auth header |
START -- User needs to integrate with Workday
|-- What's the integration pattern?
| |-- Real-time (individual records, <1s)
| | |-- Reading data?
| | | |-- Simple worker/org lookup -> REST API v1 or Graph API
| | | +-- Complex multi-object query -> SOAP (WWS) Get operations
| | +-- Writing data?
| | |-- Standard business process -> SOAP (WWS) Submit/Put
| | +-- Custom object data -> REST API or Workday Extend
| |-- Batch/Bulk (scheduled, high volume)
| | |-- Outbound (extracting)?
| | | |-- < 50K rows -> RaaS (XML format with pagination)
| | | |-- > 50K rows -> WQL with pagination or RaaS XML chunking
| | | +-- Need calculated fields? -> RaaS
| | +-- Inbound (loading)?
| | |-- File-based -> EIB with CSV/XML
| | |-- Record-by-record -> SOAP Put/Submit with retry
| | +-- Mass migration -> Workday migration tools
| |-- Event-driven (notifications)
| | |-- Outbound notifications -> Integration Events
| | +-- Real-time change detection -> Poll via RaaS/WQL (no native CDC)
| +-- Reporting / Analytics
| |-- Ad-hoc queries -> WQL via REST
| |-- Scheduled reports -> RaaS via EIB
| +-- Advanced analytics -> Prism Analytics API
|-- Direction?
| |-- Inbound -> SOAP WWS (Put/Submit) or EIB
| |-- Outbound -> RaaS, WQL, REST, or Graph API
| +-- Bidirectional -> separate inbound/outbound flows
+-- Error tolerance?
|-- Zero-loss -> idempotency + error logging + retry queues
+-- Best-effort -> exponential backoff on 429s
| Service | Common Operations | Method | Payload | Notes |
|---|---|---|---|---|
| Human_Resources | Get_Workers, Put_Worker, Get_Organizations | POST (SOAP) | XML | Core worker data CRUD |
| Staffing | Hire_Employee, Terminate_Employee, Change_Job | POST (SOAP) | XML | Business process operations |
| Compensation | Get_Compensation_Plans, Put_Compensation | POST (SOAP) | XML | Salary, bonus, stock data |
| Financial_Management | Get_Journals, Submit_Accounting_Journal | POST (SOAP) | XML | GL, AP, AR operations |
| Payroll | Get_Payroll_Results, Get_Payroll_Input | POST (SOAP) | XML | Payroll data extraction |
| Benefits_Administration | Get_Benefit_Plans, Enroll_In_Benefits | POST (SOAP) | XML | Benefits enrollment/changes |
| Recruiting | Get_Job_Requisitions, Get_Candidates | POST (SOAP) | XML | Requisitions and applicant data |
| Time_Tracking | Get_Time_Entries, Put_Time_Entry | POST (SOAP) | XML | Time clock and timesheet data |
| Integrations | Get_Integration_Events, Get_Integration_Systems | POST (SOAP) | XML | Integration monitoring |
| Identity_Management | Get_Workday_Account, Put_Workday_Account | POST (SOAP) | XML | User provisioning/deprovisioning |
| Resource | Method | Endpoint | Notes |
|---|---|---|---|
| Workers | GET | /ccx/api/v1/{tenant}/workers | List/search workers |
| Worker by ID | GET | /ccx/api/v1/{tenant}/workers/{id} | Single worker detail |
| Organizations | GET | /ccx/api/v1/{tenant}/organizations | Org hierarchy |
| Job Postings | GET | /ccx/api/v1/{tenant}/jobPostings | Active job postings |
| Time Off | GET | /ccx/api/v1/{tenant}/timeOffRequests | Time off requests |
Every Workday integration starts with a dedicated ISU for audit trail and security isolation. [src4]
ISU_MyIntegration) and passwordVerify: Log in as the ISU and confirm access to required data via Workday search.
Required for REST, Graph API, and WQL access. [src5]
Verify: Exchange refresh token for access token and confirm 200 response.
Exchange the refresh token for an access token. [src5]
curl -X POST "https://wd2-impl-services1.workday.com/ccx/oauth2/{tenant}/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-u "{client_id}:{client_secret}" \
-d "grant_type=refresh_token&refresh_token={refresh_token}"
Verify: Response contains access_token and token_type: Bearer.
Construct an XML envelope with WS-Security credentials. [src1, src3]
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:wd="urn:com.workday/bsvc">
<env:Header>
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/...">
<wsse:UsernameToken>
<wsse:Username>ISU_MyIntegration@{tenant}</wsse:Username>
<wsse:Password>your_password</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
</env:Header>
<env:Body>
<wd:Get_Workers_Request wd:version="v45.2">
<wd:Response_Filter>
<wd:Page>1</wd:Page>
<wd:Count>100</wd:Count>
</wd:Response_Filter>
</wd:Get_Workers_Request>
</env:Body>
</env:Envelope>
Verify: Response contains <wd:Get_Workers_Response> with worker data.
SOAP responses include total results and total pages. Loop through all pages. [src1]
page = 1
all_workers = []
while True:
# Build SOAP envelope with Response_Filter Page={page}, Count=100
response = requests.post(soap_endpoint, data=envelope, headers=headers)
root = ElementTree.fromstring(response.content)
workers = root.findall('.//{urn:com.workday/bsvc}Worker')
all_workers.extend(workers)
total_pages = int(root.find('.//{urn:com.workday/bsvc}Total_Pages').text)
if page >= total_pages:
break
page += 1
Verify: len(all_workers) matches Total_Results from first page.
Respect the ~10 calls/sec limit with exponential backoff. [src4]
def workday_request(url, headers, data, max_retries=5):
for attempt in range(max_retries):
response = requests.post(url, headers=headers, data=data)
if response.status_code == 429:
retry_after = int(response.headers.get('Retry-After', 2 ** attempt))
time.sleep(retry_after)
continue
response.raise_for_status()
return response
raise Exception(f"Max retries ({max_retries}) exceeded")
Verify: Integration completes without unhandled 429 errors during load testing.
# Input: OAuth access token, tenant name
# Output: List of active workers in JSON format
import requests
BASE_URL = "https://wd2-impl-services1.workday.com"
TENANT = "your_tenant"
ACCESS_TOKEN = "your_access_token"
headers = {
"Authorization": f"Bearer {ACCESS_TOKEN}",
"Content-Type": "application/json"
}
offset, limit, all_workers = 0, 100, []
while True:
url = f"{BASE_URL}/ccx/api/v1/{TENANT}/workers?limit={limit}&offset={offset}"
resp = requests.get(url, headers=headers)
if resp.status_code == 429:
import time; time.sleep(int(resp.headers.get("Retry-After", 2)))
continue
resp.raise_for_status()
workers = resp.json().get("data", [])
all_workers.extend(workers)
if len(workers) < limit: break
offset += limit
print(f"Retrieved {len(all_workers)} workers")
# Step 1: Get access token
curl -X POST "https://wd2-impl-services1.workday.com/ccx/oauth2/{tenant}/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-u "{client_id}:{client_secret}" \
-d "grant_type=refresh_token&refresh_token={refresh_token}"
# Step 2: Query workers
curl -X GET "https://wd2-impl-services1.workday.com/ccx/api/v1/{tenant}/workers?limit=10" \
-H "Authorization: Bearer {access_token}"
| Workday Field (SOAP) | REST API Field | Type | Transform | Gotcha |
|---|---|---|---|---|
| Worker_ID/ID | id | String | Direct | SOAP returns composite ID element; REST returns simple string |
| Legal_Name_Data/First_Name | legalName.firstName | String | Direct | SOAP nested XML vs REST dot-notation JSON |
| Position_Data/Business_Title | businessTitle | String | Direct | May differ from Job_Profile title |
| Worker_Status_Data/Active | active | Boolean | "1"/"0" to true/false | SOAP returns string; REST returns boolean |
| Organization_Data/Organization_Reference | supervisoryOrganization | Object | XML Ref to JSON object | SOAP uses WID reference; REST uses nested object |
| Effective_Date | effectiveDate | Date | YYYY-MM-DD | Returns in tenant timezone, not UTC |
| Code | Meaning | Cause | Resolution |
|---|---|---|---|
| 429 | Rate limit exceeded | Too many API calls per second per tenant | Exponential backoff with Retry-After header |
| 401 | Unauthorized | Invalid/expired credentials or token | Re-authenticate; check ISU is active; refresh OAuth token |
| 403 | Forbidden | ISU lacks domain security permissions | Grant Get/Put on security domains; activate pending changes |
| 500 | Internal Server Error | Workday server-side error (transient) | Retry with backoff; contact support if persistent |
| INVALID_REFERENCE | Object not found (SOAP) | WID or reference ID doesn't exist | Validate reference IDs before submission |
| VALIDATION_ERROR | Business rule violation (SOAP) | Data fails Workday validation | Check business process rules; review error detail |
| VERSION_ERROR | Unsupported WWS version (SOAP) | Requested version deprecated or invalid | Update to supported WWS version |
Schedule non-critical integrations outside payroll windows; implement circuit breaker pattern. [src4]Switch to XML format with Response_Filter pagination or use WQL. [src4]Always activate security changes immediately. [src4]Test with production-like data volumes; refresh sandbox regularly. [src4]Per-integration rate limiting via shared API gateway. [src4]# BAD -- JSON RaaS has no pagination; large datasets will timeout
url = f"https://{host}/ccx/service/customreport2/{tenant}/{report}?format=json"
response = requests.get(url, auth=(isu_user, isu_pass))
# This will timeout for >50K rows
# GOOD -- XML format supports Response_Filter with Page/Count
page = 1
all_rows = []
while True:
url = f"https://{host}/ccx/service/customreport2/{tenant}/{report}?format=simplexml&Page={page}&Count=1000"
response = requests.get(url, auth=(isu_user, isu_pass))
rows = parse_xml_rows(response.content)
all_rows.extend(rows)
if len(rows) < 1000: break
page += 1
# BAD -- Permission changes for one integration break all others
ISU: ISU_SharedAccount
-> Integration A, B, C (all sharing same credentials)
# GOOD -- Isolated credentials, permissions, and audit trail
ISU: ISU_HCM_Sync -> Integration A
ISU: ISU_Payroll_Extract -> Integration B
ISU: ISU_Benefits_Enroll -> Integration C
# BAD -- v37.0 will be deprecated; no upgrade path
SOAP_URL = f"https://{host}/ccx/service/{tenant}/Human_Resources/v37.0"
# GOOD -- Version is configurable and monitored
WWS_VERSION = os.environ.get("WORKDAY_WWS_VERSION", "v45.2")
SOAP_URL = f"https://{host}/ccx/service/{tenant}/Human_Resources/{WWS_VERSION}"
# Alert when version approaches deprecation (18-month window)
Use format=simplexml with Page/Count, or switch to WQL for JSON pagination. [src4]Always run 'Activate Pending Security Policy Changes' after any ISU permission modification. [src4]Load-test with production-like data volumes. [src4]Pin to specific version; plan upgrades proactively within 18-month window. [src2]Always parse Retry-After on 429 responses. [src4]Schedule large extracts during off-peak hours. [src4]# Test REST API authentication (OAuth token exchange)
curl -X POST "https://wd2-impl-services1.workday.com/ccx/oauth2/{tenant}/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-u "{client_id}:{client_secret}" \
-d "grant_type=refresh_token&refresh_token={refresh_token}"
# Check WWS WSDL for a specific service (verify version is accessible)
curl -X GET "https://wd2-impl-services1.workday.com/ccx/service/{tenant}/Human_Resources/v45.2?wsdl"
# Test RaaS report access (XML format, first page)
curl -X GET "https://wd2-impl-services1.workday.com/ccx/service/customreport2/{tenant}/{owner}/{report}?format=simplexml&Page=1&Count=5" \
-u "ISU_User@tenant:password"
# Test WQL query endpoint
curl -X POST "https://wd2-impl-services1.workday.com/ccx/api/wql/v1/{tenant}" \
-H "Authorization: Bearer {access_token}" \
-H "Content-Type: application/json" \
-d '{"query": "SELECT worker, fullName FROM allActiveWorkers LIMIT 5"}'
| API Version | Release Date | Status | Breaking Changes | Migration Notes |
|---|---|---|---|---|
| WWS v45.2 (2025R2) | 2026-01-09 | Current | None (patch) | Latest patch for 2025R2 cycle |
| WWS v45.0 (2025R2) | 2025-09-19 | Current | New services/operations | Base 2025R2 release |
| WWS v44.2 (2025R1) | 2025-07-11 | Supported | None (patch) | Final patch for 2025R1 |
| WWS v44.0 (2025R1) | 2025-03-14 | Supported | New services/operations | Base 2025R1 release |
| REST API v1 | 2019 | Current | Evolving (additive) | No breaking changes planned |
| Graph API v1 | 2023R1 | Current (GA) | N/A (first release) | Introduced 2023R1; evolving |
| WWS v37.0 | 2021-09-17 | Nearing EOL | --- | Plan upgrade to v44+ or v45+ |
Deprecation Policy: Workday supports each major WWS API version for a minimum of 18 months. Deprecation is announced through Workday Community and release notes. Backward compatibility is maintained within a version — integrations built for v45.0 continue working with v45.0 even as v46.x is released. Older versions do not receive new features.
| Use When | Don't Use When | Use Instead |
|---|---|---|
| Full CRUD on HCM data (workers, orgs, compensation) | Simple read-only report extraction | RaaS with XML pagination |
| Complex business process operations (hire, terminate) | Lightweight mobile/web data access | Graph API or REST v1 |
| Financial transactions requiring SOAP security | Bulk data migration (>100K records) | EIB file-based import or migration tools |
| Need access to all 55 WWS services | Real-time change notifications (CDC) | Integration Events + polling |
| Integration with middleware (MuleSoft, Boomi, Workato) | Direct database-level access | N/A — Workday has no DB access |