Skip to content
Coding Script or NotebookGoogle ColabFree

Image SEO Audit and Bulk AI Alt Text Generator (Colab Notebook)

ForVideo and visualContent audits

By Lazarina Stoy· Updated 24 Oct 2026

Images are often the least audited part of a page: missing alt text, 2 MB PNGs, the same photo uploaded five times. This free Colab notebook gives your pages a full image SEO pass in one run, and it can draft alt text for the images that have none, using a computer vision model.

Who it’s for

Ecommerce and content SEOs with large image libraries, and anyone who needs to fix accessibility gaps across many pages at once.

How to use it

  1. Add your pages. One URL per line. Start with 5 to 20 important templates (home, category, product, article) rather than the whole site.
  2. Choose your options. Set the weight threshold, the alt text length flag, whether to classify images, and the alt text backend: a free open model in Colab, or the OpenAI or Gemini API with a key in Colab Secrets.
  3. Run the audit. It finds every image (including lazy-loaded and srcset images, picture sources, inline CSS backgrounds and og:image) and measures each one.
  4. Read the page summary and issues. Flags include missing or empty alt, filename-style or stuffed alt text, generic file names, missing width and height, a lazy-loaded first image, heavy files, legacy formats with no AVIF or WebP, no srcset, CSS images Google won’t index, and broken images.
  5. Check your image mix. CLIP zero-shot labels (product on white, lifestyle, screenshot, logo, infographic and your own labels) show what kinds of images you use.
  6. Review the alt text drafts for images with no alt attribute.
  7. Review near-duplicates: resized copies by perceptual hash, and visually near-identical images by CLIP similarity.

What you get

images.csv (one row per image with every measurement, the issues and the alt suggestion), pages.csv (counts per page) and duplicates.csv.

Costs and limits

  • The default alt text model, SmolVLM-500M-Instruct, and CLIP run free in Colab. The OpenAI and Gemini options are billed per image by the provider.
  • Alt text drafts are drafts. In testing, the small open model invented text on a dog’s vest and a breed that doesn’t exist, and a larger model still got a colour wrong. Neither could explain what a diagram was for. A human needs to check every one.
  • CLIP similarity needs calibrating: two different photos of the same breed scored close to a resized copy. That’s why the notebook also uses a perceptual hash.
  • Weight thresholds are audit conventions, not Google rules.

For the strategy behind every check, read Image SEO in 2026: The Complete Guide.

What’s included

  • Finds every img, picture source, inline CSS background and og:image on your pages
  • Checks real format, file size, pixel size, alt text, file names, width/height, srcset, lazy-loading and AVIF/WebP support
  • Drafts alt text for images with no alt attribute using a free open vision model, or the OpenAI or Gemini API
  • CLIP zero-shot labels show your image mix; pHash and CLIP similarity find near-duplicates
  • Exports images.csv, pages.csv and duplicates.csv
