← Back to Blog
Image SEO

Bulk Alt Text Generation: A Practical Workflow for Hundreds of Images

Generating alt texts one by one does not scale. Here is a step-by-step workflow for processing hundreds or thousands of images — with quality control, language handling, and CMS integration.

Most alt text tutorials assume you have ten images to optimise. Real projects look different: a portfolio with 800 photos, an e-commerce catalogue with 12,000 SKUs, a media archive that grows by 200 images per week.

At that scale, manual alt text writing is not an option — and naive automation produces unusable output. This guide describes a workflow that actually works in production, covering image preparation, batch processing, quality control, and writing the results back into your CMS.

Why Bulk Workflows Fail

Teams that try to scale alt text writing usually hit one of three walls. The first is sheer volume: even a fast writer needs 30–60 seconds per image to produce a decent alt text. At 5,000 images, that is over 40 hours of focused work. The second is inconsistency — different writers produce different styles, languages, and keyword density. The third is staleness: by the time the batch is finished, hundreds of new images have been added.

A working bulk workflow has to solve all three problems at once. It needs to be fast, consistent, and continuous.

Step 1: Inventory Your Images

Before you process anything, you need a list. Export every image URL from your CMS or database into a single source — a CSV, a database query result, or an API endpoint. Each row should contain at minimum the image URL, a unique ID, and the target field where the alt text will be written back.

Bad Random folder scan with no IDs — you cannot map results back later
Good image_id, url, current_alt, target_language, context

The context column matters more than people expect. An image of a leather bag in a fashion shop needs different language than the same image in a luggage catalogue. Passing a short context hint ("luxury handbag product photography") dramatically improves keyword relevance.

Step 2: Filter Out What You Do Not Need to Process

Not every image deserves an alt text. Skip these categories before sending anything to an API:

  • Decorative images — backgrounds, dividers, gradients. These should have alt="", not generated descriptions.
  • Icons and UI elements — already covered by ARIA labels or visible text.
  • Duplicate uploads — same image hash, different URL. Process once, copy the result.
  • Images with existing high-quality alt text — do not overwrite human work without review.

On a typical e-commerce site, this filter cuts the input by 30–50%. That is money saved on API calls and editor time saved on review.

Step 3: Batch Submission with Backpressure

Sending 5,000 requests in a tight loop will get you rate-limited or throttled. The pattern that works in production is asynchronous submission with a worker queue and webhook callback.

// Laravel queue job — one image per jobclass AnalyzeImageJob implements ShouldQueue{    public function __construct(        public int $imageId,        public string $imageUrl,        public string $context,    ) {}    public function handle(): void    {        Http::withToken(config('lucidseo.api_key'))            ->post('https://lucidseo.net/api/photos/analyze', [                'url' => $this->imageUrl,                'webhook_url' => route('webhook.lucidseo'),                'context' => $this->context,                'metadata' => ['image_id' => $this->imageId],            ]);    }}

Dispatch one job per image, let the queue control the throughput (e.g. 10 concurrent workers), and let the webhook handler write results back. This pattern is resilient to failures — a single dead image does not stop the batch — and it gives you natural backpressure.

Step 4: Webhook Handler That Writes Results Back

The webhook receives the analysis result. Verify the signature, look up your local image record via the ID you passed in the metadata, and update the alt text, description, and caption fields atomically.

Route::post('/webhook/lucidseo', function (Request $request) {    if (!verifySignature($request)) {        abort(401);    }    $imageId = $request->input('metadata.image_id');    Image::where('id', $imageId)->update([        'alt_text' => $request->input('alt_text'),        'description' => $request->input('description'),        'caption' => $request->input('caption'),        'keywords' => $request->input('keywords'),        'metadata_generated_at' => now(),    ]);})->name('webhook.lucidseo');

Store a metadata_generated_at timestamp. This lets you re-run only images that have not been processed yet — essential when you resume a failed batch or add new uploads.

Step 5: Quality Control Sampling

Never publish thousands of auto-generated alt texts blindly. The right pattern is statistical sampling: pull a random 2–5% of generated outputs into a review queue and have a human spot-check them.

  • Coverage — does the alt text describe what is actually in the image?
  • Length — is it under 120 characters?
  • Language — is the output in the requested target language?
  • Keyword density — natural, not stuffed?

If your sample shows fewer than 95% acceptable outputs, do not publish the batch. Refine the context hints, adjust the language setting, and re-run.

Step 6: Continuous Processing for New Uploads

Once the historical backlog is cleared, the workflow has to run continuously for new images. The cleanest pattern is an Eloquent model observer that dispatches the analysis job whenever an image is created.

class ImageObserver{    public function created(Image $image): void    {        if ($image->alt_text === null) {            AnalyzeImageJob::dispatch(                $image->id,                $image->original_url,                $image->category?->context_hint ?? '',            );        }    }}

This is the moment the workflow stops being a one-off migration and becomes infrastructure. From here on, every new image gets metadata within seconds of upload.

Multilingual Workflows

If your site runs in multiple languages, do not translate alt texts after the fact — generate them per language directly. Translation tools lose keyword relevance because the keyword in German (e.g. "Hochzeitsfotograf München") is not a literal translation of the English one. A bulk workflow should branch per locale at the job dispatch stage, sending one analysis request per (image, language) pair.

Cost and Throughput Planning

At LucidSEO's pricing, processing 1,500 images per month on the Pro plan costs 29 €. A one-time backlog of 10,000 images costs around 340 € on overage. Throughput is typically 2–4 seconds per image — meaning 10,000 images complete in under 10 hours with 10 concurrent workers.

For most teams, the bottleneck is not the API. It is the review step. Plan for one editor-hour per 500 reviewed images and you will not be surprised.

How LucidSEO Fits This Workflow

LucidSEO's image analysis API is built around exactly this pattern — async submission, webhook callback, structured JSON response, multi-language support, and metadata pass-through so you can correlate results to your own image IDs without keeping state on our side.

{  "alt_text": "Brown leather handbag with gold clasp on white background",  "description": "Elegant brown leather handbag with polished gold clasp — handcrafted in Italy",  "caption": "Italian leather, timeless design",  "keywords": ["leather handbag", "luxury accessories", "Italian craftsmanship", "gold clasp", "product photography"]}

Getting Started

The free plan includes 50 analyses per month — enough to validate the workflow end-to-end on a small subset before committing to a full backlog 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. Export your image inventory into a single source with unique IDs and context hints.
  2. Filter out decorative images, icons, and duplicates before processing.
  3. Use a queue-based async submission pattern with webhook callbacks — never tight loops.
  4. Sample 2–5% of outputs for human quality review before publishing batches.
  5. Set up a model observer for continuous processing of new uploads.