Skip to main content
SocialCutter

AI and agents

Process Google Drive images with SocialCutter

Watch a Drive folder, download the new image, process it with SocialCutter and upload every result to another folder: OAuth, permissions and Python.

  • Google Drive
  • automation
  • Drive API
  • watched folder
  • Python
  • SocialCutter
  • centered crop

An input and output flow

The case shows up in almost every team: someone drops the design into a Drive folder and the versions for each network must come out of it. SocialCutter does not enter Drive or watch it on its own: it is an intermediate step that takes an image and a list of destinations and returns one URL per output. Moving the files is your script’s job, through the Drive API.

The loop has four steps:

  1. Detect the new image in the input folder.
  2. Download the binary.
  3. Process it with SocialCutter.
  4. Upload every result to the output folder.

You can build it with your own code (this guide) or without writing any, using the Google Drive Trigger node in n8n: Automate image resizing with n8n.

Detect the new image

The Drive API (v3) offers three routes:

Polling a folder

This is the most direct option when a single folder is all you care about. Query it with files.list, filtering by the folder and ordering by date:

q = "'<FOLDER_ID>' in parents and trashed = false"
fields = "files(id, name, mimeType, modifiedTime)"
orderBy = "modifiedTime desc"

Store the modifiedTime of the last file you handled and process only the newest. It is polling: nothing arrives instantly, but it needs no infrastructure.

The changes feed

changes.getStartPageToken() returns a token, and changes.list(pageToken=...) hands you only what changed since then. It is cheaper than walking the whole folder on every pass. One important detail: the changes feed covers the whole account or shared drive, not a single folder. If you use it, filter by parents yourself.

Real-time notifications

files.watch registers a channel that pings your server when something changes, with no polling. It needs a public HTTPS endpoint and the channel must be renewed periodically. For this guide it is optional: polling and the feed cover most cases.

Download the binary

With the fileId in hand, the download is files.get_media. In Python, MediaIoBaseDownload lands the file in a BytesIO ready to forward. You do not have to make it public or sign a link: the binary travels from Drive to SocialCutter inside your process.

Google Docs, Sheets and Slides files have no direct binary: export them first (files.export) to an image format. SocialCutter works with raster images, not documents.

Process it with SocialCutter

The binary goes up as multipart to the upload endpoint, with the destination list as a form field:

  • Method: POST
  • URL: https://api.socialcutter.theboomer.dev/api/v1/images/process/upload
  • Header: X-API-Key: sc_your_key
  • Fields: file with the binary and destinations with the JSON list

The response carries an outputs array with one entry per destination and the fields platform, format, url, width, height and size_bytes. Destinations come from the real catalogue: 6 platforms and 13 destinations with exact dimensions, listed in Social media image sizes: dimensions and ratios.

If the image already has a reachable URL, the alternative is POST /api/v1/images/process with source: { "type": "url", "value": "..." }. With Drive that is usually less convenient because it forces you to sign a temporary link.

Upload the results to another folder

Each output URL is downloaded and created in the target folder with files.create, passing parents and a media_body. A useful naming pattern keeps the original name and appends platform and format:

original-instagram-post.webp
original-instagram-story.webp
original-linkedin-post.webp

WebP is the default output (options.format); if the Drive library prefers JPG or PNG, set it in the same request. The criteria are in PNG, JPG or WebP: which format to use on each network.

OAuth and permission requirements

  • Scopes. Read on the input folder with https://www.googleapis.com/auth/drive.readonly and write on the output one. The drive.file scope only reaches files your app creates or the user picks through the Drive picker; to write into an existing folder the usual choice is the full drive scope.
  • OAuth client. Create the credentials in Google Cloud, configure the consent screen and use access_type=offline and prompt=consent to obtain a lasting refresh token. Keep it out of the code.
  • Service account. For unattended jobs, create a service account and share both folders with its address. On Workspace this may require domain-wide delegation. On shared drives add supportsAllDrives=true and includeItemsFromAllDrives=true.
  • Upload limit. 5 MB per file in SocialCutter; above that it returns 413.

Scope and parameter names are set by Google; confirm them in the official documentation: Drive API sharing and permission guide.

Full Python snippet