. Disclosure menus (APG pattern), hover intent, mobile dialog with focus trap, announcement dismiss, skip-link target, newsletter status message. No dependencies. */ (function () { 'use strict'; var d = document;/* ---------- skip link target: #main, else the page's
, else the first block after the header */ (function () { var mains = d.querySelectorAll('main'); if (mains.length > 1) { // Our post-content
plus another one: drop the landmark from ours. var ours = d.querySelector('main[data-mlfc-main]'); if (ours) { ours.setAttribute('role', 'none'); ours.removeAttribute('id'); } } var skip = d.querySelector('.mlfc-skip'); if (!skip) return; var t = d.getElementById('main'); if (!t || (t.getAttribute('role') === 'none')) { t = d.querySelector('main:not([role="none"]), [role="main"]'); if (!t) { var hd = skip.closest('.wp-block-template-part') || skip.closest('.mlfc-hdw'); t = hd && hd.nextElementSibling; } if (!t) return; if (!t.id) t.id = 'main'; skip.setAttribute('href', '#' + t.id); } if (!t.hasAttribute('tabindex')) t.setAttribute('tabindex', '-1'); skip.addEventListener('click', function () { setTimeout(function () { t.focus({ preventScroll: false }); }, 0); }); })();/* ---------- announcement dismiss */ d.querySelectorAll('[data-mlfc-ann]').forEach(function (bar) { var btn = bar.querySelector('[data-mlfc-ann-close]'); if (!btn) return; btn.addEventListener('click', function () { try { localStorage.setItem(bar.getAttribute('data-mlfc-ann'), '1'); } catch (e) {} var next = d.querySelector('.mlfc-logo'); bar.hidden = true; if (next) next.focus(); }); });/* ---------- disclosure dropdowns */ var items = [].slice.call(d.querySelectorAll('.mlfc-hdw [data-mlfc-dd]')); var fine = window.matchMedia && window.matchMedia('(hover: hover) and (pointer: fine)').matches; function btnOf(it) { return it.querySelector('button[aria-controls]'); } function panelOf(it) { return d.getElementById(btnOf(it).getAttribute('aria-controls')); } function isOpen(it) { return btnOf(it).getAttribute('aria-expanded') === 'true'; } function setOpen(it, open) { var b = btnOf(it), p = panelOf(it); if (!b || !p) return; b.setAttribute('aria-expanded', open ? 'true' : 'false'); p.hidden = !open; if (open) { // keep the panel inside the viewport p.style.left = ''; p.style.right = ''; var r = p.getBoundingClientRect(); if (r.right > window.innerWidth - 12) { p.style.left = 'auto'; p.style.right = '0'; } } } function closeAll(except) { items.forEach(function (it) { if (it !== except && isOpen(it)) setOpen(it, false); }); }items.forEach(function (it) { var b = btnOf(it), tIn, tOut; if (!b) return; b.addEventListener('click', function () { clearTimeout(tIn); clearTimeout(tOut); var open = !isOpen(it); closeAll(it); setOpen(it, open); }); if (fine) { // Hover intent: open after 300 ms, close 400 ms after the pointer leaves trigger + panel. it.addEventListener('mouseenter', function () { clearTimeout(tOut); tIn = setTimeout(function () { closeAll(it); setOpen(it, true); }, 300); }); it.addEventListener('mouseleave', function () { clearTimeout(tIn); tOut = setTimeout(function () { setOpen(it, false); }, 400); }); } it.addEventListener('focusout', function (e) { if (e.relatedTarget && !it.contains(e.relatedTarget)) setOpen(it, false); }); it.addEventListener('keydown', function (e) { if (e.key === 'Escape' && isOpen(it)) { e.stopPropagation(); setOpen(it, false); b.focus(); } }); }); d.addEventListener('click', function (e) { items.forEach(function (it) { if (isOpen(it) && !it.contains(e.target)) setOpen(it, false); }); }); d.addEventListener('keydown', function (e) { if (e.key !== 'Escape') return; items.forEach(function (it) { if (isOpen(it)) { setOpen(it, false); btnOf(it).focus(); } }); });/* ---------- mobile menu dialog */ var menuBtn = d.querySelector('.mlfc-menu-btn'); var dlg = d.getElementById('mlfc-mnav'); if (menuBtn && dlg) { var FOC = 'a[href],button:not([disabled]),input,select,textarea,[tabindex]:not([tabindex="-1"])'; var openMenu = function () { dlg.hidden = false; menuBtn.setAttribute('aria-expanded', 'true'); d.body.classList.add('mlfc-lock'); var c = dlg.querySelector('[data-mlfc-close]'); if (c) c.focus(); }; var closeMenu = function (restore) { if (dlg.hidden) return; dlg.hidden = true; menuBtn.setAttribute('aria-expanded', 'false'); d.body.classList.remove('mlfc-lock'); if (restore !== false) menuBtn.focus(); }; menuBtn.addEventListener('click', function () { dlg.hidden ? openMenu() : closeMenu(); }); dlg.querySelector('[data-mlfc-close]').addEventListener('click', function () { closeMenu(); }); dlg.addEventListener('keydown', function (e) { if (e.key === 'Escape') { e.preventDefault(); closeMenu(); return; } if (e.key !== 'Tab') return; var f = [].filter.call(dlg.querySelectorAll(FOC), function (el) { return el.offsetParent !== null || el === d.activeElement; }); if (!f.length) return; var first = f[0], last = f[f.length - 1]; if (e.shiftKey && d.activeElement === first) { e.preventDefault(); last.focus(); } else if (!e.shiftKey && d.activeElement === last) { e.preventDefault(); first.focus(); } }); // In-page links (e.g. #courses) close the menu first. dlg.addEventListener('click', function (e) { var a = e.target.closest && e.target.closest('a[href^="#"]'); if (a) closeMenu(false); }); var mq = window.matchMedia('(min-width: 1024px)'); var onMq = function () { if (mq.matches) closeMenu(false); }; if (mq.addEventListener) mq.addEventListener('change', onMq); else if (mq.addListener) mq.addListener(onMq); }/* ---------- newsletter: plain POST to Kit in a new tab (same pattern as mlfa2_newsletter()); say so in the live region */ d.querySelectorAll('form[data-mlfc-nl]').forEach(function (form) { var msg = form.querySelector('.mlfc-nl__msg'); form.addEventListener('submit', function () { if (msg) msg.textContent = 'Thanks. Kit opens in a new tab: confirm your subscription there and check your inbox.'; }); }); })();