---
title: "SuccessFactors to Workday Migration: The CTO's Technical Guide"
slug: successfactors-to-workday-migration-the-ctos-technical-guide
date: 2026-04-21
author: Raaj
categories: [Migration Guide, Workday, SuccessFactors]
excerpt: "A CTO-level guide to migrating data from SAP SuccessFactors to Workday — covering API constraints, entity mapping, EIB limits, and edge cases that break migrations."
tldr: SuccessFactors-to-Workday migration requires API-based extraction via OData v2 and loading via Workday Web Services. EIB caps at 300 MB. Plan for 8–16 weeks at enterprise scale.
canonical: https://clonepartner.com/blog/successfactors-to-workday-migration-the-ctos-technical-guide/
---

# SuccessFactors to Workday Migration: The CTO's Technical Guide


Migrating from SAP SuccessFactors to Workday is a data-model translation and API-orchestration problem. SuccessFactors stores employee data across deeply nested, effective-dated OData entities — `PerPerson`, `EmpEmployment`, `EmpJob`, `EmpCompensation` — connected through compound keys and navigation properties. Workday uses a unified, object-oriented data model built around `Worker`, `Position`, `Organization`, and `Compensation` business objects with its own effective-dating system and strict inbound data constraints. Every architectural mismatch between these two systems is where employee records, payroll history, and org structure silently break.

If you need a fast decision: **Workday's EIB (Enterprise Interface Builder) spreadsheet imports work for foundation data but cap at 300 MB via SFTP and cannot handle complex relational hierarchies.** API-based extraction via SuccessFactors OData v2 or the CompoundEmployee API, combined with Workday Web Services (SOAP) or REST API for loading, is the only path that preserves full-fidelity historical data at enterprise scale. ([help.sap.com](https://help.sap.com/docs/successfactors-employee-central/employee-central-compound-employee-api/introduction-to-compoundemployee-api?utm_source=openai)) iPaaS tools like Boomi and Workato accelerate the plumbing but don't eliminate the data-mapping problem.

This guide covers the real API constraints on both sides, object-by-object mapping, every viable migration method with trade-offs, and the edge cases that cause silent data corruption.

