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
- Bottom line: Salesforce has the most mature REST API with published limits (100K/24h Enterprise); SAP uses OData with undocumented tenant-specific throttling; Oracle ERP Cloud REST is limited to 500 records/POST (use FBDI for bulk); NetSuite shares 5-20 concurrent slots across all integrations; D365 F&O uses 6K requests per 5-minute sliding window per user.
- Key limit: Salesforce 100K calls/24h (Enterprise), NetSuite 5-20 concurrent (tier-based), D365 6K/5min/user/server, SAP and Oracle not publicly documented.
- Watch out for: NetSuite concurrency is account-wide (not per-integration), Salesforce governor limits cascade through triggers, D365 resource-based throttling overrides user-based limits, Oracle REST POST cap at 500 records.
- Best for: Side-by-side evaluation when selecting an ERP or designing multi-ERP integration architecture.
- Authentication: All five support OAuth 2.0. SAP adds SAML/mTLS. NetSuite has TBA (OAuth 1.0). D365 uses Microsoft Entra ID.
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.
| System | Role | API Surface | Deployment |
| Salesforce (v62.0) | CRM + ERP-adjacent | REST, SOAP, Bulk 2.0, Streaming, Composite | Cloud |
| SAP S/4HANA Cloud (2408/2502) | ERP — financials + SCM + manufacturing | OData V2/V4, SOAP, RFC/BAPI | Cloud |
| Oracle ERP Cloud (24D/25A) | ERP — financials + procurement + projects | REST, SOAP, FBDI, BI Publisher | Cloud |
| Oracle NetSuite (2025.1) | ERP — mid-market financials + CRM + ecommerce | REST, SuiteTalk SOAP, SuiteQL, RESTlets | Cloud |
| D365 F&O (10.0.40+) | ERP — financials + SCM | OData V4, Custom Services, DMF, Dual-Write | Cloud |
API Surfaces & Capabilities
| API Surface | Protocol | Best For | Max Records/Req | Rate Limit | Real-time? | Bulk? |
| Salesforce REST | HTTPS/JSON | Individual CRUD, <2K records | 2,000 (query), 200 (composite) | 100K calls/24h (Enterprise) | Yes | No |
| Salesforce Bulk API 2.0 | HTTPS/CSV | ETL, data migration, >2K records | 150M per file | 15K batches/24h | No | Yes |
| SAP OData V4 | HTTPS/JSON | Modern integrations, read/write | 1,000 per page (default) | ~100 req/s (tenant-specific) | Yes | Via batch |
| SAP OData V2 | HTTPS/JSON+XML | Legacy integrations, broad coverage | 1,000 per page | Shared with V4 | Yes | Via batch |
| Oracle ERP Cloud REST | HTTPS/JSON | Financial CRUD, procurement, projects | 500 per POST | Not published | Yes | No |
| Oracle FBDI | File/CSV | Bulk imports (AP, AR, GL, PO) | 500MB per file | Job queue-based | No | Yes |
| NetSuite REST | HTTPS/JSON | Record CRUD, newer integrations | 1,000 (query), 1 (write) | 5-20 concurrent (tier) | Yes | No |
| NetSuite SuiteQL | HTTPS/JSON | Complex queries, analytics | 100,000 rows/query | Shared concurrency | Yes | Read-only |
| D365 F&O OData | HTTPS/JSON | Entity CRUD, real-time integration | 10,000 per page | 6K/5min per user per server | Yes | No |
| D365 DMF | File/Package | Data migration, recurring imports | Package-based | Exempt from API limits | No | Yes |
Rate Limits & Quotas
Per-Request Limits
| System | Limit Type | Value | Notes |
| Salesforce | Max records per query | 2,000 | Use queryMore() for pagination |
| Salesforce | Max composite subrequests | 25 | All-or-nothing by default |
| Salesforce | Max request body size | 50 MB | REST API |
| Salesforce | Max batch file size | 150 MB | Bulk API 2.0 |
| SAP S/4HANA | Max records per page | 1,000 | OData $top default; adjustable |
| Oracle ERP Cloud | Max records per POST | 500 | Use FBDI for larger volumes |
| Oracle ERP Cloud | Max FBDI file size | 500 MB | Split logically for performance |
| NetSuite | Max query results | 1,000 | Per request; paginate for more |
| NetSuite | Max SuiteQL rows | 100,000 | Per query execution |
| D365 F&O | Max records per page | 10,000 | OData $top parameter |
Rolling / Daily Limits
| System | Limit Type | Value | Window | Edition Differences |
| Salesforce | API calls | 100,000 | 24h rolling | Enterprise: 100K, Unlimited: 5M, Developer: 15K |
| Salesforce | Bulk API batches | 15,000 | 24h rolling | Shared between Bulk API and Bulk API 2.0 |
| SAP S/4HANA | API throughput | ~100 req/s | Per second | Tenant-specific; varies by subscription |
| Oracle ERP Cloud | REST API rate | Not published | N/A | Contact Oracle support |
| NetSuite | Concurrent requests | 5-20 | Continuous | Standard: 5, Premium: 15, Enterprise: 20. +10 per SuiteCloud Plus |
| D365 F&O | Requests per user | 6,000 | 5-min sliding window | Per user, per app ID, per web server |
| D365 F&O | Execution time | 1,200s | 5-min sliding window | Combined execution time per user |
| D365 F&O | Concurrent requests | 52 | Continuous | Per user per web server |
Transaction / Governor Limits (Salesforce)
| Limit Type | Sync Value | Async Value | Notes |
| SOQL queries | 100 | 200 | Includes queries from triggers — cascading triggers consume from same pool |
| Records retrieved | 50,000 | 50,000 | Per transaction across all queries |
| DML statements | 150 | 150 | Each insert/update/delete counts as 1 |
| Records processed (DML) | 10,000 | 10,000 | Per transaction |
| CPU time | 10,000 ms | 60,000 ms | Exceeded = transaction abort |
| Heap size | 6 MB | 12 MB | Per transaction |
| HTTP callouts | 100 | 100 | External 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
| System | Primary Flow | Server-to-Server | User Context | Token Lifetime | MFA Support |
| Salesforce | OAuth 2.0 | JWT Bearer | Web Server flow | Access: ~2h, Refresh: until revoked | Yes (required) |
| SAP S/4HANA | OAuth 2.0 | Client Credentials | Authorization Code | Configurable | SAML 2.0, mTLS |
| Oracle ERP Cloud | OAuth 2.0 | Client Credentials | Authorization Code | Short-lived | Yes |
| NetSuite | TBA (OAuth 1.0) | TBA (consumer + token) | OAuth 2.0 (newer) | TBA: no expiry | OAuth tokens expire |
| D365 F&O | Entra ID | App Registration | Delegated (on-behalf-of) | Access: 1h, Refresh: 90d | Conditional Access |
Authentication Gotchas
- Salesforce: JWT bearer flow requires connected app with digital certificate. Self-signed for dev, CA-signed for production. [src1]
- SAP: Communication Arrangements must be configured in Fiori launchpad before external auth works — common onboarding blocker. [src4]
- Oracle: Basic auth (username:password) still works in tutorials — do NOT use in production. OAuth 2.0 required. [src6]
- NetSuite: TBA tokens do not expire — convenient but a security risk. Rotate periodically and bind to dedicated integration role. [src5]
- D365: App registrations require explicit
Dynamics ERP permission scope in Microsoft Entra ID — common first-time setup failure. [src3]
Constraints
- Salesforce 100K/24h hard cap: Cannot be increased without purchasing API call add-ons. Plan for 50-70% utilization ceiling across all integrations.
- NetSuite shared concurrency: 5 concurrent slots on Standard tier shared across ALL integrations (SOAP, REST, RESTlets). One bad integration blocks all others.
- Oracle ERP Cloud 500-record POST limit: REST API unsuitable for bulk. FBDI required for >500 records (file upload to UCM + async processing).
- SAP undocumented limits: No published fixed rate limits. Tenant-specific, adjusted by SAP support. Capacity planning requires test runs.
- D365 resource-based throttling: Even within 6K/5min user limit, resource-based throttling can activate based on aggregate server CPU/memory.
- Cross-platform token management: All five platforms use different token lifecycles. Multi-ERP integration must handle five different refresh patterns.
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
| Capability | Salesforce | SAP S/4HANA | Oracle ERP Cloud | NetSuite | D365 F&O | Winner |
| REST API maturity | Excellent | Good (OData) | Moderate | Good | Good (OData) | Salesforce |
| API documentation | Excellent | Good | Moderate | Moderate | Very Good | Salesforce |
| Rate limit transparency | Published | Undocumented | Undocumented | Moderate | Published | SF/D365 |
| Bulk data import | Bulk API 2.0 (150MB) | IDocs/BAPI | FBDI (500MB) | CSV/SOAP | DMF packages | Oracle (size) |
| Real-time events | Platform Events + CDC | Business Events | Limited | User Events | Business Events + Dual-Write | Salesforce |
| Query language | SOQL (proprietary) | OData $filter | REST params | SuiteQL (SQL-like) | OData $filter | NetSuite |
| Max concurrent | Rate-based | Tenant-specific | Not published | 5-20 (tier) | 52/user/server | D365 |
| Sandbox support | Full + Partial | Test tenant | Test instance | Limited | Sandbox + UAT | Salesforce |
| API versioning | Numbered (v62.0) | Release (2408) | Release (24D) | WSDL versioned | Platform (10.0.x) | Salesforce |
| Webhook support | Outbound Msgs + Events | Business Events | Limited (OIC) | Events + Workflow | Business Events | Salesforce |
| Pagination | Cursor (queryMore) | $skip/$top | offset/limit | offset/limit | $skip/$top | Salesforce |
| Developer experience | Excellent | Good (API Hub) | Moderate | Moderate | Very Good | Salesforce |
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
| Concept | Salesforce | SAP S/4HANA | Oracle ERP Cloud | NetSuite | D365 F&O |
| Customer name | Account.Name | BusinessPartnerFullName | OrganizationName | companyName | Name |
| Customer ID | Id (18-char) | BusinessPartner (10-digit) | PartyId (numeric) | id (internalId) | CustomerAccount |
| Amount field | Amount (decimal) | NetAmount (smallest unit) | InvoiceAmount (decimal) | amount (decimal) | InvoiceAmount (decimal) |
| Date format | YYYY-MM-DD (UTC) | YYYY-MM-DD (UTC) | YYYY-MM-DD | YYYY-MM-DD | YYYY-MM-DD (UTC) |
| Boolean | true/false | true/false | true/false | "T"/"F" or true/false | NoYes enum (0/1) |
| Currency | CurrencyIsoCode | TransactionCurrency | InvoiceCurrencyCode | currency.id | CurrencyCode |
Data Type Gotchas
- SAP amounts in smallest unit: SAP stores amounts in cents (USD). Divide by 100 before mapping to other systems. Forgetting = 100x overcharges. [src4]
- Salesforce 18-char vs 15-char IDs: API returns 18-char case-insensitive IDs. Always store and compare 18-char version. [src1]
- NetSuite internalId vs externalId: Use externalId for idempotent upserts. [src5]
- D365 enums vs strings: D365 uses integer enums (NoYes::Yes = 1). OData may expose as string labels but writes need integers. [src3]
- Oracle multi-org context: Queries without Business Unit context may return empty or cross-BU data. [src6]
Error Handling & Failure Points
Common Error Codes
| HTTP | Salesforce | SAP S/4HANA | Oracle ERP Cloud | NetSuite | D365 F&O |
| 400 | MALFORMED_QUERY | OData error details | Missing required fields | SSS_MISSING_REQD_ARGUMENT | Validation error |
| 401 | INVALID_SESSION_ID | Token expired | Token expired | INVALID_LOGIN_CREDENTIALS | Token expired |
| 403 | INSUFFICIENT_ACCESS | No authorization | Insufficient privileges | SSS_NO_PERMISSION | Missing API permission |
| 404 | NOT_FOUND | Entity not found | Resource not found | RECORD_NOT_FOUND | Entity not found |
| 429 | REQUEST_LIMIT_EXCEEDED | Throttled | Rate limited | SSS_REQUEST_LIMIT_EXCEEDED | Too Many Requests + Retry-After |
Failure Points in Production
- Salesforce governor limit cascade: Triggers on bulk DML share the 100-SOQL-query pool. 200 records can easily exceed it. Fix:
bulkify triggers — query outside loops, use maps. [src8]
- NetSuite concurrency starvation: One integration holding 14/15 slots blocks all others. Fix:
Gatekeeper pattern — limit workers to 50-70% of concurrency cap. [src5]
- D365 resource-based throttling at month-end: Aggregate load triggers 429 even when individual integrations are within limits. Fix:
schedule outside peak; use priority-based throttling config. [src3]
- Oracle FBDI silent failures: Jobs show "Succeeded" while records fail. Fix:
always download and parse ESS output file after import. [src6]
- SAP Communication Arrangement expiry: Deactivated arrangement causes silent 401. Fix:
monitor Communication Management tile; alert on repeated 401s. [src4]
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
- Salesforce — API version pinning: Not pinning version means breakage on deprecation. Fix:
always pin version in base URL; test next version before each release. [src1]
- SAP — OData V2 vs V4 mismatch: Many tutorials use V2 but newer APIs only expose V4. Fix:
check api.sap.com for each API; use V4 for new integrations. [src4, src7]
- Oracle — Business Unit context: Queries without BU context return empty or cross-org data. Fix:
always include BU parameter; scope integration user to single BU. [src6]
- NetSuite — RESTlet per-user limit: Individual users capped at 5 concurrent RESTlet executions. Fix:
dedicated integration role and user per integration. [src5]
- D365 — DMF exemption assumption: DMF is exempt from API limits, OData is not. Fix:
use DMF for bulk; OData for real-time low-volume only. [src3]
- All platforms — Partial success handling: Bulk ops can partially succeed. Fix:
always parse per-record status; implement dead-letter queues.
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
| Platform | Current Version | Release Cycle | Previous | Deprecation Policy |
| Salesforce | v62.0 (Spring '26) | 3x/year | v61.0 (Winter '26) | 3+ years minimum support |
| SAP S/4HANA | 2502 (Feb 2026) | Quarterly (Public) | 2408 (Aug 2024) | 2-year notice on API Hub |
| Oracle ERP Cloud | 25A (Q1 2026) | Quarterly (A/B/C/D) | 24D (Q4 2025) | Rare API removal |
| NetSuite | 2025.1 | Biannual | 2024.2 | 2-year notice; SOAP still supported |
| D365 F&O | 10.0.40+ (2026) | Monthly | 10.0.39 | 12-month notice for entity deprecation |
When to Use / When Not to Use
| Use When | Don't Use When | Use Instead |
| Evaluating ERP API for integration architecture | Already selected ERP, need deep reference | Single-system API cards |
| Designing multi-ERP integration spanning 2+ platforms | Need iPaaS/middleware selection | iPaaS comparison card |
| Comparing rate limits to size your integration | Need specific code examples for one platform | Single-system cards |
| Assessing authentication complexity | Need SOAP-only comparison | SOAP API comparison card |
Cross-System Comparison
| Capability | Salesforce | SAP S/4HANA | Oracle ERP Cloud | NetSuite | D365 F&O | Notes |
| API Style | REST + SOAP + Bulk | OData V2/V4 + SOAP | REST + SOAP + FBDI | REST + SOAP + SuiteQL | OData V4 + DMF | SF most diverse |
| Rate Limit Model | Fixed daily (100K/24h) | Tenant-specific | Not published | Concurrency (5-20) | Sliding window + resource | D365 most transparent |
| Bulk Import | Bulk API 2.0 (150MB) | IDocs/BAPI batch | FBDI (500MB) | CSV Import / SOAP | DMF packages | Oracle largest file |
| Event-Driven | Platform Events + CDC | Business Events | Limited | User Event Scripts | Business Events + Dual-Write | SF most mature |
| Auth | OAuth 2.0 | OAuth 2.0, SAML, mTLS | OAuth 2.0, Basic | TBA, OAuth 2.0 | Entra ID (OAuth 2.0) | SAP most options |
| Sandbox | Full + Partial | Test tenant | Test instance | Limited | Sandbox + UAT | SF most flexible |
| API Versioning | Numbered (v62.0) | Release (2408) | Release (24D) | WSDL versioned | Platform (10.0.x) | SF most explicit |
| Query Language | SOQL | OData $filter | REST params | SuiteQL (SQL-like) | OData $filter | NS most familiar |
| Developer DX | Excellent | Good | Moderate | Moderate | Very Good | SF best DX |
| Error Detail | Detailed | Moderate | Basic | Moderate | Good | SF most helpful |
| Webhook Support | Outbound Msgs + Events | Business Events | Limited (OIC) | Events + Workflow | Business Events | SF most flexible |
Important Caveats
- Edition-specific limits vary dramatically: Salesforce Developer edition has 15K API calls/24h vs 5M for Unlimited. NetSuite Standard has 5 concurrent slots vs 20 for Enterprise. Always verify for your edition.
- Sandbox != Production: Salesforce sandbox has lower API limits. SAP test tenants may differ. D365 sandbox has fewer web servers. Load-test against realistic conditions.
- Rate limits change with each release: All five platforms can change API limits in any release. Always verify against current release notes before go-live.
- Cloud deployments only: On-premise variants (SAP ECC, Oracle EBS, Dynamics AX/GP) have fundamentally different API architectures not covered here.
- Salesforce is CRM-first: Included for API maturity and integration counterpart role, but not a traditional ERP. Financial/operational capabilities narrower than the other four.
Related Units