Automate Image Alt Text with n8n + Ollama (AI Accessibility Workflow)

Published March 24, 2026 · 12 min read · By Workflow Forge

Missing or poor alt text is the #1 accessibility violation on the web. WebAIM’s 2025 study found that 54% of all website images lack adequate alternative text. For businesses, that means legal risk under the ADA, EAA (European Accessibility Act), and WCAG 2.2 guidelines — and it means millions of screen reader users can’t interact with your content.

Fixing this manually is painful. A mid-size e-commerce store with 10,000 product images would take weeks of human effort to audit and describe. Cloud vision APIs (Google Vision, Azure, AWS Rekognition) charge $1–4 per 1,000 images and send all your product photos to third-party servers.

With n8n and Ollama, you can generate WCAG-compliant alt text at scale on your own hardware — for free, with full privacy. This tutorial builds a workflow that:

  1. Scans your website, CMS, or image directory for images missing alt text
  2. Sends each image to a local vision model (LLaVA) running on Ollama
  3. Generates context-aware, descriptive alt text following WCAG 2.2 guidelines
  4. Validates the output for length, quality, and relevance
  5. Updates your CMS, database, or exports a CSV for bulk import

Why Local AI for Alt Text Generation?

FeatureCloud Vision APIsSelf-Hosted (n8n + Ollama)
Cost per 1,000 images$1–4$0 (your hardware)
10,000 images$10–40$0
100,000 images$100–400$0
Image data privacySent to cloud providerNever leaves your server
Rate limitsYes (varies by provider)None
WCAG-specific outputGeneric captionsCustom prompts for compliance
Customizable for domainLimitedFull prompt control per image type
Privacy matters: Product images, internal documentation screenshots, medical images, and user-uploaded content should not be sent to external APIs without consideration. Local processing with Ollama keeps all image data on your infrastructure — critical for GDPR, HIPAA, and internal security policies.

Prerequisites

Pull the vision model:

ollama pull llava:13b
# Or for faster processing with less RAM:
ollama pull llava:7b

Architecture Overview

Image Source (Website crawl / CMS API / Directory scan)
    ↓
[Find Images] — Identify images with missing or empty alt text
    ↓
[Download Image] — Fetch image binary for analysis
    ↓
[Analyze Image] — LLaVA vision model generates description
    ↓
[Format Alt Text] — Apply WCAG guidelines (length, context, decorative check)
    ↓
[Validate] — Quality check: not too long, not too generic, relevant
    ↓
[Output] — Update CMS, write to database, or export CSV

WCAG 2.2 Alt Text Guidelines

Before building the workflow, understand what makes good alt text according to W3C guidelines:

Step-by-Step Build

STEP 1 — Image Discovery

Find images that need alt text

Use an HTTP Request node to crawl your site, or connect to your CMS API to find images with missing alt text.

Option A: WordPress REST API

GET https://yoursite.com/wp-json/wp/v2/media?per_page=100&mime_type=image/jpeg

// Filter in n8n Function node:
const needsAlt = items.filter(item =>
  !item.json.alt_text || item.json.alt_text.trim() === ''
);
return needsAlt;

Option B: Shopify API (product images)

GET https://yourstore.myshopify.com/admin/api/2024-01/products.json?fields=id,title,images

// Extract images missing alt text:
const images = [];
for (const product of items) {
  for (const img of product.json.images) {
    if (!img.alt || img.alt.trim() === '') {
      images.push({
        json: {
          product_id: product.json.id,
          product_title: product.json.title,
          image_id: img.id,
          image_url: img.src,
          current_alt: img.alt || ''
        }
      });
    }
  }
}
return images;

Option C: HTML crawl

// Use HTTP Request to fetch page HTML, then parse with Function node:
const cheerio = require('cheerio');  // n8n has cheerio built in
const $ = cheerio.load(items[0].json.data);
const missingAlt = [];

$('img').each((i, el) => {
  const alt = $(el).attr('alt');
  const src = $(el).attr('src');
  if (!alt || alt.trim() === '') {
    missingAlt.push({
      json: { src, page_url: items[0].json.url, index: i }
    });
  }
});
return missingAlt;
STEP 2 — Image Download

