Developer ToolsFree generatorPDF, Excel, Word exportNetSuiteAll NetSuite tools

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.

Generated code
Download:

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.

Was this tool useful?

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.

ArchRay builder
Design the on-prem AI stack for NetSuite: model, GPUs, RAG pipeline, guardrails
See memory, speed and cost as you swap parts, then export the diagram. Free, in your browser.
Open the builder

Keep exploring

Free tools

ERP fixes and how-tos

AI on your ERP

Integrations

Comparisons

Software