ERP REST API Comparison: Salesforce vs SAP vs Oracle ERP Cloud vs NetSuite vs Dynamics 365

Type: ERP Integration System: Salesforce, SAP S/4HANA, Oracle ERP Cloud, NetSuite, D365 F&O Confidence: 0.82 Sources: 8 Verified: 2026-03-02 Freshness: evolving

TL;DR

System Profile

This card compares REST API capabilities across the five most widely deployed enterprise ERP platforms as of early 2026. It covers cloud deployments only. On-premise variants (SAP ECC, Oracle E-Business Suite, Dynamics AX) have fundamentally different API architectures and are not covered. Salesforce is included because its CRM platform is a dominant integration counterpart and offers ERP-adjacent capabilities through Manufacturing Cloud and Revenue Cloud.

SystemRoleAPI SurfaceDeployment
Salesforce (v62.0)CRM + ERP-adjacentREST, SOAP, Bulk 2.0, Streaming, CompositeCloud
SAP S/4HANA Cloud (2408/2502)ERP — financials + SCM + manufacturingOData V2/V4, SOAP, RFC/BAPICloud
Oracle ERP Cloud (24D/25A)ERP — financials + procurement + projectsREST, SOAP, FBDI, BI PublisherCloud
Oracle NetSuite (2025.1)ERP — mid-market financials + CRM + ecommerceREST, SuiteTalk SOAP, SuiteQL, RESTletsCloud
D365 F&O (10.0.40+)ERP — financials + SCMOData V4, Custom Services, DMF, Dual-WriteCloud

API Surfaces & Capabilities

API SurfaceProtocolBest ForMax Records/ReqRate LimitReal-time?Bulk?
Salesforce RESTHTTPS/JSONIndividual CRUD, <2K records2,000 (query), 200 (composite)100K calls/24h (Enterprise)YesNo
Salesforce Bulk API 2.0HTTPS/CSVETL, data migration, >2K records150M per file15K batches/24hNoYes
SAP OData V4HTTPS/JSONModern integrations, read/write1,000 per page (default)~100 req/s (tenant-specific)YesVia batch
SAP OData V2HTTPS/JSON+XMLLegacy integrations, broad coverage1,000 per pageShared with V4YesVia batch
Oracle ERP Cloud RESTHTTPS/JSONFinancial CRUD, procurement, projects500 per POSTNot publishedYesNo
Oracle FBDIFile/CSVBulk imports (AP, AR, GL, PO)500MB per fileJob queue-basedNoYes
NetSuite RESTHTTPS/JSONRecord CRUD, newer integrations1,000 (query), 1 (write)5-20 concurrent (tier)YesNo
NetSuite SuiteQLHTTPS/JSONComplex queries, analytics100,000 rows/queryShared concurrencyYesRead-only
D365 F&O ODataHTTPS/JSONEntity CRUD, real-time integration10,000 per page6K/5min per user per serverYesNo
D365 DMFFile/PackageData migration, recurring importsPackage-basedExempt from API limitsNoYes

Rate Limits & Quotas

Per-Request Limits

SystemLimit TypeValueNotes
SalesforceMax records per query2,000Use queryMore() for pagination
SalesforceMax composite subrequests25All-or-nothing by default
SalesforceMax request body size50 MBREST API
SalesforceMax batch file size150 MBBulk API 2.0
SAP S/4HANAMax records per page1,000OData $top default; adjustable
Oracle ERP CloudMax records per POST500Use FBDI for larger volumes
Oracle ERP CloudMax FBDI file size500 MBSplit logically for performance
NetSuiteMax query results1,000Per request; paginate for more
NetSuiteMax SuiteQL rows100,000Per query execution
D365 F&OMax records per page10,000OData $top parameter

Rolling / Daily Limits

