Apps Script has a 6-minute execution limit, and even within that, slow scripts are frustrating to work with. Most performance problems come down to the same few patterns — here's how to fix them.
1. Batch reads — never read cells one by one
The single biggest performance win in Sheets scripting is reading all data at once.
// ❌ Slow: 1000 API callsfunctionslowRead(){const sheet =SpreadsheetApp.getActiveSheet();for(let i =1; i <=1000; i++){const value = sheet.getRange(i,1).getValue();// API call each iterationLogger.log(value);}}// ✅ Fast: 1 API callfunctionfastRead(){const sheet =SpreadsheetApp.getActiveSheet();const values = sheet.getRange(1,1,1000,1).getValues();// Single call values.forEach(([value])=>Logger.log(value));}
2. Batch writes — never write cells one by one
// ❌ Slow: 1000 write callsfunctionslowWrite(){const sheet =SpreadsheetApp.getActiveSheet();for(let i =1; i <=1000; i++){ sheet.getRange(i,2).setValue(i *2);// API call each iteration}}// ✅ Fast: 1 write callfunctionfastWrite(){const sheet =SpreadsheetApp.getActiveSheet();const results =Array.from({length:1000},(_, i)=>[( i +1)*2]); sheet.getRange(1,2,1000,1).setValues(results);// Single call}
3. Avoid alternating reads and writes
Apps Script optimises consecutive reads and consecutive writes. Mixing them forces it to flush the buffer each time.
// ❌ Bad: alternating read/writefunctionbadPattern(){const sheet =SpreadsheetApp.getActiveSheet();for(let i =1; i <=100; i++){const val = sheet.getRange(i,1).getValue();// Read sheet.getRange(i,2).setValue(val *2);// Write}}// ✅ Good: read all, process in memory, write allfunctiongoodPattern(){const sheet =SpreadsheetApp.getActiveSheet();const values = sheet.getRange(1,1,100,1).getValues();// Read onceconst results = values.map(([val])=>[val *2]);// Process in memory sheet.getRange(1,2,100,1).setValues(results);// Write once}
4. Use getDataRange() instead of getLastRow/getLastColumn separately
// Fine, but two API callsconst lastRow = sheet.getLastRow();const lastCol = sheet.getLastColumn();const data = sheet.getRange(1,1, lastRow, lastCol).getValues();// Better: one callconst data = sheet.getDataRange().getValues();
5. Cache expensive lookups
If you're looking up the same data repeatedly, cache it in a plain JS object: