Version: 1.1.0
License: MIT (Copyright © 2026 Solenex Technology Pvt Ltd.)
Last Updated: January 2026
This repository contains two production-ready NetSuite Restlet scripts for retrieving metadata and analytical data from NetSuite Saved Searches and SuiteAnalytics Datasets. These scripts enable seamless integration with external systems such as Tableau, Power BI, or custom applications providing dynamic, paginated access to NetSuite analytics without manual export/import workflows.
Key Features:
- Support for both Saved Searches and SuiteAnalytics Datasets
- Efficient paginated data retrieval (1,000 records per native page)
- Custom pagination to combine multiple internal pages into single API responses
- JSON object responses with sanitized column keys (lowercase, spaces removed)
- Smart value extraction using
getText()for display values andgetValue()for raw values - JSON API responses compatible with Google Sheets, Zapier, Power Automate
- Comprehensive error handling and logging
- Production-grade code with MIT license
- Quick Start
- Script 1: Metadata Retrieval
- Script 2: Data Retrieval
- Pagination & Custom Page Sizing
- API Integration Examples
- Error Handling
- Deployment Guide
- Performance Considerations
- Troubleshooting
- NetSuite account with SuiteScript 2.1 support
- Admin access to create Restlet scripts
- Knowledge of your Saved Search or Dataset IDs (format:
customsearch_XXXXXorcustdataset_XXXXX)
- Get metadata → Call
getAnalyticsMetadata_RS.jsto learn total rows & pages - Fetch data → Call
getAnalyticsData_RS.jsfor paginated results - Integrate → Parse JSON response and process records by object key
File: getAnalyticsMetadata_RS.js
Retrieves summary metadata about a Saved Search or Dataset without pulling actual data. Use this to:
- Determine total row count
- Calculate pagination pages
- Validate report ID before bulk data fetches
- Display data availability status to end users
- In NetSuite, go to Customization > Scripts > New
- Select Restlet as script type
- Paste the content of
getAnalyticsMetadata_RS.js - Set script name:
Analytics Metadata Retriever - Deploy and note the Script ID and Deployment ID
GET https://[account-id].suiteapis.com/rest/script/[script-id]/deploy/[deploy-id]
| Parameter | Type | Required | Description | Example |
|---|---|---|---|---|
analyticsreportid |
String | ✓ | Saved Search or Dataset ID | customsearch_ar_aging or custdataset_123456 |
pagestoload |
Integer | ✓ | Number of 1,000-record pages to group into one output page | 5 (groups 5×1000 = 5,000 records/response) |
For Saved Search:
{
"analyticsname": "AR Aging Report",
"analyticstype": "SALES_ORDER_TYPE",
"totalrows": 3250,
"totalpages": 4,
"pageindexrange": "0-3"
}For Dataset:
{
"analyticsname": "Inventory Summary",
"analyticstype": "WORKBOOK",
"totalrows": 15000,
"totalpages": 15,
"pageindexrange": "0-14"
}| Field | Description |
|---|---|
analyticsname |
Name of the Saved Search or Dataset |
analyticstype |
Search type or Dataset type |
totalrows |
Total number of data rows |
totalpages |
Number of custom-paginated pages (based on pagestoload) |
pageindexrange |
Valid page indices to request (e.g., 0-2 means pages 0, 1, 2) |
Missing Parameter:
{
"error": "MISSING_PAGESTOLOAD",
"message": "The pagestoload parameter is required"
}Invalid Report ID:
{
"error": "INVALID_REPORTID",
"message": "The dataset/saved search id is invalid"
}File: getAnalyticsData_RS.js
Fetches actual data rows from a Saved Search or Dataset as JSON objects with sanitized column keys. Designed for direct integration with:
- Power Automate / Zapier
- Custom web applications
- BI tools (Tableau, Power BI via custom API connectors)
- Column key sanitization: All column names are lowercase with spaces removed (e.g.,
"Order ID"→orderid) - Smart value extraction: Uses
getText()for display values on Saved Searches,getValue()for raw values - Null handling: Null/undefined values converted to empty strings
- Object format: Data returned as array of objects (easy to process and iterate)
- In NetSuite, go to Customization > Scripts > New
- Select Restlet as script type
- Paste the content of
getAnalyticsData_RS.js - Set script name:
Analytics Data Retriever - Deploy and note the Script ID and Deployment ID
GET https://[account-id].suiteapis.com/rest/script/[script-id]/deploy/[deploy-id]
| Parameter | Type | Required | Description | Example |
|---|---|---|---|---|
analyticsreportid |
String | ✓ | Saved Search or Dataset ID | customsearch_ar_aging or custdataset_123456 |
pageindex |
Integer | ✓ | Page number to retrieve (0-indexed, based on pagestoload grouping) |
0 (first page) |
pagestoload |
Integer | ✓ | Number of 1,000-record pages to combine into one output page | 5 |
For Saved Search:
{
"analyticsname": "AR Aging Report",
"analyticstype": "SALES_ORDER_TYPE",
"totalrows": 3250,
"totalpages": 1,
"pageindexrange": "0-0",
"currentpageindex": 0,
"data": [
{
"orderid": "12345",
"customer": "ACME Corp",
"amount": "5000.00",
"daysoverdue": "45"
},
{
"orderid": "12346",
"customer": "Tech Solutions",
"amount": "3200.50",
"daysoverdue": "30"
}
]
}For Dataset:
{
"analyticsname": "Inventory Summary",
"analyticstype": "WORKBOOK",
"totalrows": 15000,
"totalpages": 3,
"pageindexrange": "0-2",
"currentpageindex": 0,
"data": [
{
"item": "SKU-001",
"location": "Warehouse A",
"quantity": "450",
"reorderlevel": "100"
},
{
"item": "SKU-002",
"location": "Warehouse B",
"quantity": "230",
"reorderlevel": "50"
}
]
}| Field | Description |
|---|---|
analyticsname |
Name of the report |
analyticstype |
Search/Dataset type |
totalrows |
Total data rows in report |
totalpages |
Total custom-paginated pages available |
pageindexrange |
Valid page indices (e.g., 0-2 means pages 0, 1, 2 exist) |
currentpageindex |
Page number of this response |
data |
Array of objects; keys are sanitized column names |
Column Key Sanitization Rules:
- Convert to lowercase:
"Order ID"→"order id" - Remove spaces:
"order id"→"orderid" - Resulting key is alphanumeric and safe for property access
Example object from response:
{
orderid: "12345", // Original: "Order ID"
customer: "acmecorp", // Original: "Customer"
amount: "5000.00", // Original: "Amount"
daysoverdue: "45" // Original: "Days Overdue"
}Missing Parameter:
{
"error": "MISSING_PAGEINDEX",
"message": "The pageindex parameter is required"
}{
"error": "MISSING_PAGESTOLOAD",
"message": "The pagestoload parameter is required"
}Invalid Report ID:
{
"error": "INVALID_REPORTID",
"message": "The dataset/saved search id is invalid"
}NetSuite's runPaged() API returns 1,000 records per page (hard limit). For a Saved Search with 5,000 rows:
Native Pages: 5 pages (pages 0–4, each with ≤1,000 records)
The pagestoload parameter lets you combine multiple native pages into a single API response. This reduces external API calls and network round-trips.
Example: Saved Search: 5,000 rows pagestoload: 5 (group 5 native pages = 5,000 rows into 1 API response)
Result: Custom Pages: 1 page (page 0, containing 5,000 rows) pageindexrange: "0-0"
Example 2: Dataset: 18,500 rows Native pages: 19 (pages 0–18) pagestoload: 5
Calculation: computedPages = ceil(19 / 5) = 4 Result: Custom Pages: 4 pages Page 0: native pages 0–4 (5,000 rows) Page 1: native pages 5–9 (5,000 rows) Page 2: native pages 10–14 (5,000 rows) Page 3: native pages 15–18 (3,500 rows)
pageindexrange: "0-3"
| Use Case | Recommended Value | Rationale |
|---|---|---|
| Small reports (<5K rows) | 1 or 2 |
Minimal latency, simple handling |
| Medium reports (5K–50K) | 5–10 |
Balance between payload size and API calls |
| Large reports (>50K) | 10–20 |
Minimize network overhead, reduce API calls |
| Google Sheets real-time sync | 1–3 |
Avoid timeout (Sheets timeout = ~30 sec) |
| Batch/overnight jobs | 20+ |
Maximize throughput, ignore latency |
{
"type": "object",
"properties": {
"analyticsreportid": {
"type": "string",
"description": "NetSuite Dataset ID (e.g., custdataset_123456)"
},
"pageindex": {
"type": "integer",
"description": "Page to fetch (0-indexed)"
},
"pagestoload": {
"type": "integer",
"description": "Pages per request"
}
}
}Flow:
- HTTP Request → GET to Restlet with params
- Parse JSON → Extract
dataarray (objects) - Apply to each → Loop through records
- Create Row → Insert into SharePoint/SQL using object properties
| Error | Cause | Solution |
|---|---|---|
INVALID_REPORTID |
Wrong dataset/search ID | Verify ID in NetSuite (format: customsearch_* or custdataset_*) |
MISSING_PAGESTOLOAD |
Parameter not provided | Add pagestoload parameter (e.g., &pagestoload=5) |
MISSING_PAGEINDEX |
Parameter not provided (data script only) | Add pageindex parameter (e.g., &pageindex=0) |
| 401 Unauthorized | Bad authentication | Check OAuth token expiry or auth header format |
| 403 Forbidden | User lacks permissions | Ensure user has permissions to view the Saved Search/Dataset |
| 500 Internal Error | Script runtime error | Check NetSuite script logs for detailed error |
Both scripts use log.debug() and log.error() for logging. View logs in:
- NetSuite UI: Customization > Scripts > [Script Name] > Execution Log
- SuiteScript Debugger: Customization > Scripts > [Script Name] > Debug
- Navigate to Customization > Scripts > New
- Select Restlet as script type
- Copy content of
getAnalyticsMetadata_RS.jsorgetAnalyticsData_RS.js - Click Save
- Click Deployments tab
- Click New
- Set Status to Testing (or Released for production)
- Click Save
- Note the Script ID and Deployment ID from the deployment record
-
Ensure user/role running the script has:
- Saved Searches: Read access to the specific searches
- Datasets: Read access to the specific datasets
- SuiteScript: Permission to execute Restlets
-
Grant via Setup > Users/Roles > [Role] > Permissions
For OAuth 2.0 (recommended):
- Create Integration Record (Setup > Integration Management > New)
- Generate OAuth tokens
- Use tokens in
Authorization: Bearer [access_token]header
For NLAuth (legacy):
- Enable Token-based Authentication on user record
- Create Access Token (Setup > Users > [User] > Access Tokens)
- Use in header:
Authorization: NLAuth nlauth_account=..., nlauth_email=..., nlauth_signature=..., nlauth_timestamp=...
- NetSuite enforces 10 requests per second (REST API limit)
- Space out requests if fetching many pages
- Consider batch windows (daily overnight jobs)
-
Reduce
pagestoloadfor real-time requests pagestoload: 1–2 (faster response, smaller payload) -
Increase
pagestoloadfor batch jobs pagestoload: 20+ (fewer API calls, larger payload) -
Cache metadata responses
- Call metadata endpoint once, reuse page count
- Avoid recalculating total pages
-
Use parallel requests (if integrating with external services)
- Fetch pages 0, 1, 2 simultaneously
- Combine results after all succeed
-
Filter in NetSuite, not externally
- If you only need recent records, add date filters to Saved Search
- Reduces payload size before API transfer
| Scenario | Rows | Columns | Approx JSON Size | Estimate |
|---|---|---|---|---|
| AR Aging (typical) | 3,000 | 8 | ~600 KB | 0.8 sec |
| Inventory (large) | 15,000 | 12 | ~3.5 MB | 2.5 sec |
| Sales Orders (huge) | 50,000 | 20 | ~14 MB | 10 sec |
Check:
- Verify Saved Search/Dataset contains records
- Confirm filters are not too restrictive
- Check user permissions on the report
- Review script execution logs
Solutions:
- Reduce
pagestoload(e.g., from 10 to 5) - Reduce column count in Saved Search (remove unnecessary columns)
- Request during off-peak hours
- Check NetSuite system performance
Causes:
pagestoloadtoo high for large datasets- Complex Saved Search with many joined records
Solutions:
- Lower
pagestoloadto 5–10 - Optimize Saved Search (remove joins, simplify filters)
- Request during maintenance window
Note: Column keys are sanitized to lowercase with spaces removed. If mapping data:
// Column "Order ID" becomes "orderid" record.orderid // Not record['Order ID']
Verify your consuming application matches sanitized keys.
Issues or questions? Contact Solenex Technology or open an issue in the repository.
Contributing: Pull requests welcome. Please follow:
- SuiteScript 2.1 conventions
- Consistent error response format
- Add test cases for new features
MIT License – See LICENSE file for details.
© 2026 Solenex Technology Pvt Ltd. All Rights Reserved.
GET /rest/script/[ID]/deploy/[ID]?analyticsreportid=XXX&pagestoload=N
GET /rest/script/[ID]/deploy/[ID]?analyticsreportid=XXX&pageindex=N&pagestoload=M
{
"analyticsname": "Report Name",
"analyticstype": "Type",
"totalrows": 1000,
"totalpages": 1,
"pageindexrange": "0-1",
"currentpageindex": 0,
"data": [
{ "columnkey1": "value1", "columnkey2": "value2" },
{ "columnkey1": "value3", "columnkey2": "value4" }
]
}analyticsreportid(required): Saved Search or Dataset IDpageindex(required for data): Page number (0-indexed)pagestoload(required): Pages per response (1–20 recommended)