← Back to Blog
Image SEO

How to Add AI-Generated Alt Text to WooCommerce Products Automatically

WooCommerce stores built from bulk product imports almost always have empty image alt text. Here is how to automate it end-to-end — finding the gaps, hooking new uploads, and writing SEO-ready descriptions back to every product without a single manual edit.

WooCommerce gives every image an alt text field — the same _wp_attachment_image_alt meta WordPress uses everywhere. Most stores leave it empty, because the products were imported in bulk from a CSV and the importer never touched it.

The result is a catalogue of product photos that are invisible to Google Images and to screen readers alike. This guide covers the full automation path on WordPress: finding the images that need alt text, hooking into new uploads, generating descriptions with product context, and writing the results back through a webhook — without a single manual edit.

Where WooCommerce Keeps Your Alt Text

There is no special WooCommerce alt text storage. Product images are ordinary WordPress attachments, and their alt text lives in a single post meta key on the attachment: _wp_attachment_image_alt. Your theme reads it when it renders wp_get_attachment_image(), and Google reads it from the rendered <img alt="...">.

This matters for automation: you never edit the product to fix alt text. You update the meta on the attachment. Every script in this guide targets that one meta key.

Why Manual Alt Text Breaks for a Catalogue

Stores that try to write alt text by hand hit three walls. The first is volume: a shop with 800 products and a four-image gallery each is 3,200 images, and a careful writer needs roughly a minute per image — over 50 hours of work. The second is the import problem: WooCommerce CSV imports and migration plugins create attachments with no alt text at all, so the gap reappears with every bulk update. The third is variation images — each colour or size variant carries its own photo, multiplying the count again.

A working solution has to find the gaps, fill them in the background, and keep doing it for every new upload.

Step 1: Find Product Images Missing Alt Text

Before processing anything, get the list. Alt text is empty when the attachment has no _wp_attachment_image_alt meta, or the value is blank. A single SQL query returns every candidate:

SELECT p.ID, p.guid
FROM wp_posts p
LEFT JOIN wp_postmeta m
  ON m.post_id = p.ID
  AND m.meta_key = '_wp_attachment_image_alt'
WHERE p.post_type = 'attachment'
  AND p.post_mime_type LIKE 'image/%'
  AND (m.meta_value IS NULL OR m.meta_value = '');
Bad Scanning the uploads folder on disk — you lose the attachment ID and cannot write results back
Good Query by attachment post type and empty alt meta — every row maps back to one attachment ID

Step 2: Process New Uploads Automatically

The cleanest hook for ongoing coverage is add_attachment, which fires the moment any image enters the media library — including images added by the product importer. Queue an analysis job instead of calling the API inline, so a slow response never blocks an upload.

add_action('add_attachment', function (int $attachment_id) {
    if (! wp_attachment_is_image($attachment_id)) {
        return;
    }

    // Never overwrite an alt text a human already wrote
    $existing = get_post_meta($attachment_id, '_wp_attachment_image_alt', true);
    if (! empty($existing)) {
        return;
    }

    as_enqueue_async_action('lucidseo_generate_alt', [$attachment_id]);
});

as_enqueue_async_action() is part of Action Scheduler, which ships with WooCommerce — so you already have a battle-tested background queue without installing anything.

Step 3: Call the API with Product Context

The biggest quality lever is the context you send. An image analysed on its own becomes "brown leather bag on white background"; the same image with the product title as context becomes "Saddle brown leather messenger bag with brass buckles". Pull the context from the parent product before sending.

add_action('lucidseo_generate_alt', function (int $attachment_id) {
    $image_url  = wp_get_attachment_url($attachment_id);
    $product_id = lucidseo_parent_product_id($attachment_id); // featured or gallery owner
    $context    = $product_id
        ? get_the_title($product_id) . ' product photography'
        : '';

    wp_remote_post('https://lucidseo.net/api/photos/analyze', [
        'headers' => [
            'Authorization' => 'Bearer ' . LUCIDSEO_API_KEY,
            'Content-Type'  => 'application/json',
        ],
        'body' => wp_json_encode([
            'url'         => $image_url,
            'context'     => $context,
            'webhook_url' => rest_url('lucidseo/v1/webhook'),
            'metadata'    => ['attachment_id' => $attachment_id],
        ]),
        'timeout' => 15,
    ]);
});

The metadata field is passed straight back in the webhook, so you can map the result to the right attachment without storing any extra state on your side.

Step 4: Write the Alt Text Back via a Webhook

Register a REST route to receive the completed analysis. Verify the signature, read the attachment ID you passed in the metadata, and update the one meta key that matters.

