# How to process images from Google Drive

A common use case for the Photoroom API is to process a large amount of images, which are stored inside a Google Drive folder.

In this tutorial, we'll see how it's possible to process these images by integrating either the [Image Editing API](https://docs.photoroom.com/image-editing-api-plus-plan) or the [Remove Background API](https://docs.photoroom.com/remove-background-api-basic-plan) with a Google Apps Script.

## Step 1: Creating a Google Apps Script

If you've never used Google Apps Script before, it's a convenient way to run code that can easily interact with Google services, including Google Drive.

To create an Apps Script, go to your Google Drive, and click on *New* > *More* > *Google Apps Script*:

<figure><img src="https://2855892273-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F1SYxn7dWbQYsNtUdJE3f%2Fuploads%2FSrBtPOPLcTzKDcVWSra4%2Fcreate-apps-script.gif?alt=media&#x26;token=2c303d9e-0a74-42fa-8cb0-bb21de46c04e" alt=""><figcaption></figcaption></figure>

This will open a new tab that contains a development environment:

<figure><img src="https://2855892273-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F1SYxn7dWbQYsNtUdJE3f%2Fuploads%2FpxeIi20Z4tBvWPKgZYyI%2FScreenshot%202024-07-03%20at%2011.33.41.png?alt=media&#x26;token=29d5c39c-4cc2-4f85-b641-8b695faca887" alt=""><figcaption></figcaption></figure>

The first thing we'll do is to allow the script to interact with Google Drive.

To do that, click on the “+” button next to *Services*:

<figure><img src="https://2855892273-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F1SYxn7dWbQYsNtUdJE3f%2Fuploads%2F8czcHj8LT6uJCyY8vwIi%2FScreenshot%202024-07-03%20at%2011.34.20.png?alt=media&#x26;token=9d16e833-e86d-4573-a3b0-03be3782d5c9" alt=""><figcaption></figcaption></figure>

Then proceed to add both the *Drive API*:

<figure><img src="https://2855892273-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F1SYxn7dWbQYsNtUdJE3f%2Fuploads%2FMUd6D5kGqwuYKHvxwRTT%2FScreenshot%202024-07-03%20at%2011.34.32.png?alt=media&#x26;token=7f057e09-affe-4247-bc69-a6a428beeb49" alt=""><figcaption></figcaption></figure>

You should now see the *Drive* services available in your development environment:

<figure><img src="https://2855892273-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F1SYxn7dWbQYsNtUdJE3f%2Fuploads%2Ft4UsYqriHJuzoQNR9lg2%2FScreenshot%202024-07-03%20at%2011.37.15.png?alt=media&#x26;token=e9d9888b-9ed9-4686-862d-68762f6dc81c" alt=""><figcaption></figcaption></figure>

Now that our environment is set up, it's time to write some code!

You can remove all the sample code `function myFunction() { }` and replace it with this code:

{% tabs %}
{% tab title="Code to integrate with the Image Editing API" %}

```javascript
// Customizable variables
const INPUT_FOLDER_ID = "REPLACE_WITH_YOUR_INPUT_FOLDER_ID";
const OUTPUT_FOLDER_ID = "REPLACE_WITH_YOUR_OUTPUT_FOLDER_ID";
const API_KEY = "REPLACE_WITH_YOUR_API_KEY";
const SANDBOX_MODE = false;
// Params can be expressed as GET params shape
const EDIT_PARAMS = "background.color=transparent&background.scaling=fill&outputSize=1000x1000&padding=0.1";
// Or as direct payload
// const EDIT_PARAMS = {
//   "background.color": "transparent",
//   "background.scaling": "fill",
//   outputSize: "1000x1000",
//   padding: 0.1
// }
const inputFolder = DriveApp.getFolderById(INPUT_FOLDER_ID);
const outputFolder = DriveApp.getFolderById(OUTPUT_FOLDER_ID);
const formDataPayload = generatePayloadFromParams(EDIT_PARAMS);

// The main function to execute the script
function imageEditingAPIMain() {
  const lock = LockService.getScriptLock();
  try {
    if (!lock.tryLock(0)) {
      console.error('Exiting, script is already running.');
      return;
    }

    const imagesToProcess = [];
    const files = inputFolder.getFiles();
    while (files.hasNext()) {
      const file = files.next();
      if (isImageFile(file) && !isFileProcessed(file.getName())) {
        imagesToProcess.push(file);
      }
    }
    console.log(`Found ${imagesToProcess.length} images to process`);
    
    const batchSize = 8; // Adjust this based on your API's rate limits
    // Process images and send them to the OUTPUT_FOLDER
    for (let i = 0; i < imagesToProcess.length; i += batchSize) {
      const batch = imagesToProcess.slice(i, i + batchSize);
      const requests = batch.map((imageToProcess) => {
        const originUrl = "https://image-api.photoroom.com";
        return {
          url: `${originUrl}/v2/edit`,
          method: "post",
          headers: {
            "x-api-key": `${SANDBOX_MODE ? "sandbox_" : ""}${API_KEY}`,
          },
          payload: {
            ...formDataPayload,
            imageFile: imageToProcess.getBlob(),
          },
          muteHttpExceptions: true,
        };
      });
      const responses = UrlFetchApp.fetchAll(requests);
      for (let j = 0; j < responses.length; j++) {
        const response = responses[j];
        const imageToProcess = batch[j];
        if (response.getResponseCode() === 200) {
          try {
            const processedImageBlob = response.getBlob();
            outputFolder.createFile(processedImageBlob.setName(imageToProcess.getName()));
            console.log(`Processed '${imageToProcess.getName()}'`);
          } catch (e) {
            console.error(`Failed to process ${imageToProcess.getName()}: ${e.message}`);
          }
        } else {
          console.error(`Failed to process ${imageToProcess.getName()}: ${response.getContentText()}`);
        }
      }
    }
  } catch (error) {
    console.error('Script failed with error:', error);
  } finally {
    // Always release the lock, even if an error occurred
    lock.releaseLock();
  }
}

function isFileProcessed(fileName) {
  const files = outputFolder.getFilesByName(fileName);
  return files.hasNext();
}

function isImageFile(file) {
  const mimeType = file.getMimeType();
  return mimeType.indexOf("image/") === 0;
}

function generatePayloadFromParams(editParams) {
  if (typeof editParams === "object") {
    return editParams;
  }
  return editParams
    .split("&")
    .map((param) => param.split("="))
    .reduce((acc, [key, value]) => {
      acc[key] = decodeURIComponent(value);
      return acc;
    }, {});
}
```

There's quite a bit of code. Fortunately, you will only need to configure a few parts:

1. update `const API_KEY` with your Photoroom API key

{% hint style="info" %}
If you don't have an API key, here are the [steps to create yours](https://docs.photoroom.com/getting-started/introduction#how-can-i-get-my-api-key).

⚠️ Make sure **not to include** `sandbox_` when copy/pasting your API key ⚠️
{% endhint %}

2. update `const INPUT_FOLDER_ID` and `const OUTPUT_FOLDER_ID` with respectively the id of the Google Drive folder where you want to read the input images and save the result images

{% hint style="info" %}
To get the id of a Google Drive folder, you just need to look at the folder's URL:

[https://drive.google.com/drive/u/0/folders/THIS\_IS\_THE\_FOLDER\_ID](https://drive.google.com/drive/u/0/folders/10s7jyIaTzIyWHClYa4QfxgzYQeXJaRcV)
{% endhint %}

3. set the value of `const SANDBOX_MODE` depending on whether or not you want to use [Sandbox mode](https://docs.photoroom.com/image-editing-api-plus-plan/sandbox-mode)
4. set the value of `const EDIT_PARAMS` with the edits that you want to apply to your images

{% hint style="info" %}
To define which edits you want to apply, we recommend that you use our [Interactive API Playground](https://www.photoroom.com/api/playground).

<img src="https://2855892273-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F1SYxn7dWbQYsNtUdJE3f%2Fuploads%2FeSsGqeEmwoZ6rKziaQT3%2FScreen%20Recording%202024-07-03%20at%2014.32.58.gif?alt=media&#x26;token=bbea1f97-0e41-445b-8132-d7bd2a81a132" alt="" data-size="original">
{% endhint %}

That's all the changes you need to make, the rest of the code can be used as-is.
{% endtab %}

{% tab title="Code to integrate with the Remove Background API" %}

```javascript
// Customizable variables
const INPUT_FOLDER_ID = "REPLACE_WITH_YOUR_INPUT_FOLDER_ID";
const OUTPUT_FOLDER_ID = "REPLACE_WITH_YOUR_OUTPUT_FOLDER_ID";
const API_KEY = "REPLACE_WITH_YOUR_API_KEY";
const SANDBOX_MODE = false;
const BACKGROUND_REMOVAL_PARAMS = {
  format: "png",
  channels: "rgba",
  size: "full",
  crop: false,
  despill: false,
}
const inputFolder = DriveApp.getFolderById(INPUT_FOLDER_ID);
const outputFolder = DriveApp.getFolderById(OUTPUT_FOLDER_ID);
const formDataPayload = generatePayloadFromParams(BACKGROUND_REMOVAL_PARAMS);

// The main function to execute the script
function backgroundRemovalAPIMain() {
  const lock = LockService.getScriptLock();
  try {
    if (!lock.tryLock(0)) {
      console.error('Exiting, script is already running.');
      return;
    }

    const imagesToProcess = [];
    const files = inputFolder.getFiles();
    while (files.hasNext()) {
      const file = files.next();
      if (isImageFile(file) && !isFileProcessed(file.getName())) {
        imagesToProcess.push(file);
      }
    }
    console.log(`Found ${imagesToProcess.length} images to process`);
    
    const batchSize = 8; // Adjust this based on your API's rate limits
    // Process images and send them to the OUTPUT_FOLDER
    for (let i = 0; i < imagesToProcess.length; i += batchSize) {
      const batch = imagesToProcess.slice(i, i + batchSize);
      const requests = batch.map((imageToProcess) => {
        return {
          url: "https://sdk.photoroom.com/v1/segment",
          method: "post",
          headers: {
            "x-api-key": `${SANDBOX_MODE ? "sandbox_" : ""}${API_KEY}`,
          },
          payload: {
            ...formDataPayload,
            image_file: imageToProcess.getBlob(),
          },
          muteHttpExceptions: true,
        };
      });
      const responses = UrlFetchApp.fetchAll(requests);
      for (let j = 0; j < responses.length; j++) {
        const response = responses[j];
        const imageToProcess = batch[j];
        if (response.getResponseCode() === 200) {
          try {
            const processedImageBlob = response.getBlob();
            outputFolder.createFile(processedImageBlob.setName(imageToProcess.getName()));
            console.log(`Processed '${imageToProcess.getName()}'`);
          } catch (e) {
            console.error(`Failed to process ${imageToProcess.getName()}: ${e.message}`);
          }
        } else {
          console.error(`Failed to process ${imageToProcess.getName()}: ${response.getContentText()}`);
        }
      }
    }
  } catch (error) {
    console.error('Script failed with error:', error);
  } finally {
    // Always release the lock, even if an error occurred
    lock.releaseLock();
  }
}

function isFileProcessed(fileName) {
  const files = outputFolder.getFilesByName(fileName);
  return files.hasNext();
}

function isImageFile(file) {
  const mimeType = file.getMimeType();
  return mimeType.indexOf("image/") === 0;
}

function generatePayloadFromParams(editParams) {
  if (typeof editParams === "object") {
    return editParams;
  }
  return editParams
    .split("&")
    .map((param) => param.split("="))
    .reduce((acc, [key, value]) => {
      acc[key] = decodeURIComponent(value);
      return acc;
    }, {});
}


```

There's quite a bit of code. Fortunately, you will only need to configure a few parts:

1. update `const API_KEY` with your Photoroom API key

{% hint style="info" %}
If you don't have an API key, here are the [steps to create yours](https://docs.photoroom.com/getting-started/introduction#how-can-i-get-my-api-key).

⚠️ Make sure **not to include** `sandbox_` when copy/pasting your API key ⚠️
{% endhint %}

2. update `const INPUT_FOLDER_ID` and `const OUTPUT_FOLDER_ID` with respectively the id of the Google Drive folder where you want to read the input images and save the result images

{% hint style="info" %}
To get the id of a Google Drive folder, you just need to look at the folder's URL:

[https://drive.google.com/drive/u/0/folders/THIS\_IS\_THE\_FOLDER\_ID](https://drive.google.com/drive/u/0/folders/10s7jyIaTzIyWHClYa4QfxgzYQeXJaRcV)
{% endhint %}

3. set the value of `const BACKGROUND_REMOVAL_PARAMS` to configure the parameters of the API call

{% hint style="info" %}
The documentation of each of these parameters is [available here](https://www.photoroom.com/api/docs/reference/71f4c26e59e43-remove-background-basic-plan).
{% endhint %}

That's all the changes you need to make, the rest of the code can be used as-is.
{% endtab %}
{% endtabs %}

## Step 2: Running the Google Apps Script

Then, the next step is to run the script!

To execute the script, you just need to click on the button *Run*:

<figure><img src="https://2855892273-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F1SYxn7dWbQYsNtUdJE3f%2Fuploads%2FNCqniX91PBEUjUulQgYw%2FScreenshot%202024-07-03%20at%2012.10.33.png?alt=media&#x26;token=26733340-eb1d-4f84-a21f-134166f1e061" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
The first time you run the script, you'll be prompted to give it permission to access your Google Sheets and Google Drive.
{% endhint %}

While the script is running, you'll see appear the execution log which lets you monitor its progress and will provide useful debug information should an error occur.

<figure><img src="https://2855892273-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F1SYxn7dWbQYsNtUdJE3f%2Fuploads%2F5iAHDfe8eyTlq8Xmvk9j%2FScreenshot%202024-07-03%20at%2012.19.43.png?alt=media&#x26;token=9b680dc7-53e8-4e73-a112-2757469bab9f" alt=""><figcaption></figcaption></figure>

The Google Drive folder whose id you used should also contain the result images:

<figure><img src="https://2855892273-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F1SYxn7dWbQYsNtUdJE3f%2Fuploads%2Fy1FW4681VxaU0ekBFs30%2FScreenshot%202024-07-03%20at%2012.29.28.png?alt=media&#x26;token=5e884e1b-d62e-477e-be50-e722b8d64a44" alt=""><figcaption></figcaption></figure>

## Step 3: Scheduling Execution of the Google Apps Script

In addition to manually executing the Google Apps Script from the development environment, it's also possible to schedule its execution at predefined times.

To schedule the execution of the script, click on *Trigger* in the left menu, then on *Add Trigger*:

<div><figure><img src="https://2855892273-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F1SYxn7dWbQYsNtUdJE3f%2Fuploads%2FLujQMaMefVcj2KtIKN6z%2FScreenshot%202024-07-03%20at%2012.14.49.png?alt=media&#x26;token=48ffd517-42a3-43d9-bf1d-514c688bee2c" alt=""><figcaption></figcaption></figure> <figure><img src="https://2855892273-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F1SYxn7dWbQYsNtUdJE3f%2Fuploads%2FcOveaO18evFdc1zmUf1c%2FScreenshot%202024-07-03%20at%2012.15.10.png?alt=media&#x26;token=180d696f-a4e7-48cf-9837-aaafb00db8c8" alt=""><figcaption></figcaption></figure></div>

Then make sure that the function to be executed is indeed the correct one, then select the periodicity at which you want your script to be executed, and finally save the trigger:

<figure><img src="https://2855892273-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F1SYxn7dWbQYsNtUdJE3f%2Fuploads%2FEB9dFYLp5MlV4mHAj36v%2FScreenshot%202024-07-03%20at%2012.15.20.png?alt=media&#x26;token=88834cf3-1e18-41d7-94b4-a7ab8dcd2a03" alt=""><figcaption></figcaption></figure>

## Conclusion

In this tutorial, we saw how to use a Google Apps Script to easily process images stored inside Google Drive through the Photoroom API.

It's worth noting that a very similar approach can also be used in the situation where you want to process images stored in another service, using a [combination of a Google Apps Script and a Google Sheet](https://docs.photoroom.com/integrations/how-to-process-images-from-google-sheets).