SystemLimit TypeValueWindowEdition Differences
SalesforceAPI calls100,00024h rollingEnterprise: 100K, Unlimited: 5M, Developer: 15K
SalesforceBulk API batches15,00024h rollingShared between Bulk API and Bulk API 2.0
SAP S/4HANAAPI throughput~100 req/sPer secondTenant-specific; varies by subscription
Oracle ERP CloudREST API rateNot publishedN/AContact Oracle support
NetSuiteConcurrent requests5-20ContinuousStandard: 5, Premium: 15, Enterprise: 20. +10 per SuiteCloud Plus
D365 F&ORequests per user6,0005-min sliding windowPer user, per app ID, per web server
D365 F&OExecution time1,200s5-min sliding windowCombined execution time per user
D365 F&OConcurrent requests52ContinuousPer user per web server

Transaction / Governor Limits (Salesforce)

Limit TypeSync ValueAsync ValueNotes
SOQL queries100200Includes queries from triggers — cascading triggers consume from same pool
Records retrieved50,00050,000Per transaction across all queries
DML statements150150Each insert/update/delete counts as 1
Records processed (DML)10,00010,000Per transaction
CPU time10,000 ms60,000 msExceeded = transaction abort
Heap size6 MB12 MBPer transaction
HTTP callouts100100External service calls within transaction

SAP, Oracle ERP Cloud, and Dynamics 365 F&O do not have per-transaction governor limits comparable to Salesforce. Their throttling is purely rate-based.

Authentication

SystemPrimary FlowServer-to-ServerUser ContextToken LifetimeMFA Support
SalesforceOAuth 2.0JWT BearerWeb Server flowAccess: ~2h, Refresh: until revokedYes (required)
SAP S/4HANAOAuth 2.0Client CredentialsAuthorization CodeConfigurableSAML 2.0, mTLS
Oracle ERP CloudOAuth 2.0Client CredentialsAuthorization CodeShort-livedYes
NetSuiteTBA (OAuth 1.0)TBA (consumer + token)OAuth 2.0 (newer)TBA: no expiryOAuth tokens expire
D365 F&OEntra IDApp RegistrationDelegated (on-behalf-of)Access: 1h, Refresh: 90dConditional Access

Authentication Gotchas

Constraints

Integration Pattern Decision Tree

START — Choosing an ERP API surface for integration
├── What's your data volume per operation?
│   ├── < 500 records
│   │   ├── Salesforce → REST API (Composite for multi-object)
│   │   ├── SAP S/4HANA → OData V4 (single or batch)
│   │   ├── Oracle ERP Cloud → REST API
│   │   ├── NetSuite → REST API or RESTlet
│   │   └── D365 F&O → OData V4
│   ├── 500-10,000 records
│   │   ├── Salesforce → Bulk API 2.0
│   │   ├── SAP → OData batch request
│   │   ├── Oracle → FBDI (file-based)
│   │   ├── NetSuite → SuiteTalk SOAP or CSV import
│   │   └── D365 → Data Management Framework (DMF)
│   └── > 10,000 records
│       ├── Salesforce → Bulk API 2.0 (split jobs if >150MB)
│       ├── SAP → IDocs or BAPI batch
│       ├── Oracle → FBDI (split files for >250MB)
│       ├── NetSuite → CSV Import or SuiteCloud Plus + SOAP
│       └── D365 → DMF (recurring)
├── Real-time (<1s)?
│   All → REST/OData (within per-request limits above)
└── Event-driven?
    ├── Salesforce → Platform Events + CDC
    ├── SAP → Business Events (2408+)
    ├── Oracle → Business Events (limited) + OIC webhooks
    ├── NetSuite → User Event Scripts + Workflow
    └── D365 → Business Events + Dual-Write

Quick Reference