Fetch image binary for vision model analysis

Use an HTTP Request node to download each image as binary data:

// HTTP Request node settings:
Method: GET
URL: {{ $json.image_url }}
Response Format: File
Output Field: image_data

For local directories, use the Read Binary File node to load images directly.

STEP 3 — Vision Model Analysis

Generate descriptions with LLaVA via Ollama

This is the core step. Send the image to Ollama’s vision model with a WCAG-optimized prompt:

// HTTP Request node to Ollama API:
POST http://localhost:11434/api/generate

{
  "model": "llava:13b",
  "prompt": "You are an accessibility expert writing alt text for screen readers. Describe this image following WCAG 2.2 guidelines:\n\n1. Be specific and concise (under 125 characters)\n2. Describe the content and function, not just appearance\n3. Do NOT start with 'image of', 'photo of', or 'picture of'\n4. If this is a decorative/background image with no informational content, respond with exactly: DECORATIVE\n5. Include relevant details: colors, text visible in image, actions depicted\n\nContext: This image is from a {{ $json.page_context || 'website' }}.\nProduct name (if applicable): {{ $json.product_title || 'N/A' }}\n\nWrite ONLY the alt text, nothing else.",
  "images": ["{{ $binary.image_data.base64 }}"],
  "stream": false,
  "options": {
    "temperature": 0.3,
    "num_predict": 80
  }
}
Prompt engineering tip: Low temperature (0.3) ensures consistent, factual descriptions. The “DECORATIVE” instruction prevents the model from generating unnecessary alt text for spacer images, backgrounds, and borders that should have empty alt attributes.
STEP 4 — Domain-Specific Prompts

Customize prompts for different image types

Different contexts need different descriptions. Use a Switch node to route images to specialized prompts:

// Function node: choose prompt based on context
const context = $json.page_context || 'general';

const prompts = {
  'product': `Describe this product image for an online store listing.
Include: product type, color, material, key features visible.
Format: "[Product feature] [color/material] [product type]"
Example: "Stainless steel insulated water bottle with flip-top lid, 32oz, matte black finish"
Keep under 125 characters.`,

  'blog': `Describe this image for a blog article.
Focus on what the image communicates in the context of the article.
Be descriptive but concise. Under 125 characters.`,

  'icon': `This is a UI icon or button image.
Describe its function, not its appearance.
Example: "Search", "Close menu", "Add to cart"
If purely decorative, respond: DECORATIVE`,

  'chart': `Describe this chart or graph for someone who cannot see it.
Include: chart type, what is being measured, key trends or values.
Example: "Bar chart showing monthly revenue from Jan to Dec 2025, trending upward from $10K to $45K"`,

  'general': `Describe this image concisely for a screen reader user.
Be specific about content. Under 125 characters.
Do not start with "image of" or "photo of".`
};

return [{ json: { ...items[0].json, prompt: prompts[context] || prompts.general } }];
STEP 5 — Validation and Formatting

Quality check the generated alt text

// Function node: validate and clean alt text
const altText = $json.generated_alt.trim();
const result = { ...items[0].json };

// Check if decorative
if (altText === 'DECORATIVE' || altText.toLowerCase().includes('decorative')) {
  result.final_alt = '';
  result.is_decorative = true;
  return [{ json: result }];
}

let cleaned = altText;

// Remove "image of", "photo of" prefixes
cleaned = cleaned.replace(/^(an? )?(image|photo|picture|photograph|screenshot|icon) (of|showing|depicting) /i, '');

// Capitalize first letter
cleaned = cleaned.charAt(0).toUpperCase() + cleaned.slice(1);

// Remove trailing period (screen readers add their own pause)
cleaned = cleaned.replace(/\.$/, '');

// Enforce max length (125 chars for screen reader compatibility)
if (cleaned.length > 125) {
  cleaned = cleaned.substring(0, 122) + '...';
}

// Quality flags
result.final_alt = cleaned;
result.is_decorative = false;
result.char_count = cleaned.length;
result.quality_warnings = [];

