2

I already looked at posts with people getting similar issues but couldn't find a clear answer.

I try retrieve a 2D array of 264735 slots using this lines of code:

  var optionalArguments = {majorDimension: "ROWS",
                     valueRenderOption: "FORMULA",
  };
  var sourceValuesObject = Sheets.Spreadsheets.Values.get(spreadsheetId, rangeA1Notation, optionalArguments)

but here is what I get:

response code : 413. Message : response too large.

This looks weird because I can't see this kind of limitations written nowhere plus how can user grab large amount of datas if 300000 cells or less make the API bug.

I've tried splitting the requests and it works but this is getting my code more complicated and slower plus I get an empty response when trying to update back the values to my sheet's range.

Am I pointing the right direction, Is this normal, is there a workaround?

EDIT: Here is a sample spreasheet

First I've tried getting the splitted ranges using Sheets.Spreadsheets.Values.get inside a for loop and it worked.

Doing the same thing with batchGet gives me the same error so I guess I'm having too large datas inside the cells.

JSmith
  • 4,519
  • 4
  • 29
  • 45
  • Can you provide a sample spreadsheet for replicating your situation? I think that it will help users think of about your issue. – Tanaike Oct 19 '18 at 22:55
  • 2
    Are you sure it's Sheets API and not a limitation on Apps script side? Try api with another language perhaps. UrlFetch calls have a maximum response size of 50MB/call. Same limitation may apply to api call. – TheMaster Oct 19 '18 at 23:30
  • @Tanaike appart from the `rangeA1Notation`variable this is the only code that makes it bug. Plus I cannot really share my code I need to ask for permission but thanks. – JSmith Oct 20 '18 at 09:59
  • @I'-'I the code works with `spreasheetApp`but there's a lot of long formulas in the cells so I guess It's too long for Sheets API. Also I don't see how you can retrieve formulas with UrlFetch and for the other language I'm using google app script with sheet bounded to script so I guess I won't go that way, sorry. Many thanks. – JSmith Oct 20 '18 at 10:03
  • @JSmith Tanaike asked for Sample spreadsheet, NOT code. – TheMaster Oct 20 '18 at 10:06
  • @I'-'I oh yes right sorry 'bout that. – JSmith Oct 20 '18 at 12:15
  • What's the `batchGet` error? – tehhowch Oct 20 '18 at 14:03
  • @tehhowch exactly the same `response code : 413. Message : response too large. ` – JSmith Oct 20 '18 at 14:16
  • @Tanaike I've just added the spreadsheet link into the post. Thanks in advance – JSmith Oct 20 '18 at 19:15
  • Ha ha.. Really? Who wrote 140 lines of formula per cell x 1000rows x 6 Columns? That's really Lol :) – TheMaster Oct 20 '18 at 21:43
  • @I'-'I yeah I know that's big but I need to deal with it :) – JSmith Oct 20 '18 at 22:47
  • @Jsmith If I were you, I'd start by fixing that formula rather than going around it. Just renaming your "namedRanges" to something smaller and simpler like `h` for header and `s` for settings would reduce your response size by 50-75%. On a cursory view, I believe that the formula is unnecessarily long and I believe I could beat it down to a total of 10 lines or less x 6 cells, rather than 140 lines x6000 cells, if I knew what was going on ;) Even converting `RC` to `A1` notation has a 50%-70% reduction in cell references lengths. – TheMaster Oct 20 '18 at 23:05
  • @JSmith Thank you for sharing the sample spreadsheet. I posted a workaround as an answer. Could you please confirm it? If this was not what you want, I'm sorry. – Tanaike Oct 21 '18 at 01:34
  • 1
    @I'-'I I think that your first comment is correct. Actually, it was found that your comment was correct by an experiment using OP's shared spreadsheet. – Tanaike Oct 21 '18 at 01:38
  • @Tanaike Nice workaround – TheMaster Oct 21 '18 at 01:45
  • 1
    @I'-'I Thank you so much. Although the methods like the batch request can call many requests by one API call, the maximum size of UrlFetchApp becomes the bottleneck. I think that in the case like that the values with the large size are retrieved, ``UrlFetchApp.fetchAll()`` is more suitable than the batch request. – Tanaike Oct 21 '18 at 01:52