For a broader HRIS migration framework, see [The Ultimate HRIS Data Migration Checklist](https://clonepartner.com/blog/blog/hris-data-migration-checklist/). If you're handling sensitive payroll data, review [How to Safely Migrate Sensitive Employee & Payroll Data](https://clonepartner.com/blog/blog/payroll-data-migration-security-compliance/). For patterns specific to ATS-to-Workday moves, see [Taleo to Workday Migration: The CTO's Technical Guide](https://clonepartner.com/blog/blog/taleo-to-workday-migration-the-ctos-technical-guide/).

## The Architecture Gap: SuccessFactors vs. Workday

**SAP SuccessFactors** is a modular HCM suite. Employee Central — its core HR module — stores data across multiple entity types connected via OData navigation properties. The root entity is `PerPerson`, which branches into `PerPersonal`, `PerEmail`, `PerPhone`, and `EmpEmployment`. Employment records expand further into `EmpJob`, `EmpCompensation`, and country-specific entities like `PerGlobalInfoUSA`.

The data model is effective-dated — every change to a field creates a new record with a `startDate`. Querying historical data requires date-range filters (`fromDate` / `toDate`) across multiple entity levels. Custom extensions use SAP's **Metadata Framework (MDF)**, which allows free-form entity creation but produces non-standard API shapes that require per-tenant discovery. SuccessFactors also exposes the **CompoundEmployee API**, which returns full employee payloads with delta handling — useful for broad extraction but with its own performance constraints. ([help.sap.com](https://help.sap.com/docs/successfactors-employee-central/employee-central-compound-employee-api/introduction-to-compoundemployee-api?utm_source=openai))

**Workday** is a cloud-native, object-oriented platform where the `Worker` business object is the central node. Workers connect to `Position`, `Supervisory Organization`, `Compensation`, and `Benefits` objects. Workday's data model is unified — HCM, payroll, and finance share a single schema — and effective dating is a first-class concept baked into every object. Workday positions EIB for simple-to-moderately complex import/export work, while Studio is the step up for custom logic and larger batch processing. ([forms.workday.com](https://forms.workday.com/content/dam/web/en-us/documents/whitepapers/it/workday-integration-on-demand-whitepaper.pdf?refCamp=7014X000002uVXB))

The key architectural differences that create migration friction:

| Dimension | SuccessFactors | Workday |
|---|---|---|
| **Data model** | Entity-relationship via OData navigation properties | Object-oriented business objects |
| **Employee record root** | `PerPerson` → `EmpEmployment` → `EmpJob` | `Worker` (unified) |
| **Custom extensions** | MDF entities (highly flexible) | Custom objects (more constrained) |
| **Org structure** | Foundation Objects (`FODepartment`, `FODivision`) | Supervisory Organizations, Cost Centers, Custom Orgs |
| **Effective dating** | Per-entity `startDate` with date-range queries | Native to all business objects |
| **Primary API** | OData v2 (REST), CompoundEmployee (SOAP), legacy SFAPI | SOAP (WWS), REST, Graph API |
| **Compound keys** | `personIdExternal` + `startDate` + context | Worker ID (WID) |

This structural mismatch means a "lift and shift" is impossible. Every SuccessFactors entity chain must be decomposed, transformed, and reassembled into Workday's object graph.

## Data Mapping: SuccessFactors Entities to Workday Objects

The mapping layer is where migrations succeed or fail. SuccessFactors splits what Workday treats as a single `Worker` record across multiple effective-dated entities.

### Core Object Mapping

| SuccessFactors Entity | Workday Object | Notes |
|---|---|---|
| `PerPerson` | `Worker` (biographical data) | Map `personIdExternal` → Worker Reference ID |
| `PerPersonal` | `Worker` (legal name, gender, DOB) | Country-specific name formats require transformation |
| `EmpEmployment` | `Worker` (employment record) | Map `userId` → Employee ID; handle multiple employments |
| `EmpJob` | `Position`, `Job Profile`, `Supervisory Org` | Most complex mapping — decomposes into 3+ Workday objects |
| `EmpCompensation` | `Compensation` (plan, grade, step) | Comp plans require Workday-side configuration first |
| `FODepartment` | `Supervisory Organization` or `Cost Center` | No 1:1 equivalent; requires architectural decision |
| `FOJobCode` | `Job Profile` | Map job codes to Workday job profiles |
| `FOLocation` | `Location` | Generally straightforward |
| `FOCompany` / `FOLegalEntity` | `Company` | Legal entity structure must match |
| `PerPhone`, `PerEmail`, `PerAddress` | Worker sub-objects (contact info) | Multiple addresses need type mapping |
| `PerPersonRelationship` | Dependents / Emergency contacts | Compliance-sensitive data |
| MDF custom entities | Custom Objects, Worktags, or archive | Workday custom objects are more constrained than MDF |

### Field-Level Mapping Sample

| SuccessFactors Field | Type | Workday Field | Transformation |
|---|---|---|---|
| `PerPerson.personIdExternal` | String | `Worker.Employee_ID` | Direct map; preserve crosswalk |
| `PerPersonal.firstName` | String | `Worker.Legal_Name.First_Name` | None |
| `PerPersonal.lastName` | String | `Worker.Legal_Name.Last_Name` | None |
| `EmpJob.department` | Ref → `FODepartment` | `Worker.Supervisory_Organization` | Lookup via reference table |
| `EmpJob.jobCode` | Ref → `FOJobCode` | `Worker.Job_Profile_Reference` | Map SF job code → WD job profile ID |
| `EmpJob.managerId` | String | `Position.Manager_Reference` | Resolve via Worker ID crosswalk |
| `EmpCompensation.payGroup` | String | `Compensation.Pay_Group_Reference` | Enum mapping required |
| `EmpJob.fte` | Decimal | `Position.FTE` | Direct map, validate range |
| `EmpEmployment.startDate` | Date | `Worker.Hire_Date` | Format: YYYY-MM-DD; normalize timezone |
| Picklist values (any) | Enum | Workday enums | Per-picklist mapping table required |

> [!WARNING]
> **MDF custom entities don't map cleanly.** SuccessFactors MDF allows arbitrary entity creation with custom fields and relationships. Workday's custom objects are more tightly controlled — they exist within the object model but can't replicate the full flexibility of MDF. Audit every MDF entity and decide: does this become a custom object, a worktag, a calculated field, or does the data stay in an archive?

### Handling Relationships and Dependencies

The load order matters. Workday enforces referential integrity at write time:

1. **Foundation data first**: Companies, Locations, Job Profiles, Supervisory Organizations
2. **Workers second**: Biographical and employment data
3. **Positions third**: Assigned to Workers, tied to Orgs
4. **Compensation and benefits last**: Dependent on Worker + Position + Plan configuration

Attempting to load a Worker referencing a Supervisory Organization that doesn't yet exist will fail silently or throw a validation error depending on the import method.

## API Constraints: Rate Limits, Token Expiry, and Pagination

Both platforms impose constraints that directly impact migration throughput and reliability.

### SuccessFactors OData API

- **Page size**: Maximum 1,000 records per response; `customPageSize` must be below 1,000. SAP recommends keeping page sizes at or below 500 for complex queries. ([help.sap.com](https://help.sap.com/doc/a7c08a422cc14e1eaaffee83610a981d/latest/en-US/SF_HCM_OData_API_DEV.pdf))
- **Pagination**: Supports client-side (`$top` / `$skip`), server-side snapshot-based, and cursor-based modes. Snapshot pagination is faster and avoids data loss from concurrent edits but cannot be combined with `$top` or `$skip`. Cursor pagination works on specific entities and has limits such as no sorting and weak filter support on MDF-based entities. ([help.sap.com](https://help.sap.com/docs/successfactors-platform/sap-successfactors-api-reference-guide-odata-v2/snapshot-based-pagination))
- **Rate limiting**: SAP postponed the introduction of hard rate limits indefinitely, but dynamic throttling still occurs based on server capacity. When triggered, the API returns HTTP 429 with a recommended retry of 300 seconds (5 minutes). ([userapps.support.sap.com](https://userapps.support.sap.com/sap/support/knowledge/en/3159263))
- **`$expand` limits**: Expanding deeply nested navigations (e.g., `PerPerson` → `employmentNav` → `jobInfoNav` → `departmentNav`) is resource-intensive and may be throttled or time out.
- **`lastModifiedDateTime` behavior**: Filters on effective-dated entities can change behavior depending on whether you combine them with `fromDate` or `toDate`. This matters for delta extraction correctness. ([help.sap.com](https://help.sap.com/docs/successfactors-platform/sap-successfactors-api-reference-guide-odata-v2/lastmodifieddatetime-and-filter?locale=en-us))
- **CompoundEmployee API**: Useful for broad employee extraction with delta semantics. SAP advises against large parallel query volume and says delta calls with `last_modified_on` should not run more often than every 15 minutes. ([help.sap.com](https://help.sap.com/docs/successfactors-employee-central/employee-central-compound-employee-api/performance-recommendations-for-compoundemployee-api?utm_source=openai))
- **Authentication**: OAuth 2.0 via SAML Bearer Assertion or X.509. Basic Auth is deprecated and will be removed in November 2026.

> [!NOTE]
> **Best practice for extraction**: Use `EmpEmployment` as the root entity, filter on `lastModifiedDateTime`, expand to `personNav`, `jobInfoNav`, and `compensationNav`, and paginate via server-side snapshot pagination. This avoids the over-fetching problem of querying `PerPerson` and expanding everything.

### Workday API

- **Rate limits**: Workday does not publish hard API rate limits publicly. Standard tenants are commonly reported to receive 5,000–10,000 API calls per hour, though exact limits vary by tenant size and subscription tier. Tune concurrency empirically rather than relying on a single number. ([stitchflow.com](https://www.stitchflow.com/user-management/workday/api))
- **Throttling behavior**: Returns HTTP 429 with a `Retry-After` header. Multiple systems hitting Workday during payroll processing windows can collectively exhaust the shared rate limit.
- **OAuth tokens**: Access tokens expire after approximately 1 hour. Workday API clients can be configured with short-lived access tokens and either expiring or non-expiring refresh tokens. Long-running batch migration jobs will fail mid-execution if token refresh logic isn't explicitly implemented. ([help.okta.com](https://help.okta.com/wf/en-us/content/topics/workflows/connector-reference/workday/overviews/authorization.htm))
- **SOAP vs REST**: Workday's SOAP API (Web Services) covers the broadest functionality — payroll, compensation, staffing operations — but XML payloads are significantly larger and slower. Developers report SOAP response times of 800–1,200ms for single-worker lookups. REST endpoints return JSON and are faster but don't yet cover all business operations.
- **EIB limits**: Inbound EIB uploads via SFTP cap at 300 MB. Browser uploads are limited to 30 MB. XSLT transformations are terminated after 2 hours. HTTP requests (including Web Services and RaaS) are terminated after 6 hours.
- **Two-gate auth**: The API client needs the right functional-area scopes, and the Integration System User (ISU) or security group needs the right domain permissions. If either side is missing, the call fails even when the credentials are valid. ([docs.celonis.com](https://docs.celonis.com/en/connecting-to-workday.html))
- **Environment drift**: Custom fields configured in sandbox may not match production, causing integrations that pass testing to fail on go-live.

> [!CAUTION]
> **The ~1-hour token expiry is the #1 cause of failed batch loads.** If your migration script runs for longer than 60 minutes without refreshing the OAuth token, every subsequent API call returns 401. Implement proactive token refresh — don't wait for the failure.

## Migration Approaches: EIB, API, iPaaS, Studio, or Managed Service?

Five viable methods exist. The right choice depends on data volume, relationship complexity, engineering bandwidth, and whether this is a one-time migration or an ongoing sync.

### 1. Workday EIB Spreadsheet Import

**How it works**: Export data from SuccessFactors (via OData API or reports), transform into Workday's XML Spreadsheet 2003 format, upload via EIB inbound integration.

**When to use**: Foundation data (locations, departments, job profiles), low-volume one-time loads, or pilot migrations.

| Pros | Cons |
|---|---|
| No coding required | 300 MB file size limit via SFTP; 30 MB via browser |
| Built into Workday — no external tools | XML Spreadsheet 2003 format is rigid |
| Supports validation-only mode | Cannot handle complex relational chains in one pass |
| Good for reference data seeding | Requires strict load ordering; cryptic error messages |

Workday positions EIB as a no-code tool for simple-to-moderately complex integrations, not as the universal answer for high-complexity migration programs. ([forms.workday.com](https://forms.workday.com/content/dam/web/en-us/documents/whitepapers/it/workday-integration-on-demand-whitepaper.pdf?refCamp=7014X000002uVXB))

### 2. API-to-API Custom Pipeline

**How it works**: Extract via SuccessFactors OData v2 or CompoundEmployee API → transform in a custom pipeline (Python, Node.js, or Java) → load via Workday SOAP Web Services or REST API.

**When to use**: Enterprise-scale migrations with complex relational data, historical records, custom MDF entities, or payroll continuity requirements.

| Pros | Cons |
|---|---|
| Full control over mapping and transformation | Requires deep knowledge of both APIs |
| Handles complex relationships and effective-dated history | Must engineer token refresh, retry, pagination |
| Scriptable, repeatable, auditable | 4–12 weeks of engineering effort |
| No file size limits | Ongoing maintenance as APIs version |
| Best path for delta replay and staged cutover | |

This is the method most CTOs should assume by default once historical data, payroll, or custom objects are in scope. SAP recommends using `last_modified_on` for CompoundEmployee deltas and warns against excessive parallel queries. ([help.sap.com](https://help.sap.com/docs/successfactors-employee-central/employee-central-compound-employee-api/performance-recommendations-for-compoundemployee-api?utm_source=openai))

### 3. iPaaS Middleware (Boomi, Workato, MuleSoft)

**How it works**: Use pre-built connectors for SuccessFactors and Workday within an integration platform. Map fields visually, configure transforms, schedule execution.

**When to use**: Organizations that already have iPaaS licensing, need ongoing sync between systems, or want a visual mapping interface.

| Pros | Cons |
|---|---|
| Pre-built connectors reduce setup time | Licensing cost (per-task or per-connection pricing) |
| Visual mapping and monitoring | Connectors abstract away details you may need |
| Error handling and retry built in | Historical bulk migration can hit platform throughput limits |
| Good for ongoing sync post-migration | Complex MDF entities may require custom scripting anyway |

Boomi provides both a [SuccessFactors Partner connector](https://help.boomi.com/docs/atomsphere/integration/connectors/r-atm-successfactors_connector_93ec6fae-8624-466e-b309-728de281d56a/) and a [Workday connector](https://help.boomi.com/docs/Atomsphere/Integration/Connectors/r-atm-Workday_Connector_08c9018b-3103-419d-ac03-fc260503b78e) with OAuth 2.0 support. Workato offers event-trigger-based recipes for real-time sync. Both handle authentication and retry logic natively, but bulk data throughput for a one-time historical load may still bottleneck at the iPaaS layer. ([discover.boomi.com](https://discover.boomi.com/solutions/connect-workday-to-sap-successfactors-and-servicenow/))

### 4. Workday Studio

**How it works**: Build integrations within Workday's own Studio IDE using XML/XSLT transformations, connectors to external sources, and Workday Web Services operations.

**When to use**: When the migration team includes certified Workday integration specialists and the target-side logic is complex.

| Pros | Cons |
|---|---|
| Runs natively on Workday infrastructure | Requires certified Workday Studio expertise |
| Deep access to Workday business objects | Limited to Workday's transformation language (XSLT) |
| Scheduling and monitoring built in | Harder to debug than general-purpose code |

### 5. Managed Migration Service

**How it works**: A specialist team handles extraction, mapping, transformation, loading, validation, and cutover.

**When to use**: When internal engineering bandwidth is limited, data complexity is high, or you need to guarantee zero downtime and payroll continuity.

| Pros | Cons |
|---|---|
| Fastest time-to-completion | External cost |
| Expertise in edge cases and undocumented limits | Requires trust in vendor |
| Handles token management, retry logic, rate limits | Less internal knowledge transfer |
| Zero-downtime execution available | |

A public Mercans case study on a 600,000-employee SuccessFactors-to-Workday transition underscores the operational side: even at that scale, success depended on acceptance testing, parallel runs, and a 90-day payroll transition window. ([mercans.com](https://mercans.com/resources/success-stories/transition-to-workday?utm_source=openai))

### Comparison Matrix

| Method | Complexity | Best For | Volume Ceiling | Relationship Handling | Ongoing Sync |
|---|---|---|---|---|---|
| EIB Import | Low–Medium | Foundation data, small orgs | 300 MB | Poor | No |
| Custom API Pipeline | High | Enterprise, full historical | Unlimited* | Excellent | Possible |
| iPaaS (Boomi/Workato) | Medium–High | Mid-market, ongoing sync | Platform-dependent | Good | Yes |
| Workday Studio | High | Workday-centric teams | Unlimited* | Good | Yes |
| Managed Service | Low (client) | Enterprise, tight deadlines | Unlimited* | Excellent | Possible |

\* Subject to API rate limits on both sides.

### Scenario Recommendations

- **< 500 employees, simple org structure**: EIB import + manual validation. 1–2 weeks.
- **500–5,000 employees, standard HR data**: iPaaS with pre-built connectors. 4–8 weeks.
- **5,000+ employees, multi-country, custom MDF entities, historical payroll**: Custom API pipeline or managed migration service. 8–16 weeks.
- **Ongoing coexistence (running both systems in parallel)**: iPaaS for real-time sync, with a separate one-time historical load via API.

## The Risks of DIY Migration at Scale

Building the extraction and loading scripts is the easy part. The hard part is handling every edge case that shows up between row 1 and row 500,000.

**Token expiry mid-batch**: Your Workday OAuth token expires after ~1 hour. A 3-hour batch job fails at the 60-minute mark. If you haven't implemented proactive token refresh with retry logic, you get a partial load with no rollback.

**SOAP payload performance**: Workday SOAP payloads carry significant XML overhead. Response times of 800–1,200ms for single-worker lookups are typical. Multiply by 50,000 workers and your migration runs for days unless you implement parallelism — which then risks hitting rate limits.

**Sandbox-production drift**: Custom fields, security groups, and integration configurations in Workday sandbox frequently don't match production. An integration that passes sandbox testing can fail on go-live because a custom field name differs or an ISU has different permissions.

**Partial update corruption**: Workday allows partial updates. If your load script fails mid-record — updating a Worker's compensation but not their position — you end up with data in an inconsistent state that's hard to detect and harder to fix.

**Picklist mismatches**: SuccessFactors picklists (country codes, employment types, pay frequencies) don't map 1:1 to Workday enums. Every picklist field requires a transformation table. Miss one value and the load errors or — worse — silently defaults to a wrong value.

**Permission bugs disguised as data bugs**: In Workday, missing API scopes and missing domain permissions can produce identical-looking errors at first. You may spend hours debugging a "data error" that's actually a security configuration issue.

The hidden cost is engineering time. A custom Workday integration typically takes 4–12 weeks to build, not including ongoing maintenance as Workday releases biannual API updates.

For a detailed breakdown of build vs. buy trade-offs, see [In-House vs. Outsourced Data Migration: A Realistic Cost & Risk Analysis](https://clonepartner.com/blog/blog/in-house-vs-outsourced-data-migration/).

## When a Managed Migration Service Makes Sense

DIY makes sense when you have deep Workday integration expertise on staff, a small dataset, and a generous timeline. For everyone else, the math favors a specialist.

Don't build this in-house if payroll cutover is in scope, your tenant has years of effective-dated history, custom MDF objects matter, multiple regions need separate dry runs, or nobody on your team owns Workday integration security full time. The hidden cost isn't just script writing — it's data audit, auth setup, dependency ordering, retry design, UAT, delta replay, rollback planning, and business coordination.

**ClonePartner** eliminates the engineering overhead that causes most migrations to slip. What we bring to SuccessFactors-to-Workday migrations:

- **Pre-built token management**: Automated OAuth refresh logic that handles Workday's ~1-hour token expiry and SuccessFactors' dynamic throttling without manual intervention.
- **Intelligent batching**: Adaptive request pacing that stays within both platforms' rate limits while maximizing throughput.
- **Entity-level mapping expertise**: Direct experience translating SuccessFactors' `PerPerson` → `EmpEmployment` → `EmpJob` chains into Workday's `Worker` → `Position` → `Organization` graph.
- **Custom object handling**: Assessment and migration of MDF entities into Workday custom objects, worktags, or archival storage.
- **Zero-downtime execution**: Parallel testing against production-equivalent data ensures payroll and HR operations are uninterrupted during cutover.

The value isn't that a service replaces engineering. The value is that the engineering is already organized around migration failure modes.

## Pre-Migration Planning

Before writing a single line of migration code, complete this groundwork.

### Data Audit

- [ ] Export full entity counts from SuccessFactors: `PerPerson`, `EmpEmployment`, `EmpJob`, `EmpCompensation`, `FODepartment`, `FOLocation`, `FOCompany`, `FOJobCode`
- [ ] Identify and count MDF custom entities
- [ ] Profile data quality: nulls, duplicates, orphan records, invalid picklist values
- [ ] Determine date range for historical data (all-time vs. last N years)
- [ ] Inventory attachments and documents (employee files, offer letters, contracts)
- [ ] Map SuccessFactors picklist values to Workday enums for every picklist field

### Scope Definition

- **What goes**: Active employees, recent terminations, org structure, comp data, core HR
- **What stays behind**: Archived records older than retention requirements, unused MDF entities, legacy integration configurations
- **What gets cleaned**: Duplicate records, inconsistent manager hierarchies, invalid email formats

### Cutover Strategy

| Strategy | Description | Best For |
|---|---|---|
| **Big bang** | All data migrated in a single cutover window | Small-to-mid orgs, clean data |
| **Phased** | Migrate by module or region (HR first, then Payroll, then Benefits) | Large orgs, complex dependencies |
| **Incremental** | Foundation data first, then delta loads over weeks | Organizations running both systems in parallel |

### Risk Mitigation

- Run at least **2 full test migrations** in Workday sandbox before production cutover
- Maintain a **complete SuccessFactors backup** (API export + report snapshots) before any production load
- Define **rollback criteria**: what record-count variance or validation-failure rate triggers a stop — decided before go-live, not during
- Schedule cutover during a **payroll dead zone** — not during a pay run

For a broader pre-flight list, use [The Ultimate HRIS Data Migration Checklist](https://clonepartner.com/blog/blog/hris-data-migration-checklist/). If payroll or personal data is involved, layer in [security and compliance controls](https://clonepartner.com/blog/blog/payroll-data-migration-security-compliance/).

## Migration Architecture: Extract → Transform → Load

Do not stream source records directly from SuccessFactors into Workday. Land raw source payloads first, then build a canonical model, then load Workday in dependency order. This gives you replay capability, auditability, and clean diffing between test runs.

### Step 1: Extract from SuccessFactors

Use the OData v2 API with server-side pagination for narrow entity reads, or the CompoundEmployee API for broad employee payloads with delta semantics. Choose based on whether you need targeted entity control or full employee snapshots.

```python
import requests
import time

SF_BASE = "https://{tenant}.successfactors.com/odata/v2"
HEADERS = {"Authorization": f"Bearer {access_token}", "Accept": "application/json"}

def extract_entity(entity, select_fields, expand_nav=None, page_size=500):
    """Extract all records from a SuccessFactors OData entity with pagination."""
    url = f"{SF_BASE}/{entity}?$top={page_size}&$select={select_fields}"
    if expand_nav:
        url += f"&$expand={expand_nav}"
    
    all_records = []
    while url:
        response = requests.get(url, headers=HEADERS)
        if response.status_code == 429:
            retry_after = int(response.headers.get("Retry-After", 300))
            time.sleep(retry_after)
            continue
        response.raise_for_status()
        data = response.json()
        all_records.extend(data.get("d", {}).get("results", []))
        url = data.get("d", {}).get("__next", None)  # server-side pagination
    return all_records

# Example: extract employment + job info
employments = extract_entity(
    "EmpEmployment",
    "userId,personIdExternal,startDate,endDate",
    expand_nav="personNav,jobInfoNav,compensationNav",
    page_size=500
)
```

### Step 2: Transform

The transformation layer handles:

- **Key translation**: SuccessFactors `personIdExternal` → Workday `Employee_ID` via a durable **crosswalk table** (a persistent mapping between source IDs and target IDs that must never be remapped mid-project)
- **Org resolution**: SuccessFactors `FODepartment.externalCode` → Workday `Supervisory_Organization_Reference_ID`
- **Picklist mapping**: SuccessFactors enum values → Workday enum values (requires a per-field lookup table)
- **Date normalization**: SuccessFactors dates may be in server timezone; Workday expects UTC
- **Multi-employment flattening**: SuccessFactors supports multiple `EmpEmployment` records per `PerPerson`; determine which maps to primary vs. additional jobs in Workday

### Step 3: Load into Workday

For API-based loading, use Workday Web Services (SOAP) for worker data:

```python
import requests
import time

WD_BASE = "https://wd5-services1.myworkday.com/ccx/service/{tenant}"
TOKEN_URL = "https://wd5-impl.workday.com/ccx/oauth2/{tenant}/token"

def refresh_workday_token(client_id, client_secret, refresh_token):
    """Proactively refresh before the 1-hour expiry."""
    response = requests.post(TOKEN_URL, data={
        "grant_type": "refresh_token",
        "refresh_token": refresh_token,
        "client_id": client_id,
        "client_secret": client_secret
    })
    response.raise_for_status()
    return response.json()["access_token"]

def load_worker(worker_data, access_token, max_retries=3):
    """Load a single worker via Workday SOAP API with retry logic."""
    soap_payload = build_hire_employee_xml(worker_data)  # custom builder
    headers = {
        "Content-Type": "text/xml",
        "Authorization": f"Bearer {access_token}"
    }
    for attempt in range(max_retries):
        resp = requests.post(f"{WD_BASE}/Human_Resources/v42.0", 
                           data=soap_payload, headers=headers)
        if resp.status_code == 429:
            retry_after = int(resp.headers.get("Retry-After", 60))
            time.sleep(retry_after)
            continue
        if resp.status_code == 401:
            access_token = refresh_workday_token(CLIENT_ID, SECRET, REFRESH)
            headers["Authorization"] = f"Bearer {access_token}"
            continue
        return resp
    raise Exception(f"Failed after {max_retries} retries: {worker_data['id']}")
```

> [!TIP]
> **Refresh tokens proactively, not reactively.** Set a timer to refresh the Workday OAuth token every 50 minutes — don't wait for the 401 error. This avoids failed writes and partial record states.

### Error Handling Requirements

- Classify errors into retryable (`429`, transient `5xx`) vs. terminal (`400` mapping error, `403` permission gap)
- Persist source request parameters and target response payloads with batch IDs
- Keep a resume marker per entity and batch so a failed run does not force a full restart
- Log every API call, every transformation, every error — you will need the audit trail

## Edge Cases and Failure Modes

### Duplicate Workers and Rehires

SuccessFactors can have orphan `EmpEmployment` records or duplicate `PerPerson` entries from rehire scenarios. Rehiring can either create a new person profile or reuse the existing one. If your HR process reused existing profiles, you'll have multiple `EmpEmployment` entities under one `PerPerson`. Workday handles this differently — determine whether these map to primary employment + additional jobs, or need consolidation.

### Country-Specific Entities

SuccessFactors stores country-specific data in CSF entities (e.g., `PerGlobalInfoUSA`, `PerGlobalInfoDEU`). These contain fields unique to each country's regulatory requirements. Workday handles localization through its own country-specific configurations, but the field mappings are not 1:1. Each country in scope needs its own mapping table.

### Multi-Level Manager Chains

Manager hierarchies in SuccessFactors are stored as `managerId` references in `EmpJob`. When loading into Workday, the manager's `Worker` record must already exist. For circular or deep hierarchies, this requires a topological sort of the load order — or a two-pass approach where workers are loaded first, then manager assignments are updated.

### Future-Dated Records

A naive current-state export can lose valid future-dated rows or over-load history. Be explicit about whether you're extracting current state, full history, or history within a defined date window.

### Attachments and Documents

SuccessFactors stores employee documents as attachments on various entities. Workday's document management has its own structure (Worker Documents). Binary file extraction from SuccessFactors, format validation, and re-upload to Workday is a separate pipeline that most iPaaS tools don't handle natively.

### API Failures During Payroll Periods

Workday rate limits are shared across all integrations hitting a tenant. During payroll processing windows, legitimate business processes consume API quota. Schedule migration loads outside payroll runs.

## Limitations and Constraints to Accept

No migration preserves everything. Document these trade-offs with stakeholders before starting:

- **SuccessFactors MDF entities have no direct Workday equivalent** in many cases. Data may need to be flattened, archived, or re-modeled. Do not attempt a blind lift-and-shift.
- **Workday custom objects are less flexible than MDF.** You cannot replicate arbitrary entity schemas.
- **Effective-dated history depth** may need to be truncated. Loading 10 years of `EmpJob` history for every worker can take days and may not justify the engineering effort if the data is only needed for reference.
- **Picklist values won't survive without mapping tables.** Every SuccessFactors dropdown field needs explicit transformation rules.
- **Workday does not support hard delete via API.** If you load bad data, correction requires targeted update operations, not deletes.
- **Historical workflow and audit events** from SuccessFactors don't map to Workday's business process framework. These typically need archival rather than migration.

## Validation and Testing

### Pre-Cutover Validation Checklist

- [ ] **Record count reconciliation**: Total workers in SuccessFactors vs. Workday, by entity type
- [ ] **Field-level spot checks**: Sample 5% of records, compare every mapped field across countries, legal entities, and job families
- [ ] **Org structure validation**: Verify every Supervisory Organization has the correct manager and reporting chain
- [ ] **Compensation accuracy**: Compare total base pay, comp plan assignments, and effective dates
- [ ] **Payroll parallel run**: Process payroll in both systems for at least one cycle and compare output
- [ ] **Report comparison**: Run equivalent reports in both systems (headcount by department, compensation summary) and diff the results
- [ ] **Effective date verification**: Spot-check high-tenure employees to ensure their job history timeline aligns correctly without overlapping date errors

### UAT Process

1. Define test scenarios per module (HR, Payroll, Benefits, Recruiting)
2. Assign business owners as testers — they know the data better than engineers
3. Provide side-by-side comparison views (SuccessFactors record vs. Workday record)
4. Set a pass/fail threshold (e.g., 99.5% field-level accuracy)
5. Document all exceptions with remediation plans

### Rollback Planning

Workday doesn't have a "restore from backup" button. Rollback options:

- **Reverse load scripts**: Build scripts that can undo specific data loads (e.g., terminate all workers created after a timestamp)
- **Dual-system operation**: Keep SuccessFactors operational until Workday is fully validated
- **Pre-migration snapshot**: Workday tenants can be restored to a sandbox copy, but not production
- Define the last acceptable delta replay window and the point where production cutover is abandoned before go-live

## Post-Migration Tasks

**Rebuild automations**: SuccessFactors workflows (approvals, notifications, triggers) don't migrate. Every business process must be rebuilt in Workday's BP framework.

**Integration re-pointing**: Every downstream system that consumed data from SuccessFactors — payroll providers, benefits carriers, learning platforms, identity provisioning — needs to be re-pointed to Workday. Map these integrations before cutover.

**User training**: Workday's UI is fundamentally different from SuccessFactors. Many issues labeled "data problems" post-migration are actually process problems: new org types, different manager-change flows, changed reference codes, or stricter security.

**Monitor for 30 days**: Run daily reconciliation reports comparing Workday data against your source-of-truth snapshot. Catch drift early.

## Best Practices From Enterprise HCM Migrations

1. **Back up everything before touching production.** Full API export from SuccessFactors, stored immutably.
2. **Run at least 2 complete test migrations.** The first surfaces mapping errors. The second validates fixes.
3. **Automate validation, not just migration.** Build comparison scripts that run automatically after every load.
4. **Load foundation data weeks before workers.** Orgs, locations, job profiles — get them in early and validated.
5. **Separate historical data from active data.** Migrate active employees first with full fidelity. Historical records can follow in a less time-pressured phase.
6. **Use idempotent loaders and stable crosswalk tables.** Never remap IDs mid-project.
7. **Don't trust sandbox alone.** Workday sandbox configurations diverge from production. Validate ISU permissions and custom fields against production before cutover.
8. **Treat security setup as part of migration engineering.** API scopes and domain permissions are not admin cleanup — they're part of the critical path.
9. **Log everything.** Every API call, every transformation, every error. You will need the audit trail.

## Making the Right Call

The SuccessFactors-to-Workday migration is a high-stakes data-engineering project. The data migration piece is where you can save months or waste them.

For straightforward datasets, EIB imports and iPaaS connectors work. For enterprise-scale, multi-country, multi-entity migrations with payroll continuity requirements, API-based pipelines — whether custom-built or managed — are the only path that preserves full data integrity.

The decision framework is simple: if your team has deep expertise in both SuccessFactors OData APIs and Workday Web Services, and you have 8–16 weeks of engineering time available, build it. If you don't, hire someone who's done it before.

> Need to migrate from SuccessFactors to Workday without payroll disruption? ClonePartner's engineering team handles complex HCM migrations with zero downtime. Book a 30-minute call to scope your migration.
>
> [Talk to us](https://cal.com/clonepartner/meet?duration=30&utm_source=blog&utm_medium=button&utm_campaign=demo_bookings&utm_content=cta_click&utm_term=demo_button_click)

## Frequently asked questions

### How long does a SuccessFactors to Workday migration take?

For small organizations (<500 employees), 1–4 weeks using EIB imports. For enterprise-scale (5,000+ employees, multi-country, historical data), expect 8–16 weeks for the data migration component alone, not including Workday implementation and change management.

### Can Workday EIB handle a full historical SuccessFactors migration?

Usually no. EIB is good for simple-to-moderately complex bulk loads and foundation data, but full historical migrations need dependency ordering, retries, delta replays, and custom transforms that push teams toward APIs or Workday Studio. EIB also caps at 300 MB via SFTP and 30 MB via browser.

### What are Workday's API rate limits for data migration?

Workday does not publish hard API rate limits publicly. Standard tenants are commonly reported to receive 5,000–10,000 API calls per hour, varying by tenant size and subscription tier. The API returns HTTP 429 with a Retry-After header when throttled. OAuth access tokens expire after approximately 1 hour, requiring explicit refresh logic in batch jobs.

### Does SuccessFactors have API rate limits for data extraction?

SAP postponed hard rate limits indefinitely but still enforces dynamic throttling based on server capacity. Throttled requests return HTTP 429 with a recommended 300-second (5-minute) retry window. The OData API returns a maximum of 1,000 records per page, and SAP recommends keeping page sizes at or below 500 for complex queries.

### Can Boomi or Workato handle SuccessFactors to Workday migration?

Both platforms have pre-built connectors for SuccessFactors and Workday and handle authentication, retry logic, and basic mapping. They work well for ongoing sync and mid-volume migrations. For large historical bulk loads (50,000+ workers with full history), iPaaS throughput can bottleneck and complex MDF entities may still require custom scripting.
