API Rate Limiting in NetSuite Integrations — Strategies That Hold Up at Volume
- NetSuite REST API has per-integration concurrency limits (typically 10 concurrent requests) not documented as hard numbers — they vary by account tier and response from Oracle support is inconsistent.
- SuiteTalk SOAP has a 10-concurrent-request limit clearly documented; exceeding it returns a CONCURRENT_CONNECTIONS_EXCEEDED error.
- The correct mitigation strategy: request queuing with exponential backoff, not retrying on a fixed interval.
- For integrations exceeding 1,000 requests/hour: use bulk record APIs (RESTlets or Map/Reduce) rather than individual record REST calls — fundamentally different rate limit envelope.
NetSuite rate limiting is one of the most poorly documented aspects of building integrations at scale. The limits are real, they vary by account tier, and they are enforced inconsistently. This guide covers what the limits actually are (based on production experience, not just documentation), and the queuing and retry patterns that prevent your integration from hitting them in the first place.
The Actual Limits You Will Encounter
| API type | Limit | Error returned when exceeded | Source |
|---|---|---|---|
| SuiteTalk SOAP | 10 concurrent connections per integration | CONCURRENT_CONNECTIONS_EXCEEDED | Documented by NetSuite |
| REST Record API | ~5–15 concurrent (varies by tier) | HTTP 429 Too Many Requests | Empirical — not clearly documented |
| SuiteQL REST queries | 5 concurrent queries per integration | HTTP 429 | Observed in production |
| RESTlets | 10 concurrent per integration | SSS_REQUEST_LIMIT_EXCEEDED | Documented by NetSuite |
| Map/Reduce scripts | Governance per key, not concurrency | SSS_USAGE_LIMIT_EXCEEDED | Documented by NetSuite |
Exponential Backoff: The Correct Retry Pattern
function callNetSuiteWithRetry(request, maxRetries = 5) {
let delay = 1000; // 1 second initial delay
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const response = await callNetSuiteAPI(request);
if (response.status === 429 || response.status === 503) {
await sleep(delay + Math.random() * 500); // jitter
delay = Math.min(delay * 2, 60000); // max 60 sec
continue;
}
return response;
} catch (e) {
if (attempt === maxRetries - 1) throw e;
await sleep(delay);
delay = Math.min(delay * 2, 60000);
}
}
}
The jitter component (Math.random() * 500) is critical — it prevents the "thundering herd" effect where all queued requests retry simultaneously after a rate limit, causing a second wave of rate limiting.
Bulk Over Rate-Limited: When to Use RESTlets
If your integration regularly makes more than 500 individual REST record API calls per hour, you will hit concurrency limits during peak periods. Switch to a RESTlet that accepts a batch payload (array of records) and processes them in a single SuiteScript execution. 500 individual calls becomes 5 batch calls of 100 records each — fundamentally different rate limit exposure.
References
- NetSuite REST Record APIOracle NetSuite Help — rate limiting documentation for the REST Record API.
- SuiteScript RESTlet ReferenceOracle NetSuite Help — RESTlet framework for bulk operations bypassing individual record API limits.
- AWS — Exponential Backoff and JitterAmazon Web Services Builder's Library — the canonical implementation reference for jittered exponential backoff retry logic.
Leave a Reply