solenex-tech/ns-analytics-datapull
Restlets scripts to pull data from existing saved searches or datasets
NetSuite Analytics Restlet API Integration
Version: 1.1.0
License: MIT (Copyright © 2026 Solenex Technology Pvt Ltd.)
Last Updated: January 2026
Overview
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
Table of Contents
- Quick Start
- Script 1: Metadata Retrieval
- Script 2: Data Retrieval
- Pagination & Custom Page Sizing
- API Integration Examples
- Error Handling
- Deployment Guide
- Performance Considerations
- Troubleshooting
Quick Start
Prerequisites
- 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)
Basic Flow
- 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
Script 1: Metadata Retrieval
File: getAnalyticsMetadata_RS.js
Purpose
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
Deployment
- 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
HTTP Endpoint
GET https://[account-id].suiteapis.com/rest/script/[script-id]/deploy/[deploy-id]
Request Parameters
| 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) |
Response Format
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"
}Response Fields
| 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) |
Error Responses
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"
}Script 2: Data Retrieval
File: getAnalyticsData_RS.js
Purpose
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)
Key Features
- 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)
Deployment
- 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
HTTP Endpoint
GET https://[account-id].suiteapis.com/rest/script/[script-id]/deploy/[deploy-id]
Request Parameters
| 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 |
Response Format
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"
}
]
}Response Fields
| 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 |
Data Object Structure
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"
}Error Responses
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"
}Pagination & Custom Page Sizing
Understanding NetSuite's Native Pagination
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)
Custom Page Grouping
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"
Choosing pagestoload Value
| 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 |
API Integration Examples
Example 1: Power Automate – Process Records
{
"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 Handling
Common Errors & Solutions
| 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 |
Logging & Debugging
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
Deployment Guide
Step 1: Create Script in NetSuite
- Navigate to Customization > Scripts > New
- Select Restlet as script type
- Copy content of
getAnalyticsMetadata_RS.jsorgetAnalyticsData_RS.js - Click Save
Step 2: Create Deployment
- 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
Step 3: Configure Permissions
-
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
Step 4: Obtain API Credentials
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=...
Performance Considerations
API Rate Limits
- NetSuite enforces 10 requests per second (REST API limit)
- Space out requests if fetching many pages
- Consider batch windows (daily overnight jobs)
Optimization Tips
-
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
Payload Size Estimates
| 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 |
Troubleshooting
Issue: "No data returned"
Check:
- Verify Saved Search/Dataset contains records
- Confirm filters are not too restrictive
- Check user permissions on the report
- Review script execution logs
Issue: "Timeout on page fetch"
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
Issue: "Memory exceeded"
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
Issue: "Column key mismatch in parsing"
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.
Support & Contribution
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
License
MIT License – See LICENSE file for details.
© 2026 Solenex Technology Pvt Ltd. All Rights Reserved.
Quick Reference
Metadata Endpoint
GET /rest/script/[ID]/deploy/[ID]?analyticsreportid=XXX&pagestoload=N
Data Endpoint
GET /rest/script/[ID]/deploy/[ID]?analyticsreportid=XXX&pageindex=N&pagestoload=M
Response Format
{
"analyticsname": "Report Name",
"analyticstype": "Type",
"totalrows": 1000,
"totalpages": 1,
"pageindexrange": "0-1",
"currentpageindex": 0,
"data": [
{ "columnkey1": "value1", "columnkey2": "value2" },
{ "columnkey1": "value3", "columnkey2": "value4" }
]
}Parameter Quick Reference
analyticsreportid(required): Saved Search or Dataset IDpageindex(required for data): Page number (0-indexed)pagestoload(required): Pages per response (1–20 recommended)