import io, json, os, requests
from google.oauth2.credentials import Credentials
from googleapiclient.discovery import build
from googleapiclient.http import MediaIoBaseDownload, MediaIoBaseUpload

SOURCE_FOLDER = "<INPUT_FOLDER_ID>"
DEST_FOLDER = "<OUTPUT_FOLDER_ID>"
DESTINATIONS = [
    {"platform": "instagram", "format": "post"},
    {"platform": "instagram", "format": "story"},
    {"platform": "linkedin", "format": "post"},
]
SC_KEY = os.environ["SOCIALCUTTER_API_KEY"]

drive = build("drive", "v3",
              credentials=Credentials.from_authorized_user_file("token.json"))

files = drive.files().list(
    q=f"'{SOURCE_FOLDER}' in parents and trashed = false",
    orderBy="modifiedTime desc",
    fields="files(id, name)",
    pageSize=5,
).execute().get("files", [])

for f in files:
    buf = io.BytesIO()
    downloader = MediaIoBaseDownload(buf, drive.files().get_media(fileId=f["id"]))
    done = False
    while not done:
        _, done = downloader.next_chunk()

    r = requests.post(
        "https://api.socialcutter.theboomer.dev/api/v1/images/process/upload",
        headers={"X-API-Key": SC_KEY},
        files={"file": (f["name"], buf.getvalue(), "image/jpeg")},
        data={"destinations": json.dumps(DESTINATIONS)},
        timeout=90,
    )
    r.raise_for_status()

    for out in r.json()["outputs"]:
        img = requests.get(out["url"], timeout=90)
        img.raise_for_status()
        media = MediaIoBaseUpload(io.BytesIO(img.content),
                                  mimetype="image/webp", resumable=False)
        drive.files().create(
            body={"name": f"{f['name']}-{out['platform']}-{out['format']}.webp",
                  "parents": [DEST_FOLDER]},
            media_body=media,
            fields="id",
        ).execute()

Cost

  • 1 use per destination (platform and format) per image.
  • Duplicate destinations in one request are not charged twice.
  • Failed processing jobs are refunded.
  • Plans at 0, 3, 9 and 29 EUR, with the API and the MCP server included.

One image with the three destinations above costs 3 uses. If the folder receives bursts, batch before calling or use POST /api/v1/images/batch.

Typical errors

ErrorWhat happensWhat to do
401 from SocialCutterThe key is missing or revokedCheck the X-API-Key header
413The file is over 5 MBShrink the master or split the job
422Malformed destinationsUse platform and format from the catalogue
429Quota exhaustedCheck GET /api/v1/wallet
403 from DriveMissing scope or the account cannot see the folderShare the folder or widen the scope
invalid_grantRefresh token expired or revokedAuthorise again
The same file is processed twiceYou are not storing the last id or datePersist the handled modifiedTime or use the changes feed

What SocialCutter does not do

The crop is centered and deterministic: there is no subject or face detection and no smart cropping. It does not edit the image (no colour grading, no background removal, no text compositing), it does not post to social networks and it does not accept files over 5 MB. It generates versions at the exact size of each destination and returns their URLs: the exchange with Drive and the publishing are your script’s job.

Next steps

Frequently asked questions

Can I watch a Drive folder without running my own server?

Yes. For a single folder, query the Drive API with a folder filter ordered by date. If you prefer not to code, the Google Drive Trigger node in n8n covers the same case with no server of your own.

Which Drive permissions do I need?

Read on the input folder (drive.readonly) and write on the output folder. The drive.file scope only reaches files your app creates or the user picks by hand; to write into an existing folder the usual choice is the full drive scope.

How do I call SocialCutter when the Drive file is not public?

You do not need a public URL. Download the binary with the Drive API and upload it as multipart to /api/v1/images/process/upload. The limit is 5 MB per file.

Can I use a service account instead of a user?

Yes, for unattended jobs. Create the service account and share both folders with its address. On Google Workspace you may need domain-wide delegation, and on shared drives the supportsAllDrives and includeItemsFromAllDrives parameters.

What does each processed image cost?

1 use per destination, meaning each platform and format pair. An image with three destinations costs 3 uses. Plans are 0, 3, 9 and 29 EUR and include the API and the MCP server.