I am using Google Apps Script to fetch data from an external URL. When the request is successful, I can get the HTTP status code with HTTPResponse.getResponseCode
. However, if the request fails with a HTTP status error (such as 404 or 429), UrlFetchApp.fetch()
throws an exception, and I am unable to access the HTTP status code.
function GetHttpResponseCode(url) {
try {
const response = UrlFetchApp.fetch(url);
return response.getResponseCode();
} catch (error) {
return response.getResponseCode(); // ReferenceError: response is not defined
// return error.getResponseCode(); // TypeError: error.getResponseCode is not a function
// throw new Error(error.message);
}
}
GetHttpResponseCode("https://www.google.com/invalidurl") // Should return 404 (Page not Found)
When I catch the error, the response
variable isn't accessible from the catch block. Similarly, calling error.getResponseCode()
results in a TypeError
.
For context, my goal is to detect a 429 (Too Many Requests) response and retry the request after a delay. Here is the actual retry logic I am trying to implement.
function FetchURLContents(url) {
const maxRetries = 3;
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const response = UrlFetchApp.fetch(url);
return response.getContentText();
} catch (error) {
// If statusCode is 429 (too many requests), retry after delay
if (response.getResponseCode() === 429) // ReferenceError here
{
Utilities.sleep(2000);
} else {
throw new Error(error.message);
}
}
}
throw new Error('Failed to fetch URL after multiple attempts.');
}
Is there a way to retrieve the HTTP status code after UrlFetchApp.fetch()
throws an error?
You can prevent UrlFetchApp.fetch
from raising an exception for response codes that indicate failure by setting the advanced parameter muteHttpExceptions=True
.
function GetHttpResponseCode(url) {
const options = {muteHttpExceptions: true};
const response = UrlFetchApp.fetch(url, options);
return response.getResponseCode();
}
function test() {
var responseCode = GetHttpResponseCode("https://www.google.com/invalidurl");
console.log(responseCode); // 404
}
Note that this does mean you need handle unexpected HTTP status errors outside the catch blocks. For my intended use case of retrying on a 429 error, I wrote the following code:
function FetchURLContents(url) {
const maxRetries = 3;
const waitTime = 2000; // 2 seconds
const options = {muteHttpExceptions: true};
for (let attempt = 0; attempt < maxRetries; attempt++) {
console.log(`Sending request to ${url}`);
var response = UrlFetchApp.fetch(url, options);
var responseCode = response.getResponseCode();
var responseBody = response.getContentText();
if (responseCode >= 100 && responseCode < 400) {
// Info/Success/Redirect
console.log("Request successful.");
return responseBody;
} else if (responseCode === 429) {
// Too many requests, wait then retry
if (attempt < maxRetries-1) {
console.log(`You are being rate limited. Retrying again in ${waitTime} ms.`)
Utilities.sleep(waitTime);
} else {
throw new Error(`Failed to fetch url after ${maxRetries} attempts due to rate limits (Status 429).`)
}
} else {
// Unhandled error code
throw new Error(`Request failed for ${url} returned code ${responseCode}. Server response: ${responseBody}`);
}
}
}