Most "NetSuite headless commerce" guides stop at the strategy. This one shows the architecture — the API surface, the rate-limit math, and the sync that keeps NetSuite and a fast storefront honest with each other.
The premise is simple: keep NetSuite as the system of record, and decouple the storefront. NetSuite stays authoritative for inventory, pricing, customers, and orders. A modern front end — React, Next.js, or Shopify Hydrogen — runs on a headless commerce platform like Shopify or Medusa, which an integration layer keeps in sync with NetSuite. Done right, you get a sub-second storefront without ever lying about stock or price.

We design and build this as a service
A modern React storefront on Medusa or Shopify, kept in two-way sync with NetSuite through Gemstone, our connector. We can stand up a working example in your NetSuite sandbox in 48 hours.
See the headless commerce serviceWhat "headless" actually means
"Headless" describes a split, not a product. In a traditional platform the storefront (the "head") and the commerce back end ship as one coupled application. Going headless means decoupling them: the back end exposes its data and logic through an API, and the storefront becomes a separate app that consumes it. The payoff is independence — you can redesign or replace the front end (a new framework, a fresh design, a native app) without rebuilding the business logic behind it, and ship front-end changes without redeploying the back end.
The counter-example is SuiteCommerce itself. NetSuite's native commerce platform — SuiteCommerce and SuiteCommerce Advanced — is the opposite of headless: the storefront and the NetSuite back end ship as a single SuiteScript application, deployed and versioned together. That tight coupling is what makes it fast to launch, and also what makes it hard to customize past a certain point. Going headless is the deliberate choice to break that coupling — trading the all-in-one convenience for a front end you fully control and a back end you reach through an API.
The head is the customer-facing front end — React, Next.js, or Shopify Hydrogen. The body is everything behind the API: your commerce logic and your system of record. The platforms people reach for here are themselves headless back ends — Medusa (open-source, self-hosted) or Shopify through its Storefront API — each providing the commerce engine while you bring your own front end. NetSuite sits one layer deeper, as the system of record for inventory, pricing, and orders. Keeping the layers straight matters: Next.js, Hydrogen, and React are heads; Medusa and Shopify are bodies; NetSuite is the system of record behind them.
Here is how the pieces fit together.
The architecture at a glance
The layer that matters here is the headless platform itself — Medusa, Shopify, or similar. It owns the storefront's data and serves it fast; an integration layer keeps that data in sync with NetSuite. That sync is the part teams skip and then regret: get it wrong and your storefront either lies about stock and price or hammers NetSuite's governance limits the first time a crawler or a traffic spike arrives.
NetSuite's API surface — pick the right door
NetSuite exposes several APIs, and choosing the wrong one is the most common early mistake.
| API | Best for | Notes |
|---|---|---|
| SuiteTalk REST Record | CRUD on individual records | Clean, governance-friendly, per-record |
| RESTlets (SuiteScript) | Custom endpoints, shaped payloads | You write the logic; full control over the response |
| SOAP SuiteTalk | Legacy integrations | Avoid for new headless builds |
For a storefront, two of those doors do the real work — and the tool that makes reads fast across both is SuiteQL, NetSuite's SQL-like query language. You don't call SuiteQL as a separate API; you run it through the SuiteTalk REST query endpoint, or inside a RESTlet via the N/query module. Either way it's the fastest read path on the platform: one shaped query instead of thousands of per-record calls. The pattern that scales is a thin RESTlet that runs a SuiteQL query and returns exactly the fields the consumer needs. SuiteTalk REST Record is fine for the occasional single-record write or lookup, but reading a catalog one record at a time will not.
A RESTlet that serves the catalog via SuiteQL
/**
* @NApiVersion 2.1
* @NScriptType Restlet
*/
define(['N/query'], (query) => {
const get = () => {
// SuiteQL reads are far cheaper than per-record REST calls.
// For catalogs over a few thousand rows, page the results.
const results = query
.runSuiteQL({
query: `
SELECT i.id, i.itemid, i.displayname, i.baseprice,
i.isinactive, i.custitem_brand AS brand
FROM item i
WHERE i.isinactive = 'F'
AND i.matrixtype IS NULL
ORDER BY i.itemid
`,
})
.asMappedResults();
return results;
};
return { get };
});For large catalogs use query.runSuiteQLPaged({ query, pageSize: 1000 }) and walk the pages, rather than pulling everything in one call. The point stands: one shaped query beats thousands of record reads.
The rate-limit reality
This is the constraint that defines the whole design. NetSuite governs concurrent requests per account: SOAP, REST, and RESTlet calls all draw from one shared, account-wide budget, set by your service tier.
| Service tier | Base concurrent requests |
|---|---|
| Standard | 5 |
| Premium | 15 |
| Enterprise | 20 |
| Ultimate | 20 |
Each SuiteCloud Plus license adds +10 (Premium with two licenses = 35); sandbox and development accounts are fixed at 5 and don't scale. Exceed the limit and NetSuite returns HTTP 429 — Request Limit Exceeded — and every script operation also burns a per-execution governance budget on top of that. The implication is blunt: a Standard-tier account gets five simultaneous calls before throttling, so a storefront that hits NetSuite on every request will exhaust it the first time a crawler or a traffic spike arrives.
The fix is not "buy more concurrency." The fix is to keep NetSuite off the shopper's hot path — sync its data into the headless database ahead of time, and reserve live calls for the few values that genuinely must be fresh, like stock at add-to-cart or order creation at checkout. Which raises the real question: where does the data live?
Where the records actually live
In a headless build, the records a shopper browses — products, customers, orders — live in the headless platform's own database, not in NetSuite. That platform is usually Medusa or Shopify in the work we do, but the shape is the same for any headless commerce backend. NetSuite stays the ERP system of record; a sync layer keeps the two in step, pulling catalog and pricing into the headless database and pushing new orders back into NetSuite. The storefront reads from a fast local database and never touches NetSuite on a page view.
The trade-off is that you now own a sync, with the reconciliation and drift problems any copy of the truth brings (more on those below).
Building the sync
The sync is the integration, and there are two honest ways to build it.
1. An iPaaS. Platforms like Celigo, Boomi, or n8n are built for exactly this — pre-built NetSuite connectors, scheduling, field mapping, retries, throttling, and backoff against NetSuite's limits, all configured rather than coded. For most teams it's the fastest, most reliable way to stand up a sync, because you're configuring a proven engine instead of debugging your own.
2. A queue-backed module inside the headless platform. When you need custom logic or tighter control, you run the sync as an extension of the platform itself — Medusa modules and subscribers, or Shopify apps and webhooks — rather than a separate service. The non-negotiable piece is a queue in front of NetSuite: a job queue like BullMQ (Redis-backed) caps how many requests hit NetSuite at once and rate-limits them to your tier's budget, with retries and dead-letter handling built in — so NetSuite is fed at a controlled pace instead of absorbing an uncontrolled fan-out.
// netsuite-sync.ts — a worker that drains writes into NetSuite at a safe pace
import { Worker } from 'bullmq';
import { pushToNetSuite } from './netsuite';
new Worker('netsuite-sync', (job) => pushToNetSuite(job.data), {
connection: { host: process.env.REDIS_HOST!, port: 6379 },
// Stay under your tier's concurrency budget (5 on Standard) with headroom.
concurrency: 3,
// Hard ceiling: at most 5 NetSuite calls per second, no matter how many queue up.
limiter: { max: 5, duration: 1000 },
});However you build it, two rules hold. Push NetSuite changes outward with User Event webhooks instead of polling, so the headless database updates the moment something changes. And make every write idempotent — key each order to NetSuite's externalId so a retry maps to the same record and a network blip never creates two sales orders.
The boundary in one paragraph
The whole design lives or dies on one boundary: NetSuite is the system of record, not a real-time API behind every page. Catalog and pricing are synced into the headless database and kept current with webhooks; orders are pushed back idempotently, through a queue; and nothing on a shopper's hot path ever waits on NetSuite. Get this boundary right and a headless NetSuite storefront is genuinely fast; get it wrong and no amount of frontend polish will save it.
The hard parts (where these integrations break)
A demo sync works on the happy path. In production, an integration is judged by how it behaves when things aren't ideal — and that's where most builds get into trouble.
Security: which door, and how you lock it
NetSuite supports several authentication schemes, and the choice sets the security posture of the whole integration:
- Token-Based Authentication (TBA) — OAuth 1.0-style per-request signing. Widely supported, but it leaves you with long-lived secrets to store and rotate.
- OAuth 2.0 Authorization Code — for apps acting on behalf of a logged-in user. The wrong tool for a server-to-server storefront.
- OAuth 2.0 Machine-to-Machine (M2M) — a client-credentials grant with no user in the loop. Your backend signs a JWT with its private key; NetSuite verifies it against an uploaded public certificate and issues a short-lived access token. It works for both RESTlets and REST Web Services.
For a headless storefront — a backend talking to NetSuite with no human present — we default to OAuth 2.0 M2M. No shared secret travels on the wire, tokens are short-lived, and the certificate can be revoked inside NetSuite without redeploying your app. (Configure it under Setup → Integration → Manage Authentication → OAuth 2.0 Client Credentials (M2M) Setup.) It is more work than pasting a token into an environment variable — and it is the difference between a leaked credential being a non-event and a breach.
Reconciliation: the safety net under the webhooks
A sync runs on webhooks — NetSuite fires an event, the headless platform updates. But webhooks are best-effort: if the receiving system is down, mid-deploy, or rate-limited when the event fires, that change is silently lost, and nothing tells you. So a real sync also needs reconciliation: a scheduled job that periodically compares the two systems and re-syncs anything that fell out of step — the order that never landed, the price that didn't update — whether or not a webhook ever arrived. Webhooks keep the data fast; reconciliation keeps it correct.
That's the half of the work the happy path hides. On top of it sit partial-failure recovery, schema and customization drift across NetSuite releases, and observability good enough to catch a broken sync before a customer does. None of this shows up in a demo; all of it shows up in month two.
That gap — between the sync that works in the demo and the one that survives Black Friday, a NetSuite upgrade, and a flood of retries from a flaky integration — is the actual job. It's the reason teams bring us in.
Anti-patterns we get called in to fix
- NetSuite as a live read API. Querying NetSuite on every page render. It will hit governance limits and feel slow even when nothing is wrong.
- No sync layer. Going "headless" without syncing NetSuite's data into the platform just moves the monolith's slowness somewhere new.
- Over-syncing. Mirroring data that changes monthly as if it changed every second.
- Ignoring concurrency until launch. Load-test against NetSuite's real limits before go-live, not after.
Which path should you build on?
The architecture above applies across the platforms you'd actually choose between — what changes is how much you build yourself. SuiteCommerce keeps it Oracle-native and fastest to launch. Shopify plus NetSuite pairs a best-in-class storefront with NetSuite as ERP and order management. Medusa plus NetSuite gives you a fully composable, code-owned stack.
We walk through that decision in depth on the NetSuite headless commerce page, and compare the platforms in the SuiteCommerce vs Shopify guide. If you are still mapping options, start with the NetSuite ecommerce guide. And if the integration layer itself is the work you need done, that's our ERP ecommerce integration service.
Medusa + NetSuite, already built: Gemstone
Everything above — the SuiteQL reads, the two-way sync, the queue that respects NetSuite's concurrency limits, idempotent order writes, OAuth 2.0 M2M, and the reconciliation work — is exactly what goes into connecting Medusa to NetSuite properly. You can build it yourself with the approach in this guide. But you don't have to.
We've spent a long time building Gemstone, our Medusa-to-NetSuite connector, and it is already running, tested, and production-ready. It handles the sync in both directions, the rate-limit and concurrency handling, and the reliability work so your team doesn't have to reinvent any of it. If you want a headless commerce storefront on Medusa with NetSuite as the system of record, we can stand up a working example in your NetSuite sandbox in 48 hours — and we build the full storefront as a service.
Want Medusa + NetSuite without building the connector?
Gemstone is our tested, production-ready Medusa-to-NetSuite connector. We can have a working example running in your NetSuite sandbox in 48 hours, then build the full storefront for you.
Talk to us about GemstoneWhat clients ask before signing

BrokenRubik
NetSuite Development Agency
Expert team specializing in NetSuite ERP, SuiteCommerce development, and enterprise integrations. Oracle NetSuite partner with 8+ years of experience delivering scalable solutions for mid-market and enterprise clients worldwide.
Related Articles
Composable Commerce: MACH Architecture & NetSuite
What composable commerce is, how MACH architecture works, and when best-of-breed beats monolithic. Plus how NetSuite fits as the ERP backbone.
NetSuite B2B Portal: SuiteCommerce vs Alternatives
Compare every NetSuite B2B portal option — SuiteCommerce, MyAccount, and third-party alternatives. Real costs, features, and which approach fits your use case.
NetSuite Ecommerce Platform: The Complete Guide
SuiteCommerce costs $2.5K-5K/mo, Shopify integration $600-2K/mo. Compare every NetSuite ecommerce option with real pricing and when each one makes sense.
BrokenRubik