Query NetSuite Custom Lists and Custom Records with SuiteQL
Custom lists and custom record types each appear in SuiteQL as their own table named after the script ID. This generator writes the queries for list values, custom record rows, a join from transactions, and the REST calls to run them.
Fill in
Prefix each with r. so the query stays valid.
1. Custom list values
SELECT id, name, scriptid, isinactive FROM customlist_ship_method WHERE isinactive = 'F' ORDER BY name
2. Custom record rows with system columns
SELECT r.id, r.name, r.externalid, r.created, r.lastmodified, BUILTIN.DF(r.owner) AS owner, BUILTIN.DF(r.custrecord_qc_result) AS custrecord_qc_result_text, r.custrecord_qc_result, r.custrecord_qc_item FROM customrecord_qc_inspection r WHERE r.isinactive = 'F' ORDER BY r.lastmodified DESC
3. Transactions linked to the custom record
SELECT t.id, t.tranid, BUILTIN.DF(t.type) AS type, t.trandate, t.custbody_qc_inspection AS linked_record_id, BUILTIN.DF(t.custbody_qc_inspection) AS linked_record_name, BUILTIN.DF(r.custrecord_qc_result) AS custrecord_qc_result_text FROM transaction t INNER JOIN customrecord_qc_inspection r ON r.id = t.custbody_qc_inspection WHERE t.custbody_qc_inspection IS NOT NULL ORDER BY t.trandate DESC
4. Run it through REST (query/v1/suiteql)
POST https://1234567-sb1.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, name, scriptid, isinactive FROM customlist_ship_method WHERE isinactive = ?",
"params": ["F"]
}5. Read or create one custom record through the REST record API
GET https://1234567-sb1.suitetalk.api.netsuite.com/services/rest/record/v1/customrecord_qc_inspection/42
POST https://1234567-sb1.suitetalk.api.netsuite.com/services/rest/record/v1/customrecord_qc_inspection
Content-Type: application/json
{
"name": "QC-2026-0142",
"custrecord_qc_result": { "id": "1" }
}Before running, confirm in Setup > Records Catalog that the list and record tables exist with these columns in your account. The name column exists on a custom record only when Include Name Field is ticked on the record type; remove r.name otherwise. Blocks 2 and 3 are SQL; blocks 4 and 5 are HTTP requests. REST access to custom records needs the record type's permission on the integration role.
Get your custom record queries written
Send us your custom record definitions and the report you need and we will return tested SuiteQL for your account.
Optional. The tool and every export work without this. No spam.
How to use it
Enter the script ID of the custom list and the custom record type exactly as shown at Customization > Lists, Records and Fields, in lower case. Add the custom record fields you want as a comma list, each prefixed with r., and name one List/Record field to show as text through BUILTIN.DF. If a transaction body field points to the custom record, enter its custbody ID to get the join. Copy block 1, 2 or 3 into your SuiteQL tool or N/query, or use block 4 to call it from an integration. Block 5 shows the record API alternative when you need one record by ID or want to create one.
Where custom objects live in SuiteQL
A custom list such as customlist_ship_method is a table with id, name, scriptid and isinactive. A custom record type such as customrecord_qc_inspection is a table with system columns (id, externalid, created, lastmodified, owner, isinactive, and name if enabled) plus one column per custrecord field. Fields of type List/Record store the internal ID of the target, so BUILTIN.DF turns them into the display value without a join. Body fields on transactions that point to custom records appear as columns on transaction; line fields appear on transactionline. Setup > Records Catalog lists the exact columns and joins for your account, which is the authority when a column name is in doubt.
Worked example
Quality inspections are stored in customrecord_qc_inspection, the result is a List/Record field custrecord_qc_result pointing to customlist_qc_result, and sales orders link to inspections through custbody_qc_inspection. Block 2 lists inspections newest first with the result as text. Block 3 returns every transaction with a linked inspection, its number, type, date and the inspection result, which is the report quality managers usually ask for. Called through REST with limit=1000, an integration can page through results using the offset rule (offset divisible by limit) and the hasMore flag in the response.
Gotchas
Table and column names are the script IDs in lower case; mixed case copied from a UI label fails. The name column is missing when the record type does not include a name field. Multi-select fields return the selected IDs rather than one value, so filtering them needs a different approach than an equality test; check how your account exposes them in the Records Catalog before writing that filter. Inactive list values are still referenced by old records, so do not filter them out when you are decoding historical data. Finally, SuiteQL respects record-level permissions on custom records: an integration role without access to the record type returns an error or no rows.
Frequently Asked Questions
How do I query a custom list from SuiteQL?
Use the list's script ID as the table: SELECT id, name, scriptid, isinactive FROM customlist_ship_method. To show a list value on another record without joining, wrap the referencing field in BUILTIN.DF, for example BUILTIN.DF(t.custbody_ship_method). Confirm the table in Setup > Records Catalog first.
What are the SuiteQL table names for custom record types?
The custom record type's script ID in lower case, for example customrecord_qc_inspection. Its custom fields are columns named by their custrecord script IDs. Standard tables use names like transaction, transactionline, customer and item. The Records Catalog lists every available table and its joins for your account and role.
Can I access custom records through the NetSuite REST API?
Yes. The record API supports custom records at /services/rest/record/v1/customrecord_x for create, read, update and delete, and SuiteQL reads them through /services/rest/query/v1/suiteql. A RESTlet is only needed when you want custom logic, validation or a combined response in a single call.
Why does BUILTIN.DF return a number instead of a name?
BUILTIN.DF returns the display value for fields that reference another record or list. On a plain number, text or date field it returns the value itself. Check that the field is a List/Record type in its definition. Multi-select fields may need different handling, so test them separately.
If you want users to ask about inspections, approvals or any custom record in plain English and get answers from NetSuite on your own servers, we can set that up.
Related Tools
NetSuite Script ID Prefixes Reference (custbody, custcol, customrecord)
The prefix NetSuite applies to each custom object and field type, where you create it, and how to reference it in SuiteScript, SuiteQL and REST.
NetSuiteNetSuite Custom Record Design Spec Template
A design spec for a new NetSuite custom record type: purpose, fields, relationships, access and reporting, ready to approve before anyone builds it.
Developer ToolsNetSuite REST Web Services Headers and Query Parameters Reference
Every request header, response header, query parameter and base endpoint you use with SuiteTalk REST Web Services, with values, defaults and examples in one table.
NetSuiteNetSuite SuiteQL Transaction Query Generator
Pick a transaction type, date range and row limit and get ready-to-run SuiteQL for headers, lines and the REST call that executes it.
More NetSuite tools
See all- NetSuite Saved Search Formula Generator
- NetSuite CSV Import Template Guide
- NetSuite Role Permission Matrix Worksheet
- NetSuite Inventory Costing Method Reference
- NetSuite Month End Close Checklist
- NetSuite License Cost Estimator
- NetSuite User Access Request Form Template
- NetSuite SuiteScript Deployment Handover Template
- NetSuite Sandbox and Release Change Log Worksheet
Keep exploring
Free tools
- 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.
- 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 System Notes Audit Query GeneratorBuild SuiteQL over the systemnote table to audit field changes by record, user and date range, with a transaction join and a field discovery query.
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.
- How to Create a Custom Record Type in NetSuiteCreate a NetSuite custom record type: Customization > Lists, Records, & Fields > Record Types, script IDs, permissions, sublists and saved searches.
- 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.
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.