appscript.dev
Automation Intermediate Drive

Auto-generate alt text for images

Describe a folder of Northwind images for accessibility — one prompt per image.

Published Oct 14, 2025

Northwind’s marketing site has hundreds of images and almost none of them have alt text. Writing a one-sentence description for every image is the kind of accessibility job that is genuinely important and reliably never gets done — it is slow, repetitive, and easy to defer.

This script clears the backlog. It walks a Drive folder of images, sends each one to Claude’s vision model, and asks for a concise one-sentence description. The results land in a sheet — filename, alt text, and a link to the image — so a person can skim them, fix anything off, and paste the descriptions into the site. The slow part is done; the review is fast.

What you’ll need

  • A Google Drive folder containing the images you want described.
  • A Google Sheet to collect the results — the script appends to its first tab.
  • An Anthropic API key saved as ANTHROPIC_API_KEY in Script Properties — see Store API keys and secrets securely.
  • The folder ID and sheet ID, which you pass to the function when you run it.

The script

// Claude vision model used to describe each image.
const VISION_MODEL = 'claude-haiku-4-5-20251001';

// The instruction sent with every image.
const ALT_TEXT_PROMPT = 'Write a concise alt text for this image, 1 sentence.';

/**
 * Walks a Drive folder, asks Claude to describe every image, and writes
 * the filename, alt text, and image URL to a sheet.
 *
 * @param {string} folderId The Drive folder of images to describe.
 * @param {string} sheetId  The spreadsheet that collects the results.
 */
function generateAltText(folderId, sheetId) {
  const files = DriveApp.getFolderById(folderId).getFiles();
  const sheet = SpreadsheetApp.openById(sheetId).getSheets()[0];

  let count = 0;

  // 1. Step through every file in the folder.
  while (files.hasNext()) {
    const file = files.next();

    // 2. Skip anything that is not an image — PDFs, docs, and so on.
    if (!file.getMimeType().startsWith('image/')) continue;

    // 3. Read the image bytes and base64-encode them for the API payload.
    const b64 = Utilities.base64Encode(file.getBlob().getBytes());

    // 4. Ask Claude's vision model for a one-sentence description.
    const alt = callClaudeWithImage(ALT_TEXT_PROMPT, b64, file.getMimeType());

    // 5. Record the result: filename, alt text, and a link to the image.
    sheet.appendRow([file.getName(), alt, file.getUrl()]);
    count++;
  }

  Logger.log('Described ' + count + ' images.');
}

/**
 * Sends one image plus a text prompt to the Anthropic API and returns
 * the model's reply. The API key lives in Script Properties.
 *
 * @param {string} prompt The instruction to send with the image.
 * @param {string} b64    The base64-encoded image bytes.
 * @param {string} mime   The image MIME type, e.g. "image/png".
 * @return {string} The model's text reply.
 */
function callClaudeWithImage(prompt, b64, mime) {
  const key = PropertiesService.getScriptProperties()
    .getProperty('ANTHROPIC_API_KEY');

  const res = UrlFetchApp.fetch('https://api.anthropic.com/v1/messages', {
    method: 'post',
    contentType: 'application/json',
    headers: { 'x-api-key': key, 'anthropic-version': '2023-06-01' },
    payload: JSON.stringify({
      model: VISION_MODEL,
      max_tokens: 100,
      messages: [{
        role: 'user',
        content: [
          { type: 'image', source: { type: 'base64', media_type: mime, data: b64 } },
          { type: 'text', text: prompt },
        ],
      }],
    }),
    muteHttpExceptions: true,
  });

  return JSON.parse(res.getContentText()).content[0].text.trim();
}

How it works

  1. generateAltText opens the Drive folder and the results sheet, then steps through every file in the folder one at a time.
  2. It checks each file’s MIME type and skips anything that does not start with image/, so stray PDFs or documents in the folder are ignored.
  3. For each image it reads the raw bytes and base64-encodes them — the format the Anthropic API expects for inline images.
  4. It calls callClaudeWithImage, which sends the encoded image and the prompt to Claude’s vision model and returns a one-sentence description.
  5. It appends a row to the sheet with the filename, the generated alt text, and a Drive link to the original image, so a reviewer can compare description to picture in one place.
  6. callClaudeWithImage reads the API key from Script Properties, builds a multi-part message (the image then the text), and returns the trimmed reply.

Example run

Point the script at a folder of three product photos and an empty results sheet. After a run, the sheet’s first tab holds:

FilenameAlt textLink
header-banner.jpgA team of four collaborating around a laptop in a bright office.drive.google.com/…
product-shot-1.pngA blue ceramic mug on a wooden desk beside an open notebook.drive.google.com/…
warehouse.jpgRows of tall storage shelves stacked with cardboard boxes.drive.google.com/…

A reviewer reads three sentences instead of writing them, tweaks the one that is slightly off, and the alt text is ready for the site.

Run it

This is an on-demand job — you run it when a batch of images needs describing:

  1. In the editor, add a small wrapper that passes your IDs, or run generateAltText from a custom function call with the folder and sheet IDs.
  2. Click Run and approve the authorisation prompt the first time.
  3. Open the results sheet and review the descriptions.
function runAltText() {
  generateAltText('1abcFolderId', '1abcSheetId');
}

Watch out for

  • Each image is a separate API call, so a large folder means a long run. Apps Script caps a single execution at six minutes — for hundreds of images, process the folder in batches or move already-done files to a processed subfolder so reruns skip them.
  • Vision API calls cost more than text calls and use your token budget. Check Anthropic’s pricing before running this over a very large library.
  • The script appends rather than de-duplicates. Running it twice on the same folder writes every image again — clear the sheet first, or track processed files, if you plan to rerun.
  • Very large images can push the request size up and slow things down. Resize oversized photos before describing them if calls start timing out.
  • Generated alt text still needs a human eye. The model describes what it sees, but it cannot know the image’s purpose on the page — a reviewer should always skim the results before they go live.
  • muteHttpExceptions keeps the loop from crashing on a single bad response, but it also means a failed call returns junk. Log and check the output if a row’s alt text looks wrong.

Related