NetSuite REST API SuiteQL Request Generator
This generator wraps a SuiteQL statement in a working POST to the NetSuite REST query/v1/suiteql endpoint and gives you the same call as curl and as a Python loop that collects every page. It is for developers testing queries in Postman or writing an extract job against NetSuite.
Fill in
Lowercase, with a hyphen for sandboxes: 1234567-sb1 (not 1234567_SB1).
OAuth 2.0: Bearer plus the access token. For TBA paste a full OAuth 1.0a header, but note it must be re-signed for every page.
Keep it on one line and use single quotes for literals. Escape any double quote as \" in the JSON body.
1 to 1000. 1000 is the default and the maximum page size.
Must be divisible by the limit, for example 0, 1000, 2000.
1. Raw HTTP request (Postman, Insomnia, REST client)
POST https://1234567.suitetalk.api.netsuite.com/services/rest/query/v1/suiteql?limit=1000&offset=0
Authorization: Bearer <access_token>
Content-Type: application/json
Prefer: transient
{
"q": "SELECT id, tranid, trandate, BUILTIN.DF(status) AS status FROM transaction WHERE type = 'SalesOrd' AND trandate >= ADD_MONTHS(SYSDATE, -1) ORDER BY id"
}2. curl
curl -X POST "https://1234567.suitetalk.api.netsuite.com/services/rest/query/v1/suiteql?limit=1000&offset=0" \
-H "Authorization: Bearer <access_token>" \
-H "Content-Type: application/json" \
-H "Prefer: transient" \
--data '{"q": "SELECT id, tranid, trandate, BUILTIN.DF(status) AS status FROM transaction WHERE type = 'SalesOrd' AND trandate >= ADD_MONTHS(SYSDATE, -1) ORDER BY id"}'3. Python: page through every result
import requests
BASE_URL = "https://1234567.suitetalk.api.netsuite.com/services/rest/query/v1/suiteql"
HEADERS = {
"Authorization": "Bearer <access_token>",
"Content-Type": "application/json",
"Prefer": "transient",
}
QUERY = """SELECT id, tranid, trandate, BUILTIN.DF(status) AS status FROM transaction WHERE type = 'SalesOrd' AND trandate >= ADD_MONTHS(SYSDATE, -1) ORDER BY id"""
def run_suiteql(query, limit=1000, offset=0):
rows = []
url = BASE_URL + "?limit=" + str(limit) + "&offset=" + str(offset)
while url:
resp = requests.post(url, headers=HEADERS, json={"q": query}, timeout=180)
if resp.status_code != 200:
raise RuntimeError(str(resp.status_code) + " " + resp.text)
data = resp.json()
for item in data.get("items", []):
item.pop("links", None)
rows.append(item)
print("offset", data.get("offset"), "count", data.get("count"),
"of", data.get("totalResults"))
url = None
if data.get("hasMore"):
for link in data.get("links", []):
if link.get("rel") == "next":
url = link.get("href")
return rows
if __name__ == "__main__":
result = run_suiteql(QUERY)
print(len(result), "rows")
if result:
print(result[0])Verify for your account: Prefer: transient is required on the suiteql resource; limit is at most 1000 per page and offset must be a multiple of limit; SuiteQL over REST returns at most 100,000 rows in total, beyond that use SuiteAnalytics Connect. The role behind the token needs the REST Web Services permission plus view access to the records queried. With TBA, limit and offset are query parameters, so each page needs a freshly signed header.
Get your SuiteQL extract reviewed
Send us the query and volumes and we will check paging, indexes and limits before you schedule it.
Optional. The tool and every export work without this. No spam.
How to use it
Enter your account ID in hostname form, paste the Authorization value, and type or paste a SuiteQL statement. Leave limit at 1000 unless you are testing, and leave offset at 0 to start from the first row. Copy block 1 into Postman (set the body to raw JSON), run block 2 from a terminal, or save block 3 as a script. The Python version reads hasMore and the link with rel next from each response, so it stops on its own after the last page. Each item in the response carries an empty links array, which the script strips before storing the row.
- Hostname form: 1234567 for production, 1234567-sb1 for sandbox 1.
- Use single quotes for string literals so the JSON body stays valid.
- Add ORDER BY on a unique column (id) so pages do not overlap.
What the endpoint expects and returns
The request is POST /services/rest/query/v1/suiteql with the statement in a JSON body under the key q, and the header Prefer: transient, which Oracle documents as required for this resource. You can also send bind values in a params array next to q, matched to ? placeholders. The response is a collection: items holds the rows, count is the number of rows on this page, offset is the starting index, totalResults is the total across all pages, hasMore says whether another page exists, and links lists self, next, prev, first and last URLs. Column names in items follow your aliases, and in practice come back in lowercase, so alias every expression you need to read by name.
Limits and gotchas
Page size is capped at 1000, and Oracle states that offset must be divisible by limit, so limit=500&offset=1000 works while limit=300&offset=1000 is rejected. A SuiteQL query over REST can reach at most 100,000 results through paging; for larger extracts Oracle points to SuiteAnalytics Connect with the NetSuite2.com data source. Concurrency counts against the account's integration concurrency limit, so run pages one after another rather than firing them in parallel from one integration. With token-based authentication the signature covers the query string, so a header signed for offset 0 fails for offset 1000. OAuth 2.0 bearer tokens do not have that problem and last 60 minutes.
- 400 with a parse error: usually a double quote inside q that was not escaped.
- Empty items but totalResults above zero: offset past the last page.
- Missing columns: the role cannot see that field or record.
Checking the result in NetSuite
Before blaming the API, run the same statement through query.runSuiteQL in the SuiteScript debugger or a small Suitelet so you know the SQL itself is right. Compare totalResults from the first page with a SELECT COUNT(*) version of the query. If a field is missing, open the Records Catalog (Setup > Records Catalog) and check the field ID and whether it is available in SuiteQL for the record. For performance, filter on indexed fields such as id, type, trandate and lastmodifieddate, and pull only the columns you need, since wide BUILTIN.DF lookups slow every page.
Frequently Asked Questions
How do I run a SuiteQL query through the NetSuite REST API?
Send POST https://<account>.suitetalk.api.netsuite.com/services/rest/query/v1/suiteql with header Prefer: transient, Content-Type: application/json and an Authorization header, and put the statement in the body as {"q": "SELECT ..."}. Add ?limit= and &offset= to control paging. The role needs REST Web Services permission and access to the queried records.
What is the maximum number of rows SuiteQL returns over REST?
Each page returns up to 1000 rows, and you page with offset, which must be a multiple of the limit. Oracle documents a ceiling of 100,000 results for SuiteQL queries this way. For anything larger, split the query by date or id range, or use SuiteAnalytics Connect with the NetSuite2.com data source.
How do I call SuiteQL from Postman?
Create a POST request to the suiteql URL, set Authorization to OAuth 2.0 bearer or OAuth 1.0 with HMAC-SHA256 and your realm, add Prefer: transient, and set the body to raw JSON with the q key. Postman signs OAuth 1.0 per request, including limit and offset, which is why TBA works there even when a pasted static header fails.
Why does my SuiteQL REST call return a 400 error?
The common causes are an unescaped double quote or line break inside the q string, a missing Prefer: transient header, an offset not divisible by limit, or a field the role cannot see. Read the o:errorDetails array in the response body: it usually names the column or position in the statement that failed.
If you want your team to ask NetSuite questions in plain English with the SuiteQL shown and checked, we can run that assistant on your own infrastructure.
Related Tools
NetSuite OAuth 2.0 Client Credentials (M2M) JWT Code Generator
Generate the openssl commands, the signed JWT client assertion and the token request for NetSuite machine-to-machine OAuth 2.0, in Python and Node.js.
Developer ToolsNetSuite Token-Based Auth OAuth 1.0 Signature Code Generator
Get dependency-free Python and Node.js code that builds a valid NetSuite TBA Authorization header for a RESTlet or REST web services call, then sends it.
NetSuiteNetSuite SuiteQL Pagination Time Calculator
Calculate pages, round trips and total extraction time for a large SuiteQL result set, and see how page size and parallel threads change it.
NetSuiteSuiteQL Query Generator
Fill in a table, columns, filter and sort and get a SuiteQL statement ready for the N/query module, the REST SuiteQL endpoint or the SuiteAnalytics Workbook.
NetSuiteNetSuite SuiteAnalytics Connect ODBC and JDBC Connection String Generator
Enter your service host, account ID and role ID to get a working JDBC URL, Windows DSN settings, ODBC connection strings, a Linux odbc64.ini stanza and test queries.
More Developer Tools tools
See all- NetSuite SuiteScript 2.1 RESTlet Skeleton Generator
- NetSuite SuiteScript Governance Units by Script Type
- NetSuite API Retry Backoff Wait Time Calculator
- NetSuite OAuth 2.0 Access Token Refresh Calculator
- NetSuite REST Web Services Headers and Query Parameters Reference
- SuiteScript 1.0 to 2.1 API Mapping Reference for NetSuite
- NetSuite REST API Sales Order and Journal Entry Request Generator
- SuiteScript 2.1 Scheduled Script Generator for NetSuite
- SuiteScript 2.1 Client Script Generator for NetSuite
Keep exploring
Free tools
- SuiteQL Query GeneratorFill in a table, columns, filter and sort and get a SuiteQL statement ready for the N/query module, the REST SuiteQL endpoint or the SuiteAnalytics Workbook.
- NetSuite SuiteQL Custom List and Custom Record Query GeneratorEnter a custom list or custom record script ID and its fields to get SuiteQL for its values, its records, a join from transactions, and a ready REST call.
- NetSuite SuiteQL Transaction Query GeneratorPick a transaction type, date range and row limit and get ready-to-run SuiteQL for headers, lines and the REST call that executes it.
ERP fixes and how-tos
- How to write and run SuiteQL queries in NetSuiteRun SuiteQL from Analytics > SuiteQL Query Tool or via N/query in SuiteScript. Syntax basics, joins, pagination and real examples for NetSuite reporting.
- Working with NetSuite's SuiteTalk REST Web Services APICall NetSuite's SuiteTalk REST record and SuiteQL endpoints correctly: base URLs, pagination with limit/offset/hasMore, expandSubResources, the Prefer: transient header, and common 404 and permission errors.
- Exporting large volumes of data out of NetSuite reliablyDesign a reliable large-scale NetSuite export: compare SuiteAnalytics Connect, SuiteQL via SuiteTalk REST, saved search CSV export and RESTlet/Map-Reduce pulls, and use lastmodifieddate for incremental sync instead of repeated full loads.
AI on your ERP
- AI agents for NetSuite manufacturing operationsAI agents for NetSuite manufacturing: WIP tracking, routing exceptions, and work order status grounded in SuiteQL, with human approval on anything that writes back.
- AI for NetSuite, Beyond the Built-In Text ToolsNetSuite's built-in AI covers text generation, not grounded answers on your own data. See how a private LLM over SuiteQL adds real Q&A and controls.
Integrations
- How to add AI agents to Oracle NetSuiteHow to add AI agents to Oracle NetSuite: SuiteQL and REST tool layer, RESTlet writes, role-scoped tokens, human approval, and where a private model fits.
- How to use Claude with Oracle NetSuiteUse Claude with Oracle NetSuite through MCP connectors, the Anthropic API, AWS Bedrock or Vertex, plus the on-prem alternative for controlled data.