CapabilitySalesforceSAP S/4HANAOracle ERP CloudNetSuiteD365 F&OWinner
REST API maturityExcellentGood (OData)ModerateGoodGood (OData)Salesforce
API documentationExcellentGoodModerateModerateVery GoodSalesforce
Rate limit transparencyPublishedUndocumentedUndocumentedModeratePublishedSF/D365
Bulk data importBulk API 2.0 (150MB)IDocs/BAPIFBDI (500MB)CSV/SOAPDMF packagesOracle (size)
Real-time eventsPlatform Events + CDCBusiness EventsLimitedUser EventsBusiness Events + Dual-WriteSalesforce
Query languageSOQL (proprietary)OData $filterREST paramsSuiteQL (SQL-like)OData $filterNetSuite
Max concurrentRate-basedTenant-specificNot published5-20 (tier)52/user/serverD365
Sandbox supportFull + PartialTest tenantTest instanceLimitedSandbox + UATSalesforce
API versioningNumbered (v62.0)Release (2408)Release (24D)WSDL versionedPlatform (10.0.x)Salesforce
Webhook supportOutbound Msgs + EventsBusiness EventsLimited (OIC)Events + WorkflowBusiness EventsSalesforce
PaginationCursor (queryMore)$skip/$topoffset/limitoffset/limit$skip/$topSalesforce
Developer experienceExcellentGood (API Hub)ModerateModerateVery GoodSalesforce

Step-by-Step Integration Guide

1. Test authentication across all five platforms

Each platform requires a different setup. Validate authentication independently before building integration logic. [src1, src3]

# Salesforce: OAuth 2.0 JWT Bearer flow
curl -X POST https://login.salesforce.com/services/oauth2/token \
  -d "grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer" \
  -d "assertion=${JWT_TOKEN}"

# SAP S/4HANA: OAuth 2.0 Client Credentials
curl -X POST https://{tenant}.authentication.{region}.hana.ondemand.com/oauth/token \
  -d "grant_type=client_credentials" \
  -d "client_id=${CLIENT_ID}" -d "client_secret=${CLIENT_SECRET}"

# Oracle ERP Cloud: OAuth 2.0
curl -X POST https://{instance}.fa.{dc}.oraclecloud.com/oauth2/v1/token \
  -d "grant_type=client_credentials" -d "scope=urn:opc:resource:consumer::all" \
  -u "${CLIENT_ID}:${CLIENT_SECRET}"

# NetSuite: Token-Based Authentication (OAuth 1.0 signature)
curl -X GET "https://{account_id}.suitetalk.api.netsuite.com/services/rest/record/v1/customer" \
  -H "Authorization: OAuth realm="{account_id}",oauth_consumer_key="...",..."

# D365 F&O: Microsoft Entra ID
curl -X POST https://login.microsoftonline.com/{tenant_id}/oauth2/v2.0/token \
  -d "grant_type=client_credentials" -d "client_id=${CLIENT_ID}" \
  -d "client_secret=${CLIENT_SECRET}" \
  -d "scope=https://{env}.operations.dynamics.com/.default"

Verify: Each returns JSON with access_token. HTTP 200 = success, 401 = credential issue.

2. Query records using each platform's syntax

Each platform has a different query approach. [src1, src4, src5]

# Salesforce: SOQL via REST
curl https://{instance}.my.salesforce.com/services/data/v62.0/query?q=SELECT+Id,Name+FROM+Account+LIMIT+10

# SAP: OData V4 with $filter
curl "https://{tenant}.s4hana.cloud.sap/.../A_BusinessPartner?\$top=10"

# Oracle: REST with query params
curl "https://{instance}.fa.{dc}.oraclecloud.com/fscmRestApi/resources/latest/invoices?limit=10"

# NetSuite: SuiteQL
curl -X POST ".../services/rest/query/v1/suiteql" \
  -d '{"q": "SELECT id, companyname FROM customer FETCH FIRST 10 ROWS ONLY"}'

# D365: OData with $filter
curl "https://{env}.operations.dynamics.com/data/Customers?\$top=10"

Verify: HTTP 200 with JSON array. Check totalSize (SF), @odata.count (SAP/D365), totalResults (NS).

3. Create a record on each platform

Write operations test permissions and field validation. [src6]

# Salesforce: POST /sobjects/Account
curl -X POST .../v62.0/sobjects/Account -d '{"Name": "Test Account"}'
# Returns: {"id": "001...", "success": true}

# SAP: POST to OData entity
curl -X POST .../A_BusinessPartner -d '{"BusinessPartnerCategory": "1", ...}'

# Oracle: POST to REST resource
curl -X POST .../invoices -d '{"InvoiceNumber": "TEST-001", "InvoiceAmount": 100}'