if (cleaned.length < 10) result.quality_warnings.push('too_short');
if (cleaned.length > 125) result.quality_warnings.push('too_long');
if (/^(a |the |an )/i.test(cleaned)) result.quality_warnings.push('starts_with_article');
if (/image|photo|picture/i.test(cleaned)) result.quality_warnings.push('contains_image_word');

return [{ json: result }];
STEP 6 — Output and Update

Write alt text back to your CMS or export

Option A: Update WordPress

// HTTP Request node:
POST https://yoursite.com/wp-json/wp/v2/media/{{ $json.image_id }}

{
  "alt_text": "{{ $json.final_alt }}"
}

Option B: Update Shopify product images

// HTTP Request node:
PUT https://yourstore.myshopify.com/admin/api/2024-01/products/{{ $json.product_id }}/images/{{ $json.image_id }}.json

{
  "image": {
    "id": {{ $json.image_id }},
    "alt": "{{ $json.final_alt }}"
  }
}

Option C: Export CSV for manual review

// Spreadsheet File node:
// Columns: image_url, current_alt, generated_alt, is_decorative, char_count, quality_warnings
// Output: alt-text-audit-2026-03-24.csv

Batch Processing for Large Sites

Processing 10,000+ images requires careful batching to avoid overwhelming Ollama:

// Function node: batch controller
const BATCH_SIZE = 20;
const DELAY_MS = 1000; // 1 second between batches

const allImages = items;
const batches = [];

for (let i = 0; i < allImages.length; i += BATCH_SIZE) {
  batches.push(allImages.slice(i, i + BATCH_SIZE));
}

// Process batches with the Loop Over Items node
// Set batch size to 20, add a Wait node (1s) between batches
// LLaVA processes ~2-5 images/second on CPU, 10-20/second on GPU
Performance estimates:
  • CPU (8-core): ~3 seconds per image → 1,200 images/hour
  • GPU (RTX 3060): ~0.5 seconds per image → 7,200 images/hour
  • GPU (RTX 4090): ~0.2 seconds per image → 18,000 images/hour
A 10,000-image e-commerce catalog takes ~2.5 hours on CPU or ~25 minutes on a mid-range GPU.

Scheduled Audits: Keep Alt Text Current

New images are added constantly. Set up a Cron trigger to run the workflow weekly:

// Cron node: Run every Sunday at 2 AM
// Only process images added/modified since last run

// Function node: filter by date
const lastRun = $workflow.staticData.lastRun || '2000-01-01';
const newImages = items.filter(img =>
  new Date(img.json.date_created) > new Date(lastRun)
);

// Save timestamp for next run
$workflow.staticData.lastRun = new Date().toISOString();
return newImages;

Compliance Reporting

Generate an accessibility audit report after each run:

// Function node: generate report
const total = items.length;
const decorative = items.filter(i => i.json.is_decorative).length;
const generated = items.filter(i => !i.json.is_decorative && i.json.final_alt).length;
const warnings = items.filter(i => i.json.quality_warnings?.length > 0).length;

const report = {
  date: new Date().toISOString().split('T')[0],
  total_images_scanned: total,
  decorative_images: decorative,
  alt_text_generated: generated,
  needs_review: warnings,
  compliance_rate: ((generated + decorative) / total * 100).toFixed(1) + '%'
};

return [{ json: report }];

Send the report via email, Slack, or save to a dashboard. Track your compliance rate over time to demonstrate WCAG progress to stakeholders.

Real-World Use Cases

SEO Benefits of Alt Text

Alt text isn’t just for accessibility — it directly impacts search rankings:

Case study: An e-commerce store that added descriptive alt text to all 8,000 product images saw a 23% increase in organic traffic from Google Image Search within 3 months. The workflow paid for the hardware investment in the first week.

Want the Complete Workflow?

The Self-Hosted AI Workflow Pack includes 11 production-ready n8n templates — including image analysis, content generation, document processing, and more. All powered by Ollama. Zero API costs.

Get 11 AI Workflows — $39

One-time payment. No subscription. 30-day guarantee.

Troubleshooting

LLaVA not generating good descriptions

Ollama connection issues from n8n

Processing too slow

Related Tutorials