# 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.
- URL: https://socialcutter.theboomer.dev/en/guides/google-drive/
- Idioma: en
- Familia: ia
- Actualizado: 2026-09-24
- Palabras clave: 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](/en/guides/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](/en/guides/medidas-redes-sociales/).

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](/en/guides/formatos-redes-sociales/).

## 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](https://developers.google.com/drive/api/guides/manage-sharing).

## Full Python snippet

```python
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](/en/guides/medidas-redes-sociales/)
- The real automation paths: [Automating social media images: 4 real paths](/en/guides/automatizar-imagenes-redes-sociales/)
- Code: [Process images with the SocialCutter API from Python](/en/guides/python/)
- No-code: [Automate image resizing with n8n](/en/guides/n8n/)
- API documentation: https://docs.socialcutter.theboomer.dev