NetSuite concurrency limits and SuiteScript governance units: why an integration fails at five concurrent requests and returns HTTP 400 instead of 429
11 min read Integration The Adapters team
Last updated September 2026
Field mapping auto-plugged · tap a port to rewire
Plug a source port into
Transform on this cable
JSON in
JSON out
5 sample records ready
A NetSuite account on the Standard service tier is allowed five concurrent web services requests. Not five per integration, five in total, shared by every application connected to the account across SOAP, REST and RESTlets combined. That one number breaks more NetSuite projects than any rate limit, and the error it produces is HTTP 400 rather than the 429 every retry library watches for. Here are the real limits by tier, read from Oracle NetSuite documentation on 24 August 2026, the six failures teams experience as one error, and seven changes that fix them.
Key takeaways
- Base concurrency is 5 on Standard, 15 on Premium, 20 on Enterprise and Ultimate. For contracts written from June 2020, and each SuiteCloud Plus license adds 10.
- The limit is account-wide, not per integration. Since the 2017.2 release, SOAP, REST and RESTlet requests all draw on one shared pool.
- A RESTlet breach returns HTTP 400, not 429. With the code SSS_REQUEST_LIMIT_EXCEEDED, which generic retry logic treats as permanent and drops.
- Scaling out makes it worse. Every retry issued while the original is still open adds another in-flight request against the same ceiling.
- Governance units are a second, separate budget. 1,000 for user event and client scripts, 5,000 for RESTlets, 10,000 for scheduled scripts.
- Ultimate has the same base limit as Enterprise. Upgrading the service tier alone buys no additional concurrency, only SuiteCloud Plus does.
What is the NetSuite integration concurrency limit?
It is the maximum number of web services requests your NetSuite account may have in progress at the same moment. For contracts written from June 2020 the account base limit is 5 concurrent requests on the Standard service tier, 15 on Premium, and 20 on both Enterprise and Ultimate. Each SuiteCloud Plus license raises the base limit by 10.
The word doing the work in that definition is account. Since the 2017.2 release, NetSuite governs SOAP web services, REST and RESTlet traffic together at the account level, so there is one pool and everything competes for it. Your ecommerce connector, your warehouse sync, the tax engine, the expense tool and whatever somebody wired up in an automation platform two years ago all draw from the same five, fifteen or twenty slots. The integration you are building is not the only tenant, and it is rarely the one that gets blamed when the ceiling is reached.
NetSuite concurrency limits by service tier
| Service tier | Base limit | With 1 SuiteCloud Plus | With 3 SuiteCloud Plus | In practice |
|---|---|---|---|---|
| Standard | 5 | 15 | 35 | The default for most mid-market accounts. Five slots for the entire account |
| Premium | 15 | 25 | 45 | Workable for a small estate provided nothing runs a wide parallel backfill |
| Enterprise | 20 | 30 | 50 | Comfortable day to day, still finite during a migration or a resync |
| Ultimate | 20 | 30 | 50 | Identical base to Enterprise. The tier upgrade on its own buys no concurrency |
| Development or partner | 5 | 5 | 5 | Fixed at five and does not scale with licenses, so sandbox load tests mislead |
Two rows in that table catch people out regularly. Ultimate carries the same base limit as Enterprise, so a tier upgrade bought for other reasons delivers no extra concurrency and the only lever that does is SuiteCloud Plus licensing. And development and partner accounts are pinned at five regardless of how many licenses the organization holds, which means a load test in a development account is not a rehearsal for production. If production has licenses the test understates what you can do. If production does not, the test is the only honest signal you will get and it is worth taking seriously.
Why is my NetSuite integration returning HTTP 400 instead of 429?
Because NetSuite does not use 429 for concurrency violations on RESTlets. A RESTlet
request rejected for exceeding the concurrency limit comes back as HTTP 400 Bad Request
with the SuiteScript error code SSS_REQUEST_LIMIT_EXCEEDED. SOAP web
services return a fault instead: ExceededConcurrentRequestLimitFault with
WS_REQUEST_BLOCKED under token-based authentication.
This matters more than it sounds. The convention across modern HTTP clients is that a 429 is transient and worth retrying, while other 4xx responses are client errors that will fail identically forever. So a well-behaved generic client, handed a NetSuite concurrency rejection, concludes the request is malformed and gives up on that record. The sync reports partial success. Nothing in the logs says "throttled". You discover the gap weeks later when a total does not match, and by then nobody connects it to a busy afternoon in March. Reading the error code out of the response body rather than trusting the status line is a small change that eliminates an entire category of silent loss.
Six NetSuite failures that look like one error
| What broke | Surface | Exact code | The fix that works |
|---|---|---|---|
| Too many requests in flight to a RESTlet | RESTlet | HTTP 400 Bad Request, SSS_REQUEST_LIMIT_EXCEEDED |
Reduce the size of the worker pool. Backoff alone does not help, because the request was rejected rather than queued |
| Concurrent SOAP calls under token-based authentication | SOAP web services | ExceededConcurrentRequestLimitFault, WS_REQUEST_BLOCKED |
Bound concurrency centrally across every process that shares the account, not per process |
| Concurrent SOAP calls using request-level credentials | SOAP web services | ExceededRequestLimitFault, WS_CONCUR_SESSION_DISALLWD |
Move to token-based authentication first, then bound concurrency |
| A script did more work than its allowance permits | SuiteScript | SSS_USAGE_LIMIT_EXCEEDED |
Move the work into a map/reduce script, the only type that yields and reschedules itself |
| A script ran longer than its stage allows | SuiteScript | SSS_TIME_LIMIT_EXCEEDED |
Split the job by stage. A 300 second RESTlet is the wrong home for bulk processing |
| A query quietly returned less than the full set | SuiteQL over REST | No error at all |
Chunk by date or key against the 100,000 result ceiling, or move to SuiteAnalytics Connect |
The last row is the one that costs real money, because it raises nothing at all. A SuiteQL query that would return more than 100,000 rows simply returns fewer, and the job is marked successful. Pipelines that trust job status build a warehouse table that looks complete and is not, and the finance team finds out during a close. The habit worth building is to watch the row counts and freshness of the tables you land rather than watching whether the job exited zero, because in this failure mode the job always exits zero.
Does scaling out make a NetSuite sync faster?
No, and past a certain point it makes it slower. Concurrency limits count requests in progress at a given instant rather than requests per second, so adding workers does not increase throughput once the pool is saturated, it increases rejections. Each rejected request is work done for nothing, and each retry issued while the original is still open occupies another slot.
This is the opposite of the intuition most engineers bring from working with rate-limited APIs, where more parallelism plus polite backoff genuinely does move more data. Against a NetSuite account with five slots, twenty workers spend most of their time being rejected and retrying, and the retries themselves crowd out the requests that would have succeeded. The correct shape is one bounded pool, sized below the account limit with headroom left for the integrations you did not write, shared across every process rather than configured per process. Two services each configured with a limit of four are not limited to four, they are limited to eight, and eight is over the ceiling on three of the five tiers.
What are SuiteScript governance units?
Governance units are NetSuite's budget for how much work a single script invocation may
do. Each script type has an allowance: 1,000 units for user event, client, Suitelet and
workflow action scripts, 5,000 for RESTlets, and 10,000 for scheduled scripts. Exceeding
it throws SSS_USAGE_LIMIT_EXCEEDED and ends the invocation mid-function, even
if the work is unfinished.
A second, separate ceiling applies at the same time: wall-clock execution time. Both are enforced independently, so a script can die from either. The table below is the one to keep, because it explains why a job that runs fine against a hundred records falls over at ten thousand, and why moving that same code into a different script type fixes it without any change to the logic.
SuiteScript governance units and time limits by script type
| Script type | Usage units | Execution time limit | Yields automatically |
|---|---|---|---|
| Map/reduce | 10,000 soft limit per map or reduce job | 3,600s input, 300s map, 900s reduce, 3,600s summarize | Yes, automatic |
| Scheduled | 10,000 | 3,600 seconds | No yield method exists in SuiteScript 2.x |
| RESTlet | 5,000 | 300 seconds | No |
| Suitelet | 1,000 | 300 seconds | No |
| User event | 1,000 | 300 seconds | No |
| Client | 1,000 | 300 seconds | No |
| Workflow action | 1,000 | 300 seconds | No |
| Mass update | 1,000 | Governed per record invocation | No |
| Bundle installation | 10,000 | Governed per invocation | No |
| SDF installation | 10,000 | Governed per invocation | No |
| Custom plug-in | 10,000 | Governed per invocation | No |
The design rule falls straight out of the last column. Map/reduce is the only type that yields and reschedules itself as it approaches a governance boundary, and its 10,000 unit figure is a soft limit per map or reduce job specifically so that no single job can monopolize a processor. Everything else stops hard. A scheduled script has no method in SuiteScript 2.x to set a recovery point, so when it exhausts its units it dies where it stands and NetSuite emails the script owner the number of units consumed. Map/reduce also carries two shape constraints worth knowing before you design around it: keys are capped at 3,000 characters, and a value larger than 10 MB returns an error.
How do I increase the NetSuite concurrency limit?
There is exactly one lever that adds capacity: SuiteCloud Plus licenses, each of which raises the account base limit by 10. Changing service tier does not reliably help, since Enterprise and Ultimate share the same base of 20. Development and partner accounts stay at 5 no matter how many licenses exist on the contract.
Before buying one, measure. The reason to be careful is that a concurrency ceiling and a badly shaped integration produce identical symptoms, and only one of them is fixed by spending money. An integration that walks records one at a time with forty workers will exhaust any limit you buy, whereas the same work expressed as a filtered SuiteQL query pulling a thousand rows per request may not need a license at all. Fix the shape first, then measure what is left, then buy the licenses that gap actually requires. Oracle does not publish a list price for SuiteCloud Plus, so any figure quoted online is a third-party estimate and should be treated as one.
Seven fixes that make a NetSuite integration hold up
01. Find the real number before you design anything
Your account base limit comes from the service tier, and every SuiteCloud Plus license adds ten on top. Then subtract what the rest of the business is already using, because tax engines, expense tools, payroll providers, shipping platforms and reporting connectors all install integration records and all draw from the same pool. Most NetSuite estates have more connected applications than the team remembers. The number you can safely design against is what is left after that inventory, not the headline figure for your tier.
02. Bound requests in flight, do not just slow them down
This is the distinction that decides whether a NetSuite integration works. A rate limit counts requests per unit of time, so sleeping between them fixes it. A concurrency limit counts requests in progress at one instant, and sleeping between requests does nothing if you still have twenty workers each holding a slot. Worse, a retry issued while the original is still open adds another in-flight request, so a naive retry loop makes the breach deeper. Use one bounded pool, sized below your limit, shared across every worker in every process.
03. Stop treating HTTP 400 as a permanent failure
A RESTlet concurrency violation returns HTTP 400 Bad Request with the SuiteScript error code SSS_REQUEST_LIMIT_EXCEEDED. Almost every HTTP client library treats 4xx other than 429 as a client error that will never succeed on retry, so it does not retry, and the record is dropped rather than delayed. Inspect the error code in the body, not just the status line. This single change converts a class of silent data loss into an ordinary transient error your pipeline already knows how to handle.
04. Put bulk work in map/reduce and nowhere else
Map/reduce is the only SuiteScript type that yields and reschedules automatically when it approaches a governance boundary, which is exactly why it exists. A scheduled script that runs out of usage units simply stops mid-function, because SuiteScript 2.x provides no way to set a recovery point in one. NetSuite then emails the script owner the unit count the script reached before it died, which is useful forensics and a poor operational alert. If a job processes an unpredictable number of records, it belongs in map/reduce.
05. Chunk every SuiteQL extract against the 100,000 row ceiling
A SuiteQL query through REST returns a maximum of 100,000 results, paged at up to 1,000 rows across a maximum of 1,000 pages. Cross that ceiling and you do not get an error, you get fewer rows. Any extract that could plausibly exceed it has to be chunked by date range or by internal ID, with each chunk verified. If the volume is routinely far above the ceiling, Oracle points you at SuiteAnalytics Connect over ODBC or JDBC against the NetSuite2.com data source, which is the supported mechanism for large analytical reads.
06. Reserve concurrency for the integration you do not control
Under Setup, Integration, Integration Management, Integration Governance, you can open an integration record and set a Concurrency Limit that caps that application. Oracle recommends using it only when you have a specific reason, and the reason it names is an external application you do not fully control that occasionally bursts. Note the quirk: the MAX Concurrency Limit shown is always one less than the total unallocated limit, because NetSuite permanently holds back a minimum of one slot for integrations without an allocation and for auto-installed records. You cannot allocate the whole pool.
07. Verify loads by totals, never by job status
The most expensive NetSuite failure produces no error anywhere. A truncated SuiteQL result, a map/reduce job that yielded and was rescheduled but whose downstream step already ran, or a chunk that silently returned nothing all leave you with a job marked successful and a table that is incomplete. The only reliable check is comparing a total in the destination against what NetSuite reports natively for the same period. Automating that comparison, so somebody hears about a row count that moved the wrong way, is worth more than any amount of retry logic.
Where this leaves you
NetSuite is not a difficult platform to integrate with, but it is an unusually explicit one about what it will let you do, and the constraints it enforces are not the ones most integration code was written to expect. Three separate budgets apply at once. Concurrency is requests in flight across the whole account. Governance units are work per script invocation. Execution time is wall-clock per stage. They are enforced independently and they fail differently, and a pipeline that instruments all three tells you which wall it hit before anyone opens a debugger.
If you are choosing a vendor rather than writing this yourself, the questions that separate a serious NetSuite connector from a checkbox on a feature list are on our NetSuite integration tools comparison, along with the full tier table and an honest look at where other tools beat us. For the specific routes, we cover NetSuite to Snowflake, NetSuite to Postgres and Shopify to NetSuite in detail. The wider build-or-buy question is in build versus buy for integrations, and the polling decision that drives most of this traffic in webhooks versus polling.
Sync NetSuite without starving your other integrations
Bounded concurrency sized to your account limit, chunked SuiteQL extracts, real retry handling on SSS_REQUEST_LIMIT_EXCEEDED and per-record logs you can read. Flat from $49 a month.
No credit card required.