# NetSuite: POST to record type
curl -X POST .../record/v1/customer -d '{"companyName": "Test Customer", ...}'
# Returns Location header with record URL

# D365: POST to OData entity
curl -X POST .../data/Customers -d '{"CustomerAccount": "TEST001", ...}'

Verify: HTTP 201 (Created) for all platforms.

4. Handle pagination for large result sets

Each platform paginates differently — production integrations must handle this. [src1]

# Salesforce: Cursor-based (nextRecordsUrl)
# SAP/D365: $skip/$top offset pagination
# Oracle: offset/limit parameters
# NetSuite: offset/limit (REST) or cursor (SOAP)

Verify: Check for nextRecordsUrl (SF), @odata.nextLink (SAP/D365), hasMore (NS), or empty array (end of data).

Data Mapping

Cross-Platform Field Naming

ConceptSalesforceSAP S/4HANAOracle ERP CloudNetSuiteD365 F&O
Customer nameAccount.NameBusinessPartnerFullNameOrganizationNamecompanyNameName
Customer IDId (18-char)BusinessPartner (10-digit)PartyId (numeric)id (internalId)CustomerAccount
Amount fieldAmount (decimal)NetAmount (smallest unit)InvoiceAmount (decimal)amount (decimal)InvoiceAmount (decimal)
Date formatYYYY-MM-DD (UTC)YYYY-MM-DD (UTC)YYYY-MM-DDYYYY-MM-DDYYYY-MM-DD (UTC)
Booleantrue/falsetrue/falsetrue/false"T"/"F" or true/falseNoYes enum (0/1)
CurrencyCurrencyIsoCodeTransactionCurrencyInvoiceCurrencyCodecurrency.idCurrencyCode

Data Type Gotchas

Error Handling & Failure Points

Common Error Codes

HTTPSalesforceSAP S/4HANAOracle ERP CloudNetSuiteD365 F&O
400MALFORMED_QUERYOData error detailsMissing required fieldsSSS_MISSING_REQD_ARGUMENTValidation error
401INVALID_SESSION_IDToken expiredToken expiredINVALID_LOGIN_CREDENTIALSToken expired
403INSUFFICIENT_ACCESSNo authorizationInsufficient privilegesSSS_NO_PERMISSIONMissing API permission
404NOT_FOUNDEntity not foundResource not foundRECORD_NOT_FOUNDEntity not found
429REQUEST_LIMIT_EXCEEDEDThrottledRate limitedSSS_REQUEST_LIMIT_EXCEEDEDToo Many Requests + Retry-After

Failure Points in Production

Anti-Patterns

Wrong: Same API surface for all data volumes

// BAD — Using SF REST API for 50K-record import (50% of daily limit in one op)
for (let i = 0; i < 50000; i++) {
  await fetch(`${instanceUrl}/services/data/v62.0/sobjects/Contact`, {
    method: 'POST', body: JSON.stringify(contacts[i])
  });
}

Correct: Match API surface to data volume

// GOOD — Use Bulk API 2.0: consumes 1 batch instead of 50K REST calls
const job = await fetch(`${instanceUrl}/services/data/v62.0/jobs/ingest`, {
  method: 'POST',
  body: JSON.stringify({ object: 'Contact', operation: 'insert', contentType: 'CSV' })
});

Wrong: Ignoring NetSuite shared concurrency

// BAD — 3 integrations each opening 5 parallel connections on Standard tier (limit: 5)
Promise.all(records.map(r => fetch(netsuiteUrl, { body: r })));

Correct: Centralized concurrency governance

// GOOD — Reserve 30% for other integrations
const MAX_CONCURRENT = Math.floor(concurrencyLimit * 0.7);
const semaphore = new Semaphore(MAX_CONCURRENT);
for (const record of records) {
  await semaphore.acquire();
  fetch(netsuiteUrl, { body: record }).finally(() => semaphore.release());
}

Wrong: Hardcoded retry on D365 429

// BAD — Fixed 1s retry ignores Retry-After header
if (response.status === 429) { await sleep(1000); retry(); }

