A Practical NetSuite Migration Guide: Moving Off SOAP Before 2028
Oracle is deprecating NetSuite SOAP APIs by 2028. Learn how to architect a modern migration using SuiteQL, REST, and RESTlets to avoid concurrency traps.
Oracle NetSuite is aggressively phasing out its legacy SOAP web services. If your B2B SaaS platform relies on SuiteTalk SOAP endpoints to sync accounting, inventory, or HRIS data—or if you are connecting AI agents to ERP data—you are currently operating on borrowed time.
Oracle has set a hard deadline: by the 2028.2 NetSuite release, all SOAP endpoints will be permanently disabled, and SOAP-based integrations will stop working. If you maintain a NetSuite integration at a B2B SaaS company, this is not a "nice to have" migration. It is a mandated one with a fixed countdown. Engineering teams must immediately plan to create a practical NetSuite migration guide without SOAP to avoid breaking critical customer workflows.
Migrating off NetSuite SOAP is rarely a simple endpoint swap. Modern REST APIs offer predictable semantics, standard JSON payloads, and logical routing. NetSuite's API ecosystem offers none of these out of the box. Attempting to map legacy SOAP XML requests directly to NetSuite's SuiteTalk REST API will immediately expose your infrastructure to severe performance bottlenecks, missing metadata, and aggressive concurrency throttling.
This guide provides the architectural playbook you need to survive this transition. We will examine the exact deprecation timeline, why a naive move to REST is an engineering trap, and how to architect a modern, reliable NetSuite integration using a hybrid approach of SuiteQL, REST, and SuiteScript.
The NetSuite SOAP API Deprecation Timeline (2025–2028)
Oracle has been methodical about this retirement. The deprecation is a phased rollout, not a sudden cliff. Oracle's messaging to developers is unambiguous: any new capability introduced in NetSuite will only be available through modern REST surfaces. The legacy XML endpoints are entering a strict maintenance phase.
Here is the strict schedule you must architect around:
| Release | What Happens |
|---|---|
| 2025.2 | Last planned SOAP endpoint ships. No subsequent SOAP endpoints will be released unless required for critical business continuity. |
| 2026.1 | No new features will be exposed via SOAP. Any new capabilities introduced in NetSuite will only be available through REST. |
| 2027.2 | SOAP usage is restricted exclusively to the single 2025.2 endpoint. All older endpoints are permanently retired. |
| 2028.2 | Complete removal. All SOAP endpoints are disabled. All SOAP integrations break. |
Oracle's own documentation is blunt about the reasoning: SOAP does not support the latest business features, new records have not been made available to SOAP for years, and the underlying XML technology stack does not support modern architecture standards like SuiteAnalytics Workbooks and SuiteScript 2.x Analytics APIs.
For engineering teams, this timeline creates an immediate architectural mandate. You cannot wait until 2027 to begin this migration. Enterprise ERP integrations are notoriously difficult to refactor. Gartner research indicates that 75% of ERP implementation projects run into serious setbacks, often due to integration architecture and data mapping failures. Rebuilding a NetSuite integration requires auditing hundreds of custom fields, validating multi-currency routing logic, and rewriting complex data transformations.
If you are still on SOAP right now, you are already accumulating technical debt with every NetSuite release. You must transition your infrastructure to communicate directly with NetSuite's modern API surfaces before the hard cutoff.
Why "Just Use the REST API" is a Migration Trap
When developers realize SOAP is dying, their first instinct is to rewrite every request to target the suitetalk.api.netsuite.com/services/rest/record/v1/ endpoints. The most common mistake teams make is treating this as a find-and-replace from SOAP to REST.
This approach will fail in production. The SuiteTalk REST API is fundamentally designed for single-record CRUD operations. It is actively hostile to bulk data extraction and complex relational queries. Attempting a 1-to-1 migration will expose you to three severe limitations:
1. The N+1 Sub-Resource Problem
The most severe limitation is how the REST Record Service handles sub-resources. When retrieving a list of records, the REST API does not allow developers to use the expandSubResources parameter.
If you query a list of 200 Purchase Orders and need the line-item details for each, the REST API forces you to make 200 additional, separate API calls. SOAP's legacy getList operation returned everything in one shot. REST forces you into a massive N+1 query bottleneck.
2. No Ordering and Hard Pagination Ceilings
The REST Record API lacks basic querying flexibility. You cannot specify the order in which records are returned—there is no equivalent of an ORDER BY clause. Furthermore, you can get a maximum of 1,000 records per page, and you can only retrieve the first 1,000 pages of results. For enterprise NetSuite accounts that have a massive volume of historical transaction records, this hard ceiling makes complete data synchronization impossible.
3. Single-Record Writes (Until Recently)
Historically, NetSuite's REST Record API processed exactly one record per request for creates or updates, whereas SOAP handled batch operations of up to 1,000 records. The 2026.1 release finally introduces homogeneous batch operations for REST, allowing developers to submit multiple same-type operations in a single asynchronous REST call. However, if your customers are on older releases or rely on heterogeneous batches, you are stuck with one-at-a-time writes.
The bottom line: a 1-to-1 SOAP-to-REST migration will dramatically increase your API call volume. Given NetSuite's strict concurrency limits, this spike in network requests will instantly trigger a cascade of HTTP 429 errors and block all other integration traffic for that customer. To safely architect a reliable NetSuite API integration, you must abandon the idea of a pure REST implementation.
The Tri-Partite Architecture: SuiteQL, REST, and RESTlets
The real replacement for SOAP isn't just REST. It is a combination of three distinct API surfaces, each handling what it does best. To bypass the limitations of the REST API, your integration layer must intelligently route requests based on the operation type.
flowchart TD
A[Unified API Request] --> B{Operation Type}
B -->|Complex Reads / Lists / JOINs| C[SuiteQL Endpoint]
B -->|Single Record Write / Update| D[SuiteTalk REST API]
B -->|PDFs / Dynamic Metadata / Gaps| E[SuiteScript RESTlet]
C --> F[(NetSuite Database)]
D --> F
E --> F1. SuiteQL: Your Primary Read Layer
SuiteQL is NetSuite's proprietary, SQL-like query language. It is exposed via a POST request to /services/rest/query/v1/suiteql. It is the single biggest upgrade over SOAP for data reads, and nearly all list and get operations in your migration should target this endpoint.
Unlike the REST Record API, SuiteQL allows you to execute complex JOINs across related tables in a single network request. A query for vendors can easily JOIN entity addresses, subsidiary relationships, and currency tables without triggering N+1 bottlenecks. It supports advanced WHERE clauses, aggregation (SUM, COUNT, GROUP BY), case-insensitive LIKE searches, standard offset pagination, and computed columns via BUILTIN.DF() for display values.
Here is what a typical SuiteQL vendor list query looks like:
SELECT
v.id,
v.companyname,
v.email,
ea.addr1,
ea.city,
ea.state,
ea.zip,
BUILTIN.DF(v.subsidiary) AS subsidiary_name
FROM vendor v
LEFT JOIN entityaddress ea ON v.defaultbillingaddress = ea.nkey
WHERE v.isinactive = 'F'
AND v.lastmodifieddate >= '2026-01-01'
ORDER BY v.companyname
FETCH FIRST 100 ROWS ONLYOne API call. Vendors, addresses, subsidiary names. No N+1. The trade-off is that SuiteQL is strictly read-only. It is your data extraction engine, not your mutation engine.
2. SuiteTalk REST: Your Write Layer
Reserve the SuiteTalk REST API exclusively for writes. When your application needs to create a customer, update an invoice, or delete a tracking category, format a standard JSON payload and issue a POST, PATCH, or DELETE request to the specific record endpoint.
Because these are targeted, single-record operations, they are less likely to exhaust concurrent thread limits. For single-record reads where you need expanded sub-resources (like line items on a specific purchase order), the REST API with ?expandSubResources=true works well. Just do not use it for bulk listing.
3. RESTlets (SuiteScript): Filling the Gaps REST Can't
Certain critical capabilities are entirely absent from both SuiteQL and the REST API. Oracle's own documentation acknowledges that developers should not expect 100% parity of REST with SOAP, explicitly stating that when an object or method is not available in REST, developers should use SuiteScript RESTlets instead.
For these edge cases, you must deploy a custom SuiteScript Suitelet into the customer's NetSuite account:
- PDF Generation: The REST API has no PDF rendering capability. If your integration needs to download a Purchase Order PDF, your Suitelet must utilize the server-side
N/rendermodule to generate the binary viarender.transaction(). - Dynamic Form Metadata: NetSuite forms are highly dynamic. Custom fields vary per account, and select options change based on the current record state. The standard REST metadata catalog provides basic schema info, but it cannot tell you which fields are mandatory on a specific custom form at runtime. A deployed Suitelet can create an in-memory record using
record.create()and introspect its properties to return accurate field IDs and mandatory flags. - Legacy Tax Data: Currently, the SuiteQL
salestaxitemtable does not expose the full tax rate configuration, including nested tax type references. If your application requires deep tax compliance data, a RESTlet acts as your escape hatch.
Handling NetSuite Authentication and Concurrency Limits
The OAuth 1.0 TBA Math Problem
NetSuite uses OAuth 1.0 Token-Based Authentication (TBA) for server-to-server integrations. This is not modern OAuth 2.0. Every single API request requires an Authorization header containing an OAuth 1.0 signature that must be computed dynamically.
The signature requires combining five credentials (the Consumer Key and Consumer Secret from the integration record, plus the Token ID and Token Secret from the TBA access token, and the Account ID), a randomly generated nonce, a Unix timestamp, the full canonical request URL, and the HTTP method.
These elements must be sorted and encoded according to strict OAuth 1.0 parameter rules to form a base string. You then compute the signature using HMAC-SHA256(consumer_secret&token_secret, base_string). A single misplaced character or incorrect URL encoding will result in an opaque authentication failure.
Building this logic from scratch is a massive engineering tax. This is why shipping API connectors as data-only operations using declarative configuration—where the auth scheme, credential paths, and signature algorithm are defined as data rather than hardcoded logic—is vastly superior to writing custom NetSuite authentication handlers. (Note: NetSuite's newer releases do support OAuth 2.0 for REST, which you should evaluate to simplify your auth layer going forward, but TBA remains ubiquitous).
Surviving Concurrency Limits, Not Rate Limits
Here is a point many teams get wrong: NetSuite does not impose traditional daily API rate limits. It enforces concurrent thread limits based on the customer's service tier.
The default tier allows just 15 concurrent requests. Tier 2 gets 25; Tier 3 gets 35; Tier 4 gets 45; Tier 5 gets 55. Each SuiteCloud Plus license adds 10 additional concurrent threads to this base pool. This means you can make millions of requests per day—as long as no more than your allotted limit are in flight at the exact same time.
When you exceed the allowed concurrent threads, NetSuite immediately responds with an HTTP 429 Too Many Requests error. How your external system reacts determines whether your integration recovers or crashes entirely. A common mistake in custom middleware is to retry a failed request immediately. NetSuite rejects Request A, middleware immediately retries Request A, meanwhile Request B arrives, and NetSuite rejects both, causing a distributed deadlock.
Your integration architecture must treat HTTP 429s as an expected operational state. The correct pattern is exponential backoff with jitter. The platform should normalize upstream rate limit information into standardized headers (ratelimit-limit, ratelimit-remaining, ratelimit-reset) per the IETF specification. When NetSuite returns a 429, you must pass that error directly back to the caller. Do not automatically retry or silently absorb rate limit errors in a proxy layer, as this obscures the real concurrency situation. If you want to know how to normalize pagination and error handling across 50+ APIs, the secret is aggressive standardization at the proxy layer and disciplined retry logic at the client layer.
How to Create a Practical NetSuite Migration Guide Without SOAP
To successfully move your infrastructure off legacy XML endpoints without breaking production, execute the following architectural steps.
1. Audit Existing SOAP Payloads for Complex JOINs
Begin by logging every legacy SOAP request your system currently makes. Categorize each by operation type (read, write, metadata), record types touched, and batch usage. Identify the operations that rely heavily on nested XML structures to pull data across multiple NetSuite tables. These are the danger zones. Do not attempt to map these to the REST Record API. Document the exact tables and fields required, and prepare to rewrite these extractions as SuiteQL queries.
2. Implement Polymorphic Resource Routing
NetSuite's underlying data model is heavily fragmented. From an accounting perspective, vendors and customers are both simply entities you transact with. However, NetSuite treats them as entirely separate record types with separate database tables. Similarly, classes, departments, and locations are all forms of organizational segmentation but exist in isolation.
When migrating off SOAP, do not expose this fragmentation to your internal application logic. Implement a mapping configuration that links unified fields to provider-specific fields. Create a single, polymorphic contacts resource in your application. Use a query parameter or discriminator field (contact_type) to dynamically route the request to either the vendor or customer SuiteQL table or REST endpoint. This isolates your core application from NetSuite's schema quirks and matches how modern accounting platforms expose similar concepts.
3. Build Feature-Adaptive Query Logic
No two NetSuite instances are identical. NetSuite is a platform with wildly different configurations per customer. Some use NetSuite OneWorld (which requires subsidiary mapping), while others use standard editions. Some operate in multi-currency environments, while others are single-currency.
Your SuiteQL queries cannot be static strings; they must be feature-adaptive. A vendor query that JOINs the currency and subsidiary tables will fail fatally on a standard account that doesn't have those features enabled. During the initial connection setup, your integration should query the customer's NetSuite metadata to detect the active features. Based on this context, your query construction logic must dynamically include or exclude specific JOINs.
4. Transition to Declarative Mappings
Maintaining integration-specific code for NetSuite is a massive liability. Every time Oracle updates a schema requirement, you are forced to initiate a code deployment.
Transition your integration layer to use declarative mappings. Define the translation between your application's unified schema and NetSuite's native format using functional expression languages like JSONata. This allows you to handle NetSuite's flat PascalCase fields, custom field detection, and dynamic URL generation entirely as data operations. Separating your mapping logic from your execution pipeline is non-negotiable for long-term stability.
5. Run in Parallel, Then Cut Over
Do not attempt a big-bang migration. Run your new REST/SuiteQL integration in shadow mode alongside the existing SOAP integration:
- Route reads through SuiteQL first, and compare the JSON output to your legacy SOAP XML output.
- Once reads match flawlessly, route writes through the REST API, verifying the results against SOAP.
- Cut over workflow by workflow, hidden behind feature flags.
- Decommission SOAP paths only once validated in production.
More than 70% of ERP initiatives fail due to poor change management and insufficient testing. A parallel-run approach drastically de-risks the transition.
Securing Your NetSuite Infrastructure for 2028
The SOAP shutdown is not a distant problem. The countdown is already running, and the degradation of SOAP performance has already begun. Engineering teams that migrate to a SuiteQL-first architecture today will secure a massive reliability advantage over competitors still wrestling with legacy XML.
If you are a B2B SaaS company building NetSuite integrations for your customers—particularly if you need to build ERP integrations without storing customer data—the complexity multiplies. Handling OAuth 1.0 TBA math, SuiteQL orchestration, polymorphic routing, and feature-adaptive queries across dozens of unique customer accounts is a significant engineering investment. To go deeper on abstracting this architecture, read our guide on how to integrate the Oracle NetSuite API without SOAP complexity or explore enterprise auth patterns to see how declarative configuration solves the TBA signature problem out of the box.
FAQ
- When will the NetSuite SOAP API stop working?
- NetSuite SOAP web services will be completely removed in the 2028.2 release. The 2025.2 endpoint is the last planned SOAP release, and starting with 2026.1, no new SOAP endpoints are being shipped. You must migrate before 2028.
- Can I replace NetSuite SOAP operations with the SuiteTalk REST API?
- A 1-to-1 replacement will fail. The REST Record API lacks efficient sub-resource expansion for lists, has hard pagination ceilings, and lacks ordering. This triggers N+1 query bottlenecks that exhaust NetSuite's strict concurrent thread limits.
- What is SuiteQL and why should I use it instead of REST for reads?
- SuiteQL is NetSuite's SQL-like query language accessed via the REST API. It supports multi-table JOINs, complex WHERE clauses, and aggregation in a single API call, completely eliminating the N+1 problem inherent in the REST Record API.
- How does NetSuite handle API rate limits?
- NetSuite does not impose traditional daily rate limits. Instead, it enforces concurrent thread limits based on your service tier (typically starting at 15 concurrent requests). Exceeding this limit returns HTTP 429 errors, requiring caller-side exponential backoff.
- How do I authenticate with the NetSuite API?
- NetSuite relies heavily on OAuth 1.0 Token-Based Authentication (TBA). Every request requires an HMAC-SHA256 signature dynamically computed from your Consumer Key, Consumer Secret, Token ID, Token Secret, Account ID, a nonce, and a timestamp.