2 Answers2

5

How about this workaround?

Experiment:

This experiment uses your shared sample spreadsheet.

When the endpoint of Sheets API is directly called by UrlFetchApp, if the response size is larger than 50 MB (52,428,800 Byte), the response less than 50 MB is returned. The size of 50 MB is due to the limitation of UrlFetchApp. On the other hand, at Advanced Google Service, it cannot confirm this situation, because the error occurs when it is over the limitation. So by using UrlFetchApp, the reason of the error in your situation can be confirmed. So at first, I confirmed this using the following script.

var spreadsheetId = "#####";
var range = "'Copie de Feuille 1'!A1:JE1000";
var url = "https://sheets.googleapis.com/v4/spreadsheets/" + spreadsheetId + "/values/" + range + "?majorDimension=ROWS&valueRenderOption=FORMULA";
var res = UrlFetchApp.fetch(url, {headers: {Authorization: "Bearer " + ScriptApp.getOAuthToken()}});
Logger.log(res.getContentText().length.toString())
var values = JSON.parse(res.getContentText());

When the above script is run, 52428450 is returned. And the error of "Unterminated string literal" occurs at the last line. This means that the object is incomplete. From this result, it is found that the values cannot be retrieved by one call of values.get with the range of 'Copie de Feuille 1'!A1:JE1000. This is the same situation with I'-'I`s comment.

In your sample spreadsheet, it was found that the boundary of range that the error occurs is 'Copie de Feuille 1'!A1:JE722. When the values are tried to be retrieved from the range 'Copie de Feuille 1'!A1:JE723, the error occurs. The size of values retrieved from 'Copie de Feuille 1'!A1:JE722 is 52,390,229 bytes. This is less than 50 MB (52,428,800 Byte). The size from 'Copie de Feuille 1'!A1:JE723 is 52,428,450 bytes which is the same value from 'Copie de Feuille 1'!A1:JE1000. From this, it is found that it is over the limitation of UrlFetchApp.

Workaround:

In order to avoid this error and retrieve all values, as a workaround, I think that I would like to propose that it splits the range for retrieving values from Spreadsheet. But in your question, you want the speed. So I would like to propose the following sample script.

  1. Create requests.
  2. Fetch the created requests using UrlFetchApp.fetchAll().
    • By UrlFetchApp.fetchAll(), each request can work by the asynchronous processing.
    • At Sheets API of Advanced Google Service, this cannot be used. And also, at values.batchGet, because the all retrieved values is over the limitation, the error occurs.
    • By this, the process cost using UrlFetchApp.fetchAll() becomes lower than that of Sheets API of Advanced Google Service.

Sample script:

var ranges = ["'Copie de Feuille 1'!A1:JE500", "'Copie de Feuille 1'!A501:JE1000"]; // This was used from the shared spreadsheet. So please modify this for your environment.
var token = ScriptApp.getOAuthToken();
var requests = ranges.map(function(e) {
  return {
    method: "get",
    url: "https://sheets.googleapis.com/v4/spreadsheets/" + spreadsheetId + "/values/" + e + "?majorDimension=ROWS&valueRenderOption=FORMULA",
    headers: {Authorization: "Bearer " + token},
    muteHttpExceptions: true,
  }
});
var res = UrlFetchApp.fetchAll(requests);
var values = res.reduce(function(ar, e) {
  Array.prototype.push.apply(ar, JSON.parse(e.getContentText()).values);
  return ar;
}, []);
Logger.log(values.length) // 1000
Logger.log(values[0].length) // 265

Note:

  • If you want to use the script, please confirm that Sheets API is enabled at API console.
  • When the arrays are merged, if the limitation error of array occurs, please use each array without merging arrays.
  • When Sheets API of Advanced Google Service is used, also no error occurs at the range of 'Copie de Feuille 1'!A1:JE722 and the error occurs at 'Copie de Feuille 1'!A1:JE723. This result is the same with the result of UrlFetchApp.

References:

Tanaike
  • 181,128
  • 11
  • 97
  • 165
  • 2
    This answer has helped me very efficiently deal with a Google Apps Script http request too large (>50MB) problem. Clear answer with clean code. Cheers! For my case I had to use Array.prototype.push.apply(ar, JSON.parse(e.getContentText("UTF-8"))); (UTF-8 and without .values because I fetched from REST API instead of a spreadsheet) – Frank Jan 09 '20 at 21:28
  • @Frank Thank you for your comment. – Tanaike Jan 09 '20 at 22:16
1

Well here is what I've found by trying to compare different ways to get values:

function compare(){
  getValuesSpreasheetApp();
  getValuesUrlFetch();
  getValues();
}
//using spreadsheetApp.getFormulas()
function getValuesSpreasheetApp(){
  var t0 = new Date().getTime();
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var dataRange = ss.getActiveSheet().getDataRange();
  var formulas = dataRange.getFormulas();
  var values = dataRange.getValues();
  var t1 = new Date().getTime();
  Logger.log("spreadsheetApp: " + (t1 -t0));
}
//using UrlFetchApp.fetchAll
function getValuesUrlFetch()
{
  var t0 = new Date().getTime();
  var ranges = ["'Copie de Feuille 1'!A1:JE500", "'Copie de Feuille 1'!A501:JE1000"];
  var token = ScriptApp.getOAuthToken();
  var spreadsheetId = SpreadsheetApp.getActiveSpreadsheet().getId();
  var requests = ranges.map(function(e) {
      return {
      method: "get",
      url: "https://sheets.googleapis.com/v4/spreadsheets/" + spreadsheetId + "/values/" + e + "?majorDimension=ROWS&valueRenderOption=FORMULA",
      headers: {Authorization: "Bearer " + token},
      muteHttpExceptions: true,
    }
  });
  var res = UrlFetchApp.fetchAll(requests);
  var values = res.reduce(function(ar, e) {
    Array.prototype.push.apply(ar, JSON.parse(e.getContentText()).values);
    return ar;
  }, []);
  var t1 = new Date().getTime();
  Logger.log("UrlFetch: " + (t1 -t0));
}
//using Sheets.Spreadsheets.Values.get()
function getValues(){
  var t0 = new Date().getTime();
  var spreadsheetId = SpreadsheetApp.getActiveSpreadsheet().getId();
  var ranges = ["'Copie de Feuille 1'!A1:JE500", "'Copie de Feuille 1'!A501:JE1000"];
  var optionalArguments = {majorDimension: "ROWS",
                 valueRenderOption: "FORMULA",
 };
  res = [];
  for(var i = 0; i < ranges.length; i++)
    res.push(Sheets.Spreadsheets.Values.get(spreadsheetId, ranges[i]));
  var values = res.reduce(function(ar, e) {
    Array.prototype.push.apply(ar, e.values);
    return ar;
  }, []);
  var t1 = new Date().getTime();
  Logger.log("values.get: " + (t1 -t0));
}

and here is the result:

[18-10-23 01:35:30:034 CEST] spreadsheetApp: 13828

[18-10-23 01:35:33:614 CEST] UrlFecth: 3580

[18-10-23 01:35:38:185 CEST] values.get: 4570

Thanks to @Tanaike for giving me such a precise answer on when the error occurs and giving me a alernative solution. here is the benchmark and fetchAll is the fastest solution.

Community
  • 1
  • 1
JSmith
  • 4,519
  • 4
  • 29
  • 45
  • 1
    Thank you. It seems that ``var optionalArguments = {majorDimension: "ROWS", valueRenderOption: "FORMULA"};`` is not used at ``getValues()``. If my understanding is correct, in this case, the retrieved values are different from others. – Tanaike Oct 22 '18 at 23:28
  • oh your right I will correct that and see the difference. Thanks you very much – JSmith Oct 22 '18 at 23:34