add_action('rest_api_init', function () {
    register_rest_route('lucidseo/v1', '/webhook', [
        'methods'             => 'POST',
        'callback'            => 'lucidseo_handle_webhook',
        'permission_callback' => '__return_true', // signature is verified inside
    ]);
});

function lucidseo_handle_webhook(WP_REST_Request $request) {
    if (! lucidseo_verify_signature($request)) {
        return new WP_REST_Response(['error' => 'invalid signature'], 401);
    }

    $attachment_id = (int) $request['metadata']['attachment_id'];
    $alt           = sanitize_text_field($request['alt_text']);

    update_post_meta($attachment_id, '_wp_attachment_image_alt', $alt);

    return new WP_REST_Response(['ok' => true], 200);
}

Because you only ever update _wp_attachment_image_alt, the change is picked up everywhere the image appears — product pages, category grids, and the gallery lightbox — without touching the products themselves.

Step 5: Backfill the Existing Catalogue

For the historical backlog, feed the IDs from Step 1 into the same queue. Action Scheduler paces the throughput, so you will not flood the API or your own server.

// Run once from WP-CLI: wp eval-file backfill-alt.php
$missing = lucidseo_images_without_alt(); // the SELECT from Step 1, as an array of IDs

foreach ($missing as $attachment_id) {
    as_enqueue_async_action('lucidseo_generate_alt', [$attachment_id]);
}

Tune the concurrency in WooCommerce → Status → Scheduled Actions or via the action_scheduler_queue_runner_concurrent_batches filter. Ten concurrent batches clears 10,000 images comfortably within a workday.

Step 6: Quality Control Before You Trust It

Never publish thousands of generated alt texts blind. Pull a random 2–5% sample into a review screen and spot-check four things:

  • Coverage — does it describe what is actually in the photo, not just repeat the product name?
  • Length — under 125 characters, so screen readers and Google both keep it whole.
  • Language — output in the locale your customers actually search in.
  • Keywords — natural phrasing, not stuffed.

If fewer than 95% of the sample is acceptable, refine the context hints and re-run rather than publishing the batch.

Multilingual Stores (WPML / Polylang)

If your shop runs in several languages with WPML or Polylang, do not translate the alt text after the fact — generate it per language. The German keyword ("Lederrucksack Herren") is not a literal translation of the English one, and a translated alt text loses the search relevance that made it worth generating. Branch per locale at the job stage and send one analysis request per image-and-language pair, writing each result to the matching translated attachment.

Common Pitfalls

  • Updating the product instead of the attachment — alt text is attachment meta, never a product field. Editing the product changes nothing.
  • Overwriting human work — always check for an existing alt text before queuing, and keep the previous value in a log so a bad batch is reversible.
  • Inline API calls on upload — calling the API directly inside add_attachment blocks the editor while the image is analysed. Always queue.
  • Forgetting variation images — variant photos are separate attachments. A query filtered to featured images only will silently miss them.

Cost Estimate for a Typical Shop

A WooCommerce store with 500 products and four images each holds 2,000 images. After filtering out the ones that already have decent alt text (~30%), you process around 1,400 — which fits inside a single month on LucidSEO's Pro plan. The whole migration costs less than one hour of a copywriter's time, and the ongoing sync is effectively free at typical upload volumes.

How LucidSEO Fits WooCommerce Workflows

LucidSEO's image analysis API is built for exactly this pattern — context-aware analysis, webhook callbacks with metadata pass-through so you can map results straight to attachment IDs, and multi-language output for international stores.

{
  "alt_text": "Saddle brown leather messenger bag with brass buckles",
  "description": "Handcrafted saddle brown leather messenger bag with adjustable strap and brass hardware",
  "caption": "Built for the daily commute",
  "keywords": ["leather messenger bag", "saddle brown", "brass buckles", "commuter bag", "handcrafted leather"]
}

Getting Started

The free plan includes 50 analyses per month — enough to run the full integration on a single product category before committing to a full catalogue migration.

LucidSEO Image Analysis API

Upload a photo, get back alt text, description, caption and keywords in seconds — fully automated via webhook.

  • WordPress, Webflow, custom CMS — integrates in minutes
  • 12+ languages — metadata in the language your clients search in
  • Free plan — 50 analyses/month, no credit card required
Start for free →

Action List

  1. Query attachments with empty _wp_attachment_image_alt to find every gap.
  2. Hook add_attachment and queue work with Action Scheduler — never call the API inline.
  3. Pass the parent product title as context — it is the single biggest quality lever.
  4. Write results back to the attachment meta from a verified webhook route.
  5. Backfill the catalogue once, then let the upload hook keep it current.