Correct: Respect Retry-After header

// GOOD — D365 returns Retry-After with specific wait time
if (response.status === 429) {
  const retryAfter = parseInt(response.headers.get('Retry-After') || '5');
  await sleep(retryAfter * 1000); retry();
}

Common Pitfalls

Diagnostic Commands

# Salesforce: Check remaining API limits
curl https://{instance}.my.salesforce.com/services/data/v62.0/limits \
  -H "Authorization: Bearer ${SF_TOKEN}" | jq '.DailyApiRequests'

# SAP: Test OData metadata access (validates auth + permissions)
curl "https://{tenant}.s4hana.cloud.sap/.../\$metadata" \
  -H "Authorization: Bearer ${SAP_TOKEN}"

# Oracle: Test REST API connectivity
curl "https://{instance}.fa.{dc}.oraclecloud.com/fscmRestApi/resources/latest" \
  -H "Authorization: Bearer ${ORACLE_TOKEN}"

# NetSuite: Check available record types
curl "https://{account_id}.suitetalk.api.netsuite.com/services/rest/record/v1/metadata-catalog" \
  -H "Authorization: OAuth ..."

# D365: List available data entities
curl "https://{env}.operations.dynamics.com/data/\$metadata" \
  -H "Authorization: Bearer ${D365_TOKEN}" | head -100

Version History & Compatibility

PlatformCurrent VersionRelease CyclePreviousDeprecation Policy
Salesforcev62.0 (Spring '26)3x/yearv61.0 (Winter '26)3+ years minimum support
SAP S/4HANA2502 (Feb 2026)Quarterly (Public)2408 (Aug 2024)2-year notice on API Hub
Oracle ERP Cloud25A (Q1 2026)Quarterly (A/B/C/D)24D (Q4 2025)Rare API removal
NetSuite2025.1Biannual2024.22-year notice; SOAP still supported
D365 F&O10.0.40+ (2026)Monthly10.0.3912-month notice for entity deprecation

When to Use / When Not to Use

Use WhenDon't Use WhenUse Instead
Evaluating ERP API for integration architectureAlready selected ERP, need deep referenceSingle-system API cards
Designing multi-ERP integration spanning 2+ platformsNeed iPaaS/middleware selectioniPaaS comparison card
Comparing rate limits to size your integrationNeed specific code examples for one platformSingle-system cards
Assessing authentication complexityNeed SOAP-only comparisonSOAP API comparison card

Cross-System Comparison

CapabilitySalesforceSAP S/4HANAOracle ERP CloudNetSuiteD365 F&ONotes
API StyleREST + SOAP + BulkOData V2/V4 + SOAPREST + SOAP + FBDIREST + SOAP + SuiteQLOData V4 + DMFSF most diverse
Rate Limit ModelFixed daily (100K/24h)Tenant-specificNot publishedConcurrency (5-20)Sliding window + resourceD365 most transparent
Bulk ImportBulk API 2.0 (150MB)IDocs/BAPI batchFBDI (500MB)CSV Import / SOAPDMF packagesOracle largest file
Event-DrivenPlatform Events + CDCBusiness EventsLimitedUser Event ScriptsBusiness Events + Dual-WriteSF most mature
AuthOAuth 2.0OAuth 2.0, SAML, mTLSOAuth 2.0, BasicTBA, OAuth 2.0Entra ID (OAuth 2.0)SAP most options
SandboxFull + PartialTest tenantTest instanceLimitedSandbox + UATSF most flexible
API VersioningNumbered (v62.0)Release (2408)Release (24D)WSDL versionedPlatform (10.0.x)SF most explicit
Query LanguageSOQLOData $filterREST paramsSuiteQL (SQL-like)OData $filterNS most familiar
Developer DXExcellentGoodModerateModerateVery GoodSF best DX
Error DetailDetailedModerateBasicModerateGoodSF most helpful
Webhook SupportOutbound Msgs + EventsBusiness EventsLimited (OIC)Events + WorkflowBusiness EventsSF most flexible

Important Caveats

Related Units