Bulk Alt Text Generator — Image SEO & Accessibility Metadata
Pricing
from $10.00 / 1,000 image processeds
Bulk Alt Text Generator — Image SEO & Accessibility Metadata
Generate alt text, SEO filenames, captions, titles & keywords for up to 5,000 images per run. WCAG-informed, 20+ languages, structured JSON output. Failed images are never charged. $0.01 per image.
Pricing
from $10.00 / 1,000 image processeds
Rating
0.0
(0)
Developer
My Excel Solutions
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
3 days ago
Last modified
Categories
Share
Generate structured alt text, SEO filenames, captions, titles, and keywords from direct image URLs or a sitemap. This Apify Actor is designed for ecommerce catalogs, content teams, accessibility audits, agencies, and AI agents that need predictable JSON at scale.
The generated alt text is WCAG-informed and can help an accessibility workflow. It does not certify WCAG, European Accessibility Act, or other legal compliance; a human should review important or ambiguous images in their page context.
What you get
Each successful image produces a record like this:
{"sourceUrl": "https://example.com/products/rust-red-backpack.jpg","status": "ok","altText": "Rust-red canvas backpack with front pocket and brown leather straps","seoFilename": "rust-red-canvas-travel-backpack","caption": "A durable rust-red canvas backpack designed for everyday travel and commuting.","title": "Rust-Red Canvas Travel Backpack","keywords": ["canvas backpack","red backpack","travel bag","daypack","commuter bag","leather straps"],"model": "gemini-2.5-flash-lite","tokensUsed": 512}
Failures are explicit and uncharged:
{"sourceUrl": "https://example.com/broken.jpg","status": "failed","failureReason": "not_an_image"}
Before and after
These outputs are captured by the deterministic end-to-end fixture run used in this repository. The same run exercises downloading, image normalization, structured model output, charging, and dataset records.
| Source state | Generated alt text | Generated SEO filename |
|---|---|---|
IMG_1042.jpg, no alt text | Rust-red canvas backpack with front pocket and brown leather straps | rust-red-canvas-travel-backpack |
product-final-2.png, no alt text | White ceramic coffee mug beside roasted coffee beans on a wooden table | white-ceramic-coffee-mug |
Input
Use either imageUrls or sitemapUrl, not both.
{"imageUrls": ["https://example.com/images/product-front.jpg","https://example.com/images/product-side.png"],"context": "Handmade silver jewellery store","language": "en","tone": "ecommerce","fields": ["altText", "seoFilename", "caption", "title", "keywords"]}
imageUrls: up to 5,000 direct HTTP(S) image URLs.sitemapUrl: image sitemap, one-level sitemap index, or page sitemap. Page sitemap mode fetches at most 200 pages and readsog:imagemetadata.context: optional factual brand or product context, limited to 2,000 characters.language: output language code; the Console includes 20 common choices.tone:neutral,ecommerce, oreditorial.fields: any non-empty subset ofaltText,seoFilename,caption,title, andkeywords.
Sitemap mode
For an image sitemap:
{"sitemapUrl": "https://example.com/image-sitemap.xml","context": "Outdoor clothing retailer","language": "de","tone": "ecommerce"}
The Actor extracts <image:image><image:loc> values. For a sitemap index it follows child sitemaps one level deep. If the input is a page sitemap, it fetches up to 200 pages with concurrency four and extracts each page's og:image. URLs are deduplicated and the final image list is capped at 5,000.
Reliability and cost controls
- Downloads are limited to 20 MB and 30 seconds per image.
sharpverifies the payload, applies EXIF orientation, strips metadata, flattens transparency, limits the longest edge to 512 px, and re-encodes JPEG at quality 80 before Gemini receives it.- At most eight images are processed concurrently.
- Gemini calls use a server-side JSON response schema, two retries with backoff for 429/5xx responses, and one corrective retry for invalid model JSON.
- A failed download or model response cannot crash the rest of the run and is never charged as
image-processed. - Every run logs
{images, succeeded, failed, charged, estCogsUsd, ms}. The estimate uses actual Gemini usage token counts. - Prompt context and output length are bounded. The designed worst-case COGS is below $0.001/image, with a hard $0.003/image stop guardrail.
Pricing and free-tier gate
| Event | Price | When it occurs |
|---|---|---|
image-processed | $0.01 | Once per successful image only |
apify-actor-start | $0.001 | Synthetic Actor start event configured in Apify Console |
Failed images are returned with status: "failed" and are not charged as image-processed. The Actor checks the pay-per-event spending limit after each successful dataset push and exits gracefully when the limit is reached.
Up to eight images can already be in flight when a charge-limit or COGS stop is detected. Any remaining completed results in that batch are intentionally withheld and uncharged, although their Gemini cost may already have been incurred; the run summary still counts them as processed.
Runs started by users for whom APIFY_USER_IS_PAYING !== "1" are capped at five images. Every emitted record receives "note": "free tier", and the log explains how many images were omitted.
Run locally
Prerequisites: Node.js 22 and npm.
npm installnpm run checknpm run build
For a completely local run with no Gemini cost, start the fixture image server in one PowerShell window:
npm run fixture:server
Then run the Actor in another window:
$env:MOCK_GEMINI='1'$env:APIFY_USER_IS_PAYING='1'$env:ACTOR_TEST_PAY_PER_EVENT='true'$env:ACTOR_USE_CHARGING_LOG_DATASET='true'npx apify-cli run -i '{"imageUrls":["http://127.0.0.1:3210/backpack.png","http://127.0.0.1:3210/coffee.jpg","http://127.0.0.1:3210/corrupt.jpg"],"context":"Fixture ecommerce store"}'
Local results are saved under storage/datasets/default. Charging-log records are under storage/datasets/charging_log when the charging-log environment variable is enabled.
For a real Gemini run, remove MOCK_GEMINI, set GEMINI_API_KEY in the environment, and keep the API key out of input JSON and source control:
Remove-Item Env:MOCK_GEMINI -ErrorAction SilentlyContinue$env:GEMINI_API_KEY='<your-developer-key>'npx apify-cli run -i '{"imageUrls":["https://example.com/product.jpg"],"context":"Your factual product context"}'
API and MCP/agent use
After deployment, call the Actor with the standard Apify API or client and read the default dataset:
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: process.env.APIFY_TOKEN });const run = await client.actor('YOUR_USERNAME/bulk-alt-text-generator').call({imageUrls: ['https://example.com/product.jpg'],context: 'Minimalist homeware shop',language: 'en',});const { items } = await client.dataset(run.defaultDatasetId).listItems();
For an MCP-capable agent, connect to https://mcp.apify.com, expose this Actor as a tool, and pass the same input object. The hosted Apify MCP server infers structured output fields from the Actor dataset schema, so agents can consume success and failure records directly.
{"mcpServers": {"apify": {"url": "https://mcp.apify.com"}}}
Development
npm run typechecknpm run lintnpm run testnpm run check
The test suite covers large JPEG downscaling, PNG transparency, corrupt/oversized/timed-out downloads, concurrency, recorded Gemini responses and retries, COGS math, image/page/index sitemaps, charging limits, the free-tier gate, and an end-to-end local fixture run.
Limits
- No image generation or editing.
- No OCR or document processing.
- No Shopify CSV preset or asynchronous Gemini Batch API in Phase 1.
- AI output can be wrong; review high-impact accessibility and product content in its rendered page context.