This card compares API rate limits, throttling mechanisms, and governance models across the six most common enterprise ERP platforms as of early 2026. It covers cloud deployments only — on-premise SAP and Dynamics have different (or no) API throttling models.
| System | Role | API Surface | Rate Limit Model |
|---|---|---|---|
| Salesforce | CRM + Platform | REST, SOAP, Bulk API 2.0, Streaming | Hard daily quota by edition |
| SAP S/4HANA Cloud | ERP | OData v2/v4, SOAP, RFC | Infrastructure-level (API Management) |
| Oracle Fusion Cloud ERP | ERP | REST, SOAP, FBDI, BI Publisher | Per-request record cap + fair-use |
| Oracle NetSuite | ERP | REST, SuiteTalk SOAP, RESTlets, SuiteQL | Concurrency slots + rate windows |
| Dynamics 365 F&O | ERP | OData v4, Custom Services, DMF | Resource-based + optional user-based |
| Workday | HCM + Financials | REST, SOAP, RaaS, WQL | Undocumented / per-tenant |
| API Surface | Protocol | Best For | Max Records/Request | Rate Limit | Real-time? | Bulk? |
|---|---|---|---|---|---|---|
| Salesforce REST | HTTPS/JSON | Individual CRUD, <2K records | 2,000 per query page | 100K calls/24h (Enterprise) | Yes | No |
| Salesforce Bulk API 2.0 | HTTPS/CSV | Data migration, >2K records | 150M per file | 15,000 batches/24h | No | Yes |
| SAP OData v4 | HTTPS/JSON | Standard CRUD, queries | Configurable ($top) | Infrastructure-dependent | Yes | Via $batch |
| Oracle Fusion REST | HTTPS/JSON | Standard CRUD, queries | 499 per GET | Fair-use / not published | Yes | No |
| Oracle Fusion FBDI | File-based/CSV | Bulk data loading | Unlimited (file-based) | Scheduled job queue | No | Yes |
| NetSuite REST | HTTPS/JSON | Individual CRUD | 1,000 per query | Concurrency-governed | Yes | No |
| NetSuite SuiteTalk SOAP | HTTPS/XML | Bulk operations, search | 1,000 per search page | Shared concurrency pool | Yes | Partial |
| Dynamics 365 OData | HTTPS/JSON | Standard CRUD, queries | 5,000 per page | 6,000 req/5min/user/server | Yes | Via $batch |
| Dynamics 365 DMF | File-based | Bulk import/export | Unlimited (file-based) | Exempt from API limits | No | Yes |
| Workday REST | HTTPS/JSON | Individual CRUD | 100 default page size | Not publicly documented | Yes | No |
| Workday RaaS | HTTPS/JSON,CSV,XML | Report extraction | Up to 2GB per report | Not publicly documented | No | Yes |
| System | Limit Type | Value | Notes |
|---|---|---|---|
| Salesforce | Max records per REST query page | 2,000 | Use queryMore/nextRecordsUrl for pagination [src1] |
| Salesforce | Max composite subrequests | 25 | All-or-nothing by default [src1] |
| Salesforce | Max request body size | 50 MB | REST API [src1] |
| Salesforce | Max Bulk API file size | 150 MB | Per Bulk API 2.0 job [src1] |
| Oracle Fusion | Max records per GET | 499 | Hard limit, cannot be overridden [src6] |
| Oracle Fusion | Max records per POST | 500 | Recommended maximum [src6] |
| NetSuite | Max objects per query | 1,000 | REST defaults to 100 if unspecified [src3] |
| NetSuite | Max SuiteQL rows | 100,000 | Per single query [src3] |
| Dynamics 365 F&O | Max concurrent requests per user | 52 | Per user, per app ID, per web server [src2] |
| SAP S/4HANA | Default page size | 100 | Configurable via $top; no hard maximum documented [src5] |
| Workday | Default page size | 100-500 | Varies by endpoint; 200-500 recommended [src7] |
| System | Limit Type | Value | Window | Edition / License Differences |
|---|---|---|---|---|
| Salesforce | Total API calls | 100,000 base | 24h rolling | Enterprise: 100K + 1,000/user; Unlimited: 5M; Developer: 15,000; Performance: 5M [src1] |
| Salesforce | Bulk API batches | 15,000 | 24h rolling | Shared across all editions [src1] |
| Salesforce | Streaming events | 200,000 | 24h | Higher with add-on licenses [src1] |
| Salesforce | Concurrent long-running requests | 25 (prod) / 5 (dev) | Per org | Requests running >20 seconds [src1] |
| Dynamics 365 F&O | Requests per user per server | 6,000 | 5-min sliding window | Per user, per app ID, per web server [src2] |
| Dynamics 365 F&O | Execution time per user per server | 1,200 seconds | 5-min sliding window | Combined execution time [src2] |
| Dynamics 365 Dataverse | Licensed user requests | 40,000 | 24h | D365 Enterprise/Professional; Team Member: 6,000 [src4] |
| Dynamics 365 Dataverse | Non-licensed user pool | 500,000 base + 5,000/license | 24h | Tenant-level, shared; max 10M [src4] |
| NetSuite | Concurrent requests | 5-20 base | Rolling | Standard: 5; Premium: 15; Enterprise/Ultimate: 20; +10 per SuiteCloud Plus [src3, src8] |
| NetSuite | Per-user RESTlet concurrency | 5 | Rolling | Hard cap regardless of tier [src3] |
| Oracle Fusion | REST API calls | Not published | Fair-use | Throttles via 429 responses [src6] |
| SAP S/4HANA | API calls | Not published | Fair-use | Managed via API Management policies [src5] |
| Workday | API calls | Not published | Unknown | Tenant-specific; contact Workday support [src7] |
| System | Limit Type | Per-Transaction Value | Notes |
|---|---|---|---|
| Salesforce | SOQL queries | 100 sync / 200 async | Includes queries from triggers [src1] |
| Salesforce | DML statements | 150 sync / 300 async | Each insert/update/delete counts as 1 [src1] |
| Salesforce | DML records processed | 10,000 | Per transaction [src1] |
| Salesforce | CPU time | 10,000 ms sync / 60,000 ms async | Exceeded = transaction abort [src1] |
| Salesforce | Heap size | 6 MB sync / 12 MB async | Exceeded = transaction abort [src1] |
| Salesforce | Callouts (HTTP) | 100 | Per Apex transaction [src1] |
| NetSuite | Governance units per script | Varies by script type | Units consumed per API call (e.g., search = 10, record load = 5) [src3] |
| Dynamics 365 F&O | Execution time per request | Part of 1,200s combined pool | Tracked in 5-min window [src2] |
| System | Primary Auth Flow | Rate Limit Tracking | Token Lifetime | Notes |
|---|---|---|---|---|
| Salesforce | OAuth 2.0 JWT Bearer | Per org (daily) + per user (governor) | 2h default session | Connected app required [src1] |
| SAP S/4HANA | OAuth 2.0 / X.509 / Basic | Per infrastructure policy | Configurable | API Management handles throttling [src5] |
| Oracle Fusion | OAuth 2.0 / Basic Auth | Per tenant (fair-use) | Configurable | JWT bearer for server-to-server [src6] |
| NetSuite | Token-Based Auth (TBA) + OAuth 2.0 | Per account (concurrency) | Token: no expiry | TBA tokens can be revoked [src3, src8] |
| Dynamics 365 F&O | OAuth 2.0 (Entra ID) | Per user + per app ID + per web server | Entra token: 1h default | Service principals use non-licensed pool [src2, src4] |
| Workday | OAuth 2.0 / WS-Security | Per tenant | Configurable | Rate limits tied to integration system user [src7] |
START -- Which ERP system are you integrating with?
|
+-- Salesforce
| +-- < 2,000 records/operation? --> REST API (100K calls/24h)
| +-- > 2,000 records? --> Bulk API 2.0 (15K batches/24h, 150MB/file)
| +-- Need real-time events? --> Platform Events / CDC (200K events/24h)
| +-- Governor limits concern? --> Batch Apex (async: 200 SOQL, 60s CPU)
|
+-- SAP S/4HANA Cloud
| +-- Standard CRUD? --> OData v4 (no hard rate limit)
| +-- Bulk data? --> $batch requests or IDoc/BAPI
| +-- Need throttling? --> Deploy SAP API Management layer
|
+-- Oracle Fusion Cloud
| +-- < 500 records? --> REST API (499 max per GET)
| +-- > 500 records read? --> REST with offset pagination loops
| +-- Bulk import? --> FBDI (file-based, no API limit)
| +-- Bulk extract? --> BI Publisher reports / BIP
|
+-- NetSuite
| +-- < 1,000 records? --> REST API or SuiteQL
| +-- Concurrency concern? --> Reserve slots via Integration Records
| +-- Bulk operations? --> SuiteTalk SOAP with saved searches
| +-- Need real-time? --> RESTlets (5 concurrent per user max)
|
+-- Dynamics 365 F&O
| +-- Standard CRUD? --> OData v4 (6,000 req/5min/user/server)
| +-- Bulk import/export? --> DMF (exempt from API limits)
| +-- High volume real-time? --> Dual Write or Virtual Entities
| +-- Dataverse (CE)? --> 40,000 req/24h per licensed user
|
+-- Workday
+-- Standard CRUD? --> REST API (limits undocumented)
+-- Report extraction? --> RaaS (up to 2GB per report)
+-- Bulk query? --> WQL (Workday Query Language)
+-- Always implement --> Retry logic with exponential backoff
| Capability | Salesforce | SAP S/4HANA | Oracle Fusion | NetSuite | Dynamics 365 F&O | Workday |
|---|---|---|---|---|---|---|
| Daily API limit | 100K-5M (by edition) | Not published | Not published | Not published | 6K/5min/user/server | Not published |
| Concurrency limit | 25 long-running | Infrastructure | Not published | 5-20 base slots | 52/user/server | Not published |
| Max records/GET | 2,000 | Configurable ($top) | 499 | 1,000 | 5,000 | 100-500 |
| Bulk mechanism | Bulk API 2.0 (150MB) | $batch / IDoc | FBDI (file) | SuiteTalk lists | DMF (exempt) | RaaS (2GB) |
| Throttle response | HTTP 429 | Depends on infra | HTTP 429 | 429 (REST) / 403 (SOAP) | HTTP 429 | HTTP 429 |
| Retry-After header | Yes | Depends | Not guaranteed | Not guaranteed | Yes | Not documented |
| Governor/txn limits | Yes (extensive) | No | No | Yes (gov units) | Execution time pool | No |
| Limit transparency | Excellent | Low | Low | Good | Good | Poor |
| Limit configurability | Buy add-ons only | Full (via API Mgmt) | None | Buy SuiteCloud Plus | Buy capacity add-ons | Contact support |
| Sandbox limits | Lower than prod | Same as prod | Same as prod | Lower (dev = 5 slots) | Same (1 server in trial) | Same as prod |
| System | Code | Meaning | Cause | Resolution |
|---|---|---|---|---|
| Salesforce | REQUEST_LIMIT_EXCEEDED | Daily API limit hit | Exceeded 24h rolling quota | Wait for rolling window reset or purchase add-on API calls [src1] |
| Salesforce | GOVERNOR_LIMIT | Transaction limit exceeded | Too many SOQL/DML in one transaction | Bulkify code; use async processing [src1] |
| NetSuite | 429 Too Many Requests | Concurrency/rate exceeded | All concurrency slots occupied | Exponential backoff; reserve dedicated slots [src3] |
| NetSuite | SSS_REQUEST_LIMIT_EXCEEDED | RESTlet rate limit | Per-user RESTlet concurrency exceeded | Reduce parallel RESTlet calls; max 5 per user [src3] |
| Dynamics 365 | 429 Too Many Requests | Service protection triggered | 6,000 requests in 5-min window OR resource threshold | Respect Retry-After header; distribute across time [src2] |
| Oracle Fusion | 429 Too Many Requests | Fair-use throttle triggered | Sustained high request volume | Implement backoff; switch to FBDI for bulk [src6] |
| Workday | 429 Too Many Requests | Rate limit exceeded | Exceeded tenant-specific threshold | Exponential backoff with jitter [src7] |
Test against a full-copy sandbox provisioned from your actual production edition. [src1]Reserve dedicated concurrency per integration record; migrate SOAP to REST where possible. [src3]Start at 1,200 req/min and increase until 429 errors appear, then use Retry-After intervals. [src2]Always check the hasMore attribute; loop with offset pagination. [src6]Deploy SAP API Management with spike arrest and quota policies before production. [src5]Always implement retry with exponential backoff; contact Workday support for tenant-specific limits. [src7]// BAD -- treating all ERPs like Salesforce with a single daily counter
const MAX_DAILY_CALLS = 100000; // This is Salesforce-specific!
let callCount = 0;
async function callErpApi(erpSystem, endpoint) {
if (callCount >= MAX_DAILY_CALLS) {
await waitForReset();
}
callCount++;
return fetch(endpoint);
}
// GOOD -- each ERP has its own throttling model
const rateLimitStrategies = {
salesforce: { type: 'daily_quota', limit: 100000, window: '24h' },
netsuite: { type: 'concurrency', maxSlots: 15, retryOn: [429, 403] },
dynamics365: { type: 'sliding_window', limit: 6000, window: '300s' },
oracle_fusion: { type: 'fair_use', retryOn: [429] },
sap: { type: 'infrastructure', managed: 'api_management' },
workday: { type: 'undocumented', retryOn: [429] },
};
async function callErpApi(erpSystem, endpoint) {
const strategy = rateLimitStrategies[erpSystem];
try {
return await fetchWithRetry(endpoint, strategy);
} catch (e) {
if (strategy.retryOn?.includes(e.status)) {
const retryAfter = e.headers?.get('Retry-After') || 5;
await sleep(retryAfter * 1000);
return callErpApi(erpSystem, endpoint);
}
throw e;
}
}
// BAD -- looping REST calls to import 10,000 records (499 max per request)
for (let offset = 0; offset < 10000; offset += 499) {
await fetch(`/fscmRestApi/resources/v1/invoices?limit=499&offset=${offset}`);
// 21 API calls minimum, each potentially throttled
}
// GOOD -- use File-Based Data Import for bulk operations
const fbdiPayload = {
OperationName: "importBulkData",
DocumentContent: base64EncodedCsv,
ContentType: "text/csv",
DocumentAccount: "fin$/journal$/import$"
};
await fetch('/fscmRestApi/resources/v1/erpintegrations', {
method: 'POST',
body: JSON.stringify(fbdiPayload)
});
// BAD -- all integrations compete for the same pool
// Integration A: SOAP sync (holds slots for 5-10s per request)
// Integration B: REST real-time (needs <1s responses)
// Integration C: RESTlet webhooks (blocked by A and B)
// Result: Integration C gets 429 errors during peak hours
// GOOD -- allocate slots via NetSuite Integration Records
// Setup > Integration > Integration Management > Integration Governance
// Integration A (SOAP sync): 8 reserved slots
// Integration B (REST real-time): 5 reserved slots
// Integration C (RESTlets): 3 reserved slots
// Unallocated: 1 slot minimum (required by NetSuite)
// Total: 17 slots = Premium tier (15 base + 1 SuiteCloud Plus license)
Always provision a full-copy sandbox from your production org for integration testing. [src1]Monitor execution time via Dynamics 365 telemetry; simplify OData queries. [src2]Check Setup > Integration > Integration Governance for actual account limits. [src3, src8]Always implement pagination with hasMore attribute checking. [src6]Read this comparison card before designing multi-ERP integrations.Deploy API Management with spike arrest before go-live. [src5]# === SALESFORCE: Check remaining API quota ===
curl -H "Authorization: Bearer $SF_TOKEN" \
"https://yourInstance.salesforce.com/services/data/v62.0/limits" \
| jq '.DailyApiRequests'
# Returns: { "Max": 100000, "Remaining": 87234 }
# === NETSUITE: Check concurrency governance ===
# Navigate to: Setup > Integration > Integration Management > Integration Governance
# Or use SuiteQL:
curl -X POST "https://ACCOUNT_ID.suitetalk.api.netsuite.com/services/rest/query/v1/suiteql" \
-H "Authorization: OAuth ..." \
-d '{"q": "SELECT * FROM integrationGovernance"}'
# === DYNAMICS 365 F&O: Monitor throttling ===
# Check Lifecycle Services (LCS) > Environment Monitoring > Raw Logs
# Filter for: ThrottledRequests
# === DYNAMICS 365 DATAVERSE: Check daily allocation ===
# Power Platform Admin Center > Licensing > Capacity add-ons > Download reports
# === ORACLE FUSION: Test pagination behavior ===
curl -u "user:pass" \
"https://your-instance.fa.ocs.oraclecloud.com/fscmRestApi/resources/v1/invoices?limit=499&offset=0" \
| jq '.hasMore'
# If true, increment offset by 499 and repeat
# === SAP S/4HANA: Check API Management metrics ===
# SAP Integration Suite > API Management > Analyze > API Traffic
| Capability | Salesforce | SAP S/4HANA | Oracle Fusion | NetSuite | Dynamics 365 F&O | Workday |
|---|---|---|---|---|---|---|
| Rate Limit Architecture | Hard daily quota + governor limits | No built-in; infrastructure-managed | Fair-use / undocumented | Concurrency slots + rate windows | Resource-based + sliding window | Undocumented / per-tenant |
| Daily API Cap | 100K-5M by edition | Unlimited (without API Mgmt) | Not published | Not published | 6K/5min/user/server (F&O) or 40K/24h/user (Dataverse) | Not published |
| Concurrency Cap | 25 long-running | Dialog work processes | Not published | 5-20 base + 10/SuiteCloud Plus | 52/user/app/server | Not published |
| Max Records/GET | 2,000 | Configurable ($top) | 499 (hard cap) | 1,000 | 5,000 | 100-500 |
| Bulk API | Bulk API 2.0 (150MB/file) | $batch, IDoc, BAPI | FBDI (file-based) | SuiteTalk lists (1K/page) | DMF (exempt from limits) | RaaS (2GB/report) |
| Transaction/Governor Limits | Yes: 100 SOQL, 150 DML, 10K records, 10s CPU | No | No | Yes: governance units per script | Combined execution time pool | No |
| HTTP 429 + Retry-After | Yes, both | Depends on API Mgmt | 429 yes, Retry-After not guaranteed | 429 (REST) / 403 (SOAP) | Yes, both | 429 yes, Retry-After unclear |
| Limit Transparency | Excellent (API endpoint) | Low | Low | Good (admin panel) | Good (admin center) | Poor |
| Buy More Capacity | API call packs | Scale infrastructure | Contact Oracle sales | SuiteCloud Plus (+10 slots) | Capacity add-on (+50K/24h) | Contact Workday |
| Limit Scope | Per org (daily) / per txn (governor) | Per tenant (infrastructure) | Per tenant (fair-use) | Per account (concurrency + rate) | Per user + app + server | Per tenant |
| Sandbox Differences | Lower limits than production | Same as production | Same as production | Developer = 5 concurrent max | Trial = 1 web server | Same as production |
| Use When | Don't Use When | Use Instead |
|---|---|---|
| Planning integration architecture for multi-ERP environments | Need detailed single-system API reference | System-specific API capability card |
| Capacity planning for a new ERP integration project | Choosing which ERP to buy | business/erp-selection category |
| Debugging rate limit errors across different ERP systems | Need authentication flow details | business/erp-integration/erp-authentication-comparison/2026 |
| Comparing ERP platforms for integration friendliness | Need bulk import specifics | business/erp-integration/erp-bulk-import-comparison/2026 |
| Designing retry/backoff strategy for multi-ERP middleware | Need code-level API tutorials | System-specific API capability card |