google-apps-scriptfacebook-graph-api

GoogleAppScript UrlFetchApp.fetch Batch request to Graph Facebook API


I'm trying to set up batch POST requests from a Google Apps Script project to the Facebook Graph API. I already have many working GET requests to the FB API, as well as POST requests to Telegram from the same project. However, I'm struggling with batch requests.

This function always throws an exception:

Exception: UrlFetch failed because too much traffic is being sent to the specified URL.
    at fetchFbBatchMe (items_status:192:32)
    at __GS_INTERNAL_top_function_call__.gs:1:8

For the next request, the payload is only 80 characters. According to the documentation, UrlFetchApp.fetch allows payloads up to 2048 characters.


function fetchFbBatchMe() {
  let fbApiKey = getTokenFB();

  let batch = {
    'batch': [{
      method: 'GET',
      relative_url: 'me'
    }]};

///I tried a lot of different variations of params and batch
  let params = {
    method: 'post',
    muteHttpExceptions: false,
    headers: {
      'Content-Type': 'application/json',
      'Authorization': fbApiKey
    },
    payload: JSON.stringify(batch)
  };

  try {
///This call always throws an error
    let response = UrlFetchApp.fetch(FB_API_URL, params);

    if (response.getResponseCode() !== 200) {
      Logger.log('Error response: ' + response.getResponseCode());
      Logger.log(response.getContentText());
    } else {
      Logger.log(response.getContentText());
    }
  } catch (e) {
    Logger.log(e);
  }
}

The same request works perfectly in Postman.

I've tried changing params, JSON.stringify-ing and encodeURIComponent-ing them. I’ve also tried applying the same transformations to the payload and to parts of the inner objects. I've attempted setting the contentType property to both 'application/json' and 'application/x-www-form-urlencoded'.

I've checked the Google Issue Tracker, and there are many similar questions but no solution. Example

I also tried the "useIntranet": true parameter suggested in this answer, but it doesn't work either.


Solution

  • After a long trial and error, I finally found a working solution:

    function fetchFbBatchMe() {
      const API_KEY = /*your key here*/;
      const url = `https://graph.facebook.com/`+ 'gg'; //add any random tail to base url
      let payload = {
        batch: [{
          method: 'GET',
          relative_url: 'me' //facebook endpoint
        }]
      };
    
      try {
        let response = UrlFetchApp.fetch(url, {
          method: "post",
          payload: JSON.stringify(payload), // must be stringified, otherwise 400 error
          // contentType: "application/json", // optional: you can set it here 
                                              //instead of headers
          headers: {
            "Content-Type": "application/json", // required for batch requests
            "Authorization": "Bearer " + API_KEY
          },
          muteHttpExceptions: true, // return response even on HTTP errors
        });
    
        if (response.getResponseCode() !== 200)
          Logger.log('Error response: ' + response.getResponseCode());
    
        let responsesArray = JSON.parse(response.getContentText());
        for (let i = 0; i != responsesArray.length; i++) {
          let current = responsesArray[i];
          if (current.code !== 200) {
            Logger.log("Request error code: " + current.code);
            let error = JSON.parse(current.body).error;
            console.log(error.message);
            continue;
          }
          let result = JSON.parse(current.body);
          console.log(result);
          /* Some actions with result */
        }
      } catch (e) {
        Logger.log(e);
      }
    }
    

    Explanation

    1. For some reason, Google Apps Script limits requests to https://grahp.facebook.com/ . At certain times of the day I can get a response, but even the very first request to this URL may be rejected. So it looks like Google applies this restriction to ALL users. This behavior seems really strange to me.
    Exception: UrlFetch failed because too much traffic is being sent to the specified URL.
    
    1. In the meantime, there is a workaround. I found that even if https://graph.facebook.com/{API_VERSION} doesn’t work, sometimes https://graph.facebook.com/{API_VERSION}/? does. I don’t know how long this trick will keep working, especially now that I’m posting it here. :) Hopefully, the limit for certain user is just a bug and will eventually be fixed.

    2. The API version you request can be any you want — Facebook always responds with the latest one, for example:

      {
          "name": "Facebook-API-Version",
          "value": "v23.0"
      },
      
    3. When sending batch requests to the Facebook Graph API, you should always set the content type to application/json. You can either define it in the request payload field (contentType) or as a header ('Content-Type'). Both options work, but if you omit it, you’ll get a 400 error.

    4. Also, always stringify the payload object — otherwise you’ll get a 400 error.