NetSuite SuiteQL for WooCommerce Integrations — Replacing Saved Searches at Scale
- SuiteQL is SQL-like query language for NetSuite data — faster, more flexible, and more maintainable than Saved Search for integration use cases.
- The most important SuiteQL capability for integrators: lastModifiedDate filtering for incremental sync — fetch only records changed since the last sync run.
- SuiteQL queries are called via the REST API endpoint /services/rest/query/v1/suiteql — no SuiteScript required for read operations.
- Saved Searches should be kept for human-facing reporting inside NetSuite; SuiteQL is the correct interface for machine-to-machine data extraction.
Most NetSuite integrations start with Saved Searches for data extraction. A Saved Search works in the UI and can be called via SuiteScript — but it is the wrong tool for a production integration at volume. Saved Searches are fragile (a NetSuite admin can accidentally modify one), they have limited programmatic filtering, and they cannot be version-controlled. SuiteQL is the correct approach: SQL-like, deterministic, called via REST, and entirely controlled by your integration code.
The Incremental Sync Pattern in SuiteQL
POST /services/rest/query/v1/suiteql
Content-Type: application/json
Prefer: transient
{
"q": "SELECT i.internalId, i.itemId, i.quantityAvailable,
i.quantityOnHand, i.lastModifiedDate
FROM inventoryItem AS i
WHERE i.lastModifiedDate > TO_DATE(
'2026-06-09 14:30:00', 'YYYY-MM-DD HH24:MI:SS'
)
AND i.isInactive = 'F'
ORDER BY i.lastModifiedDate ASC
LIMIT 1000 OFFSET {offset}"
}
// Run repeatedly, incrementing OFFSET by 1000, until
// result count < 1000 (end of results).
Key SuiteQL Patterns for WooCommerce Integrations
| Use case | SuiteQL example (abbreviated) |
|---|---|
| Changed orders since last sync | SELECT id, tranId, status FROM salesOrder WHERE lastModifiedDate > TO_DATE('{ts}', ...) AND status NOT IN ('salesOrder:C') |
| Inventory by location | SELECT item, location, quantityAvailable FROM inventoryBalance WHERE item IN ({sku_list}) |
| Customer by email | SELECT internalId FROM customer WHERE email = '{email}' AND isInactive = 'F' |
| Open invoices for a customer | SELECT tranId, amountRemaining, dueDate FROM invoice WHERE entity = {customer_id} AND amountRemaining > 0 |
Pagination: Always Use LIMIT and OFFSET
SuiteQL results are capped at 1,000 records per request. For any query that could return more than 1,000 records (full catalog sync, historical order pull), implement pagination using LIMIT 1000 OFFSET {n}. Increment the offset by 1,000 per request until the result count is less than 1,000, indicating you have reached the end. Never assume a single SuiteQL call returns all results.
Unlike Saved Searches (which live in the NetSuite UI and can be modified by any admin), SuiteQL queries live in your integration's source code. This means they are version-controlled, testable, and cannot be accidentally broken by a NetSuite admin editing a saved search. This is one of the key operational advantages of SuiteQL over Saved Search for integration data extraction.
References
- SuiteQL Overview — Oracle NetSuiteOracle NetSuite Help — SuiteQL syntax, supported joins, and the REST API endpoint for queries.
- NetSuite REST API Query EndpointOracle NetSuite Help — /services/rest/query/v1/suiteql endpoint reference and authentication requirements.
- NetSuite Record Schema ReferenceOracle NetSuite Help — table and field names available in SuiteQL queries.
Leave a Reply