Book a Free Consultation
All resources
SuiteScriptPerformanceSaved Search

Why You Should Never record.load() Inside a Loop in SuiteScript

July 9, 2026 · Updated August 15, 2026 · 6 min read· Part of the 100 NetSuite Tips series

Need help applying this in your account?

Quick answer

Loading records inside a loop in SuiteScript is one of the most common causes of governance limit errors and slow script execution. Each record.load() call consumes governance units and makes a synchronous database request. A loop that loads 200 records makes 200 separate database calls. The fix is to design the saved search to return the field values you need as columns directly on the search results, so the loop iterates over result rows rather than loading each record individually. For example, if you need subsidiary, currency, and status from a set of sales orders, add those as columns and read them with result.getValue() per row. The only time record.load() inside a loop is appropriate is when you need to access sublist data that cannot be returned as search result columns, such as line-level item quantities or rates on a transaction record.

Using Google AI Search? Add SuitePacific as a Preferred Source so we show up in your AI answers.
Add as Preferred Source

Why Is record.load() in a Loop a Problem?

A common SuiteScript pattern runs a saved search, loops through the results, and loads each record to read additional data:

results.each(function(result) {
    var so = record.load({
        type: record.Type.SALES_ORDER,
        id: result.id
    });

    var customer = so.getValue('entity');
    // Do something with customer
    return true;
});

If the search returns 1,000 results, this performs 1,000 full record loads, just to read a single field value from each one.

Each record.load() retrieves the complete record: every body field, every sublist, every line item. For a sales order with 40 line items, you are loading all 40 lines 1,000 times, when you only needed the customer name.

This increases governance unit usage, slows execution, and is one of the most common causes of scripts approaching or hitting governance limits.

Loop with record.load(): 1,000 loads Saved search returns 1,000 IDs record.load(1) record.load(2) record.load(3) … Each loads: all body fields + all line items + sublists + subrecords 1,000 × full record cost = governance risk Search columns: 0 extra loads Search returns 1,000 rows with columns result[0] result[1] result[2] … result.getValue('entity'): already there No additional operation needed 0 extra record loads · governance intact
Include the fields you need as search columns. The data is already in the result: no loop record load required.

The fix: design your search to return what you need

The correct approach is to include the required fields as columns in the saved search, so the data is already available in the result:

results.each(function(result) {
    var customer = result.getValue('entity');
    // entity was already returned by the search
    return true;
});

This works because the entity field is included as a column in the saved search. No additional record operation needed. The search already has the data.

The savings scale directly with the number of results. 1,000 results becomes zero additional record operations instead of 1,000.

How to add columns to a saved search programmatically

If you are building the search in code rather than using a saved search ID, add the fields as columns when creating the search:

var mySearch = search.create({
    type: search.Type.SALES_ORDER,
    filters: [
        ['status', search.Operator.ANYOF, 'SalesOrd:B']
    ],
    columns: [
        search.createColumn({ name: 'entity' }),
        search.createColumn({ name: 'amount' }),
        search.createColumn({ name: 'trandate' })
    ]
});

Then read the values directly from each result in the loop, no record load needed.

When Is record.load() in a Loop Actually Necessary?

There are cases where loading inside a loop is unavoidable. The key question is: what are you doing with the record?

Loading is necessary when you need to:

  • Update the record: record.load() is required if you need to call setValue() and save(). (Alternatively, use record.submitFields() for body-field-only updates, which avoids the full load.)
  • Read or modify sublists: Sublist data (line items, address sublists) is not available as search columns. If you need line-level data, you need record.load().
  • Work with subrecords: Subrecords require the full record context.

If you genuinely need one of the above, load only what you must, and consider whether the script design can be restructured to reduce the number of loads. For example, if you are updating one field on many records, record.submitFields() avoids a full load entirely.

What Should You Ask Before Writing a Loop in SuiteScript?

Before writing any loop that involves records, ask: does this field value need to come from a record load, or can I include it in the search?

In the majority of cases, the answer is: include it in the search. Build the search first to return the data the loop needs, then write the loop second.

The fastest SuiteScript loop is one where the search has already done the work.

Need help applying this in your account?

We work with post-go-live NetSuite accounts every day. Tell us what you're working on.