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:
- Detect the new image in the input folder.
- Download the binary.
- Process it with SocialCutter.
- 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:
filewith the binary anddestinationswith 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.readonlyand write on the output one. Thedrive.filescope 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 fulldrivescope. - OAuth client. Create the credentials in Google Cloud, configure the consent screen and use
access_type=offlineandprompt=consentto 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=trueandincludeItemsFromAllDrives=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
| Error | What happens | What to do |
|---|---|---|
| 401 from SocialCutter | The key is missing or revoked | Check the X-API-Key header |
| 413 | The file is over 5 MB | Shrink the master or split the job |
| 422 | Malformed destinations | Use platform and format from the catalogue |
| 429 | Quota exhausted | Check GET /api/v1/wallet |
| 403 from Drive | Missing scope or the account cannot see the folder | Share the folder or widen the scope |
| invalid_grant | Refresh token expired or revoked | Authorise again |
| The same file is processed twice | You are not storing the last id or date | Persist 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
- Sizes and ratios: Social media image sizes: dimensions and ratios
- The real automation paths: Automating social media images: 4 real paths
- Code: Process images with the SocialCutter API from Python
- No-code: Automate image resizing with n8n
- API documentation: https://docs.socialcutter.theboomer.dev
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.