Home > Mobile >  Quota exceeded Sheets API write requests per minute. Node.js
Quota exceeded Sheets API write requests per minute. Node.js

Time:05-03

I'm having quota issues with the Sheets API provided by Google (using node.js). I am already using batch requests and have had my write quota increased to 900 per minute via a written request, but I still this error (question at the bottom after explanation):

enter image description here

What I am trying to do is generate a sports league schedule for a chosen amount of weeks (spreadsheets). Each week has 3 separate games (sheets). The user would first just choose how many weeks:

enter image description here

When the user clicks "Yes" for confirming the schedule, spreadsheets are created for whatever the chosen amount is:

enter image description here

Each spreadsheet gets 3 sheets created for the 3 different time slots and the team roster data is copied into the sheets:

enter image description here

Edit 3: I'm using the following code to try and generate these spreadsheets:

// Create Result Sheets
exports.createGoogleResultsSheets = async (req,res) => {
  console.log("createGoogleResultsSheets");
  const season = req.body.teamData[0].stats[0].season;
  // console.log(season);
  const resultSeasonFolders = await getChildFiles(resultParentFolderID);
  // console.log(resultSeasonFolders);
  const exists = propertyExists(resultSeasonFolders,'name',season);
  // Create season folder if it does not exist yet and get its id
  let seasonResultsFolderId = null;
  if (exists) {
    // console.log("Season Exists");
    const result = resultSeasonFolders.filter(folder=>folder.name == season).map(folder=>folder.id);
    seasonResultsFolderId = result[0];
  } else {
    // console.log("Season does not exist");
    // Create Season Folder
    let fileMetadata = {
      'name': season,
      'mimeType': 'application/vnd.google-apps.folder',
      'parents' : [resultParentFolderID]
    };
    const result = await drive.files.create({auth: jwtClient, resource: fileMetadata});
    // console.log(result);
    seasonResultsFolderId = result.data.id;
  }
  // console.log("Folder ID:");
  // console.log(seasonResultsFolderId);
  var i = 0;
  for (const week of req.body.schedule) {
    //console.log(util.inspect(week, false, null, true));
    i  ;
    // create sheet for each week
    let fileMetadata = {
      'name': "Week-" i,
      'mimeType': 'application/vnd.google-apps.spreadsheet',
      'parents' : [seasonResultsFolderId]
    };
    let result = await drive.files.create({auth: jwtClient, resource: fileMetadata});
    let spreadsheetId = result.data.id;
    for (const game of week) {
      game.teamA.data.forEach(player => {
        player.push(game.teamA.name);
      });
      game.teamB.data.forEach(player => {
        player.push(game.teamB.name);
      });
      // Can't have sheet names containing : if we want to use .append()
      let sheetName = game.time.toString().replace(':', '.').trim();
      // add each game to weeks spreadsheet
      await sheets.spreadsheets.batchUpdate ({
        spreadsheetId : spreadsheetId,
        resource: {requests: [
          {addSheet: {properties: {title: sheetName }}}
        ]}
      });
      console.log("Spreadsheet Id:");
      console.log(spreadsheetId);
      // console.log(sheetName);
      // let sheetId = await getSheetId(spreadsheetId,sheetName);
      // // format cells
      // let formatResources = {
      //   spreadsheetId: sheetId,
      //   resource: {
      //     requests: [
      //       {
      //         repeatCell: {
      //           range: {
      //             sheetId: sheetId,
      //             startRowIndex: 0,
      //           },
      //           cell: {
      //             userEnteredFormat: {
      //               textFormat: {
      //                 bold: true,
      //               },
      //             },
      //           },
      //           fields: "userEnteredFormat.textFormat",
      //         },
      //       },
      //     ],
      //   },
      // };
      //await sheets.spreadsheets.batchUpdate(formatResources);
      // add data to each game sheet
      let resources = {
        spreadsheetId: spreadsheetId,
        resource:{
          valueInputOption: "RAW",
          data:[
            {
              range: "'"   sheetName   "'!A1:I1",
              values: [['First','Last','Email','Position','Number','Team','Goals','Assists','Penalties']]
            },
            {
              range: "'"   sheetName   "'!K1:L1",
              values: [['Team','Shots']]
            },
            {
              range: "'"   sheetName   "'!K2:L3",
              values: [[game.teamA.data.name,''],[game.teamB.data.name,'']]
            },
            {
              range: "'"   sheetName   "'!A2:F12",
              values: game.teamA.data
            },
            {
              range: "'"   sheetName   "'!A14:F24",
              values: game.teamB.data
            }
          ]
        }
      };
      await sheets.spreadsheets.values.batchUpdate(resources);
    }
    // delete "Sheet1" (gid=0) from every spreadsheet
    await sheets.spreadsheets.batchUpdate({
      spreadsheetId: spreadsheetId,
      resource: { requests: [
        {deleteSheet : {sheetId :0}}
      ]}
    });
  }
};

Note: Google Sheets API/Service Details shows that I am not using that many write requests by the way:

enter image description here

Note 2: And I have had my quota limit increased:

enter image description here

Question 1: Am I correctly using batchUpdates, or am I missing a concept that can streamline this code further?

Question 2: My calculations are that I am using 245 write calls to the sheets api, but the "API/Service Details" console is showing 31. Am I missing some concept or miscalculating this somehow? How is my quota being exceeded here? Are the nested 5 arrays a single batchUpdate or 5 batchUpdates? This would add hundreds of write calls to the sheets api if the latter applies.

API Write Calculations:

  1. Create 105 sheets (35 weeks * 3 games) 105 write calls

  2. Add 5 data ranges to each of the 105 sheets 105 write calls (or is this 105 * 5 ?)

  3. Delete "Sheet1" from all 35 sheets using: 35 write calls

Edit: Per request, this is what a week looks like (I cut the length of 'data' , which is normally 10 people for readability. I'd also like to note that I am not having errors with writing these ranges if I choose 1 or 2 weeks instead of 35.

week:

[
  {
    teamA: {
      data: [
        [ 'Robert', 'Manning', '[email protected]', 'C', '45' ],
        [ 'Adrian', 'Martin', '[email protected]', 'RW', '5' ],
      ],
      name: 'Green'
    },
    teamB: {
      data: [
        [ 'Isaac', 'Payne', '[email protected]', 'C', '11' ],
        [ 'Alan', 'Lewis', '[email protected]', 'RW', '13' ],
      ],
      name: 'Orange'
    },
    time: '4:30'
  },
  {
    teamA: {
      data: [
        [ 'Stewart', 'Taylor', '[email protected]', 'RW', '56' ],
        [ 'Lucas', 'Davies', '[email protected]', 'RW', '85' ],
      ],
      name: 'Yellow'
    },
    teamB: {
      data: [
        [ 'Dylan', 'Baker', '[email protected]', 'C', '11' ],
        [ 'Edward', 'Dowd', '[email protected]', 'D', '65' ],
      ],
      name: 'Black'
    },
    time: '6:00'
  },
  {
    teamA: {
      data: [
        [ 'Gavin', 'Knox', '[email protected]', 'C', '45' ],
        [ 'Paul', 'Wallace', '[email protected]', 'RW', '5' ],
      ],
      name: 'Red'
    },
    teamB: {
      data: [
        ['Andrew','Sanderson','[email protected]','C','11'],
        ['Stewart','MacLeod','[email protected]','RW','13'],
      ],
      name: 'Teal'
    },
    time: '7:30'
  }
]

Edit 2: I took bits of code out to restrict the scope of the question to API calls, so things like sheetName definitions were omitted, but here it is:

enter image description here

CodePudding user response:

I believe your goal is as follows.

  • You want to reduce the number of requests of Sheets API by modifying your script.

In this case, how about the following modification?

In this modification, 2 API calls are used in the loop of for (const week of req.body.schedule) {,,,}.

Modified script:

for (const week of req.body.schedule) {
  i  ;
  let fileMetadata = {
    name: "Week-"   i,
    mimeType: "application/vnd.google-apps.spreadsheet",
    parents: [seasonResultsFolderId],
  };
  let result = await drive.files.create({auth: jwtClient, resource: fileMetadata});
  let sheetId = result.data.id;

  const sheetNames = week.map((game) =>
    game.time.toString().replace(":", ".").trim()
  );

  // Add sheets and delete 1st tab.
  await sheets.spreadsheets.batchUpdate({
    spreadsheetId: sheetId,
    resource: {
      requests: [
        ...sheetNames.map((title) => ({
          addSheet: {
            properties: { title },
          },
        })),
        { deleteSheet: { sheetId: 0 } },
      ],
    },
  });

  // Put values to each sheet.
  const data = week.map((game, i) => {
    game.teamA.data.forEach((player) => {
      player.push(game.teamA.name);
    });
    game.teamB.data.forEach((player) => {
      player.push(game.teamB.name);
    });
    return [
      {
        range: "'"   sheetNames[i]   "'!A1:I1",
        values: [
          [
            "First",
            "Last",
            "Email",
            "Position",
            "Number",
            "Team",
            "Goals",
            "Assists",
            "Penalties",
          ],
        ],
      },
      {
        range: "'"   sheetNames[i]   "'!K1:L1",
        values: [["Team", "Shots"]],
      },
      {
        range: "'"   sheetNames[i]   "'!K2:L3",
        values: [
          [game.teamA.name, ""],
          [game.teamB.name, ""],
        ],
      },
      {
        range: "'"   sheetNames[i]   "'!A2:F12",
        values: game.teamA.data,
      },
      {
        range: "'"   sheetNames[i]   "'!A14:F24",
        values: game.teamB.data,
      },
    ];
  });
  await sheets.spreadsheets.values.batchUpdate({
    spreadsheetId: sheetId,
    resource: { valueInputOption: "RAW", data },
  });
}
  • In your situation, at Method: spreadsheets.batchUpdate, all requests for adding sheets and deleting a sheet can be included in one request.
  • In your situation, at Method: spreadsheets.values.batchUpdate, all requests for putting values to each sheet can be included in one request.

Note:

  • When Sheets API is used in a loop, an error might occur because of the continuous requests. When this script is run, such error occurs, please put the script for waiting in the loop.

  • If you want to reduce the number of requests of Drive API, the batch requests might be able to be used. Ref

References:

  • Related