# Alt Text Generator - Batch Image Descriptions (`ntriqpro/alt-text-batch`) Actor

Generate descriptive alt text for hundreds of images at once with AI vision. Output is JSON ready for CMS bulk-update and covers both SEO ranking and WCAG accessibility compliance — built for product catalogs, media libraries and social captions.

- **URL**: https://apify.com/ntriqpro/alt-text-batch.md
- **Developed by:** [Daniel Carter](https://apify.com/ntriqpro) (community)
- **Categories:** AI, Developer tools, Automation
- **Features:** Limited permissions
- **Stats:** 25 total users, 7 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.20 / 1,000 alt text generateds

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Alt Text Batch Generator

Batch-process multiple images to generate AI-powered alt text descriptions for web accessibility using Anthropic Claude vision (Claude Haiku 5.5).

| | Free plan (preview) | Paid Apify plan (Starter and up) |
|---|---|---|
| Images described per run | 1 (the first URL) | every URL you enter (up to 500 a run) |
| The other images | counted in a `free-plan-preview` row: how many were queued, and an estimate (BRONZE price) of a paid run of the same input | described |
| Price | same events | same events, with plan discounts: BRONZE 20% off, SILVER 35% off, GOLD and up 48% off |

A free run ends as SUCCEEDED with a status message that says how many images were left out. Run the same input again on a paid plan for all of them.

### Overview

This Actor processes up to 500 images in a single run and generates alt text descriptions suitable for screen readers and accessibility compliance. Each successfully processed image is charged at $0.01 (pay-per-event pricing).

#### Key Features

- **Batch Processing**: Process up to 500 images per run
- **AI-Powered**: Uses Claude Haiku 5.5 vision through the Anthropic Messages API
- **Flexible Output**: Choose between concise (WCAG-compliant max 125 chars) or detailed (2-3 sentence) descriptions
- **Multi-Language**: Supports 10 languages (English, Korean, Japanese, Chinese, Spanish, French, German, Italian, Portuguese, Russian)
- **Pay-Per-Event**: $0.01 per generated alt text; $0.002 for an image URL whose host refuses it (see Pricing)
- **Error Resilience**: Continues processing even if individual images fail
- **Detailed Logging**: Track processing time and confidence scores per image

### Input

Provide an array of publicly accessible image URLs:

```json
{
  "urls": [
    "https://example.com/image1.jpg",
    "https://example.com/image2.png",
    "https://example.com/image3.webp"
  ],
  "language": "en",
  "style": "concise",
  "maxUrls": 500
}
```

#### Input Parameters

| Parameter | Type | Default | Required | Description |
|-----------|------|---------|----------|-------------|
| `urls` | array | — | ✓ | Array of image URLs (1-500 items) |
| `language` | string | `en` | — | Language code: en, ko, ja, zh, es, fr, de, it, pt, ru |
| `style` | string | `concise` | — | Output style: `concise` (≤125 chars) or `detailed` (2-3 sentences) |
| `maxUrls` | integer | `500` | — | Safety limit on processing count (1-500) |

### Output

Each image produces one dataset record:

```json
{
  "url": "https://example.com/image1.jpg",
  "status": "success",
  "altText": "A fluffy orange tabby cat sitting on a wooden windowsill",
  "detailedDescription": null,
  "language": "en",
  "model": "claude-haiku-5-5",
  "processingTimeMs": 2450,
  "confidence": 0.85,
  "error": null,
  "code": null
}
```

#### Output Fields

| Field | Type | Description |
|-------|------|-------------|
| `url` | string | Original image URL |
| `status` | string | `success` or `error` |
| `altText` | string | Generated alt text (concise style) |
| `detailedDescription` | string|null | Detailed description (detailed style only) |
| `language` | string | Output language code |
| `model` | string | AI model name (claude-haiku-5-5) |
| `processingTimeMs` | integer | Processing time in milliseconds |
| `confidence` | number | AI confidence score (0-1) |
| `error` | string|null | Error message if failed |
| `code` | string|null | Error code: `INVALID_URL`, `TIMEOUT`, `API_ERROR`, etc. |

### Usage Examples

#### Example 1: Basic Batch Processing (English, Concise)

```json
{
  "urls": [
    "https://picsum.photos/id/237/400/300.jpg",
    "https://upload.wikimedia.org/wikipedia/commons/1/18/Dog_Breeds.jpg"
  ],
  "language": "en",
  "style": "concise"
}
```

**Cost**: $0.02 (2 images × $0.01)

#### Example 2: Large Batch with Detailed Descriptions (Korean)

```json
{
  "urls": [
    "https://example.com/image1.jpg",
    "https://example.com/image2.jpg",
    "https://example.com/image3.jpg",
    "...up to 500 images..."
  ],
  "language": "ko",
  "style": "detailed",
  "maxUrls": 500
}
```

**Cost**: $0.03 - $5.00 depending on successes (1-500 images × $0.01)

### Pricing

| Event | Price (FREE tier list price) | When it is charged |
|---|---|---|
| `alt-text-generated` | $0.01 | once per image whose alt text row is delivered |
| `image-checked` | $0.002 | once per image URL that was checked without an alt text: its host answered with an HTTP error, a non-image or an empty body (`code: "IMAGE_UNREACHABLE"`, `errorSide: "target"`, `reason`, `httpStatus`), or the input itself could not be used — an invalid URL (`INVALID_URL`/`INVALID_INPUT`), a file Claude cannot take, or an image Claude refuses (`errorSide: "input"`). Charged only once this event is on the live price list |

**Who is behind a failed URL.** When the URL's own host answers 401, 403, 404 or 410, the check is done and the answer is the host's: `errorSide: "target"` with `reason` (`login-required`, `forbidden`, `not-found`, `gone`) and `httpStatus`, charged as `image-checked`. When it answers 429, 503 or another temporary code, the Actor waits briefly and asks once more; the same kind of answer again is the host's own state: `reason: "temporarily-unavailable"` with the HTTP code, charged the same way. Our side is never charged: our time limit, a host that never answered, errors of the Claude vision service or of our own key and cost cap (`errorSide: "service"`); a file Claude cannot take (not a JPEG/PNG/GIF/WebP image, over 7 MB or 8000 px) is `errorSide: "input"`, charged as `image-checked` like an invalid URL.

If the platform restarts a run, the new run life reads the run's own dataset and the platform's charge counts: inputs already answered are not processed, delivered or charged again. If the dataset cannot be read back, the run stops without processing or charging anything more.

Not charged: `TIMEOUT`, `API_ERROR`, `PROCESSING_ERROR`, `EXCEPTION` rows (`errorSide: "service"` — our side) and notice rows. `INVALID_URL`/`INVALID_INPUT` rows are the input's own answer and are charged as `image-checked`. Plan discounts: BRONZE 20%, SILVER 35%, GOLD and up 48%.

The run stops when its maximum charge is reached: no further image is sent to Claude, a `charge-limit-reached` notice row says how many images were not processed, and the run ends as SUCCEEDED. If our vision service fails 3 images in a row, the run stops with a free `service-unavailable` notice row instead of spending your run time on the rest.

#### Cost Examples

| Images | Success | Cost |
|--------|---------|------|
| 10 | 10 | $0.10 |
| 50 | 48 (2 failed) | $0.48 |
| 100 | 95 (5 failed) | $0.95 |
| 500 | 500 | $5.00 |

### Timeout & Rate Limiting

- **Per-image timeout**: 180 seconds, and never longer than the time the run has left
- **Batch timeout**: the run timeout you set in Run options (default 1 hour). Images take about 5 seconds each, so 500 images need about 40 minutes — set a longer run timeout for big batches
- **Near the run timeout**: the run stops starting new images, keeps every result already delivered, and adds one free `time-budget-reached` notice row listing how many inputs were not processed — re-run with the remaining URLs
- **Rate limit**: 100ms delay between requests (built-in throttling)

### Error Codes

| Code | Meaning | Recovery |
|------|---------|----------|
| `INVALID_INPUT` | Missing/invalid URL parameter | Check URL format |
| `INVALID_URL` | URL not HTTP(S) or malformed | Verify URL is valid and public |
| `TIMEOUT` | Request exceeded the per-image limit (up to 180 seconds) | Image too large or service overloaded |
| `IMAGE_UNREACHABLE` | The image host answered with an error (401/403/404/410, or 429/503 twice — `reason` and `httpStatus` say which), not an image, or empty | Check that the URL opens publicly |
| `API_ERROR` | AI service returned error | Check service status |
| `PROCESSING_ERROR` | Other processing failure | Check image format/size |
| `EXCEPTION` | Unhandled exception | Contact support |

### Legal Notice

#### Image Source Responsibility

- You must only submit images that you own or have explicit permission to analyze.
- You are responsible for complying with the source website's Terms of Service and robots.txt.
- Do not use this tool to analyze images from stock photo sites (Getty, Shutterstock) without a valid license.

#### Copyright

- Generated alt text descriptions are not derivative works of the original images.
- However, fetching images from third-party URLs may violate the source site's Terms of Service.
- We assume no liability for third-party intellectual property claims arising from your use.

#### WCAG Compliance

- AI-generated alt text is a starting point, NOT a guarantee of WCAG 2.1 or ADA compliance.
- All generated descriptions must be reviewed by a human before production use.
- This tool assists accessibility efforts but does not replace professional accessibility auditing.

#### AI Limitations

- Descriptions may contain inaccuracies or miss important context.
- Specialized images (medical, technical, charts) require professional review.

#### Data Processing

- Images are fetched temporarily by the Actor and sent to Anthropic (United States) for analysis through the Anthropic API. Do not submit images you are not allowed to send to a third-party AI provider.
- Anthropic is the AI provider and processes the image under its own API terms. This Actor is not affiliated with or endorsed by Anthropic.
- No images are stored, cached, or redistributed by the Actor.

### Technical Details

#### AI Service Integration

- **Model**: Claude Haiku 5.5 (`claude-haiku-5-5`) via the Anthropic Messages API (base64 image block)
- **Request Timeout**: up to 180 seconds per image
- **Supported Formats**: JPEG, PNG, WebP, GIF (the type is read from the file, not from the host)
- **Max Image Size**: 7 MB and 8000 x 8000 px; larger images are answered with an `errorSide: "input"` error row charged as `image-checked`. Claude downscales big images itself, so the cost of one image is bounded

#### Environment Variables (Optional)

```bash
AI_REQUEST_TIMEOUT=180000
ANTHROPIC_API_KEY=...   # the Actor owner's key; without it every image is an uncharged error row
```

The model cost of one run is capped (US$2). The cap protects the owner, not the customer: customers pay only the events above.

#### Processing Pipeline

1. Validate input array and language/style parameters
2. For each URL:
   - Validate URL format
   - Fetch the image from its own host, then ask Claude vision with an optimized prompt
   - Collect response (alt text or error)
   - Push to dataset
   - Charge on success (or `image-checked` when the host refused the URL or the input could not be used); stop at the run's maximum charge
3. Log summary (success/error counts, total cost)

### Limitations

| Limitation | Value |
|-----------|-------|
| Max images per run | 500 on paid plans, 1 on the free plan |
| Timeout per image | 180 seconds (shorter when the run is nearly out of time) |
| Total batch timeout | Your run timeout (Apify default 1 hour) |
| Concurrent requests | Sequential (100ms between) |
| Supported formats | JPEG, PNG, WebP, GIF |

### Support

For issues or questions:

1. Check error codes and logs in run output
2. Verify all image URLs are publicly accessible
3. Confirm language code is supported
4. Review AI service status

### Version History

- **v1.0.0** (2026-03-30): Initial release
  - Batch processing (up to 500 images)
  - Concise and detailed styles
  - Multi-language support
  - Pay-per-event billing

***

**Last Updated**: 2026-03-30
**Maintainer**: ntriq Engineering
**License**: Apache 2.0

***

### 🔗 Related Actors by ntriqpro

Build your data pipeline with the ntriqpro Actor suite:

- [**image-batch-optimizer**](https://apify.com/ntriqpro/image-batch-optimizer) — Image Compressor — TinyPNG Alternative
- [**image-upscale-mcp**](https://apify.com/ntriqpro/image-upscale-mcp) — AI Image Upscaler 2x/4x
- [**vehicle-damage-assessment**](https://apify.com/ntriqpro/vehicle-damage-assessment) — AI Vehicle Damage Inspector

### ⭐ Love it? Leave a Review

Your rating helps other professionals discover this actor. [Rate it here](https://apify.com/ntriqpro/alt-text-batch/reviews).

# Actor input Schema

## `urls` (type: `array`):

Array of publicly accessible image URLs to process (max 500). Free plan: the first image is described and the rest are counted in a free-plan-preview row; paid Apify plans describe every image. Billing: alt-text-generated per alt text, image-checked per URL whose host answers with an error (401/403/404/410, or 429/503 again after one retry) or a non-image. Our side (our time limit, no answer, errors of the Claude vision service, a file Claude cannot take) is not charged.

## `language` (type: `string`):

Language code for alt text generation (en, ko, ja, zh, etc.)

## `style` (type: `string`):

Type of alt text to generate: concise (max 125 chars) or detailed (2-3 sentences)

## `maxUrls` (type: `integer`):

Safety limit for number of URLs to process (prevents accidental overconsumption)

## Actor input object example

```json
{
  "urls": [
    "https://picsum.photos/id/237/400/300.jpg"
  ],
  "language": "en",
  "style": "concise",
  "maxUrls": 500
}
```

# Actor output Schema

## `dataset` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "urls": [
        "https://picsum.photos/id/237/400/300.jpg"
    ],
    "language": "en",
    "style": "concise"
};

// Run the Actor and wait for it to finish
const run = await client.actor("ntriqpro/alt-text-batch").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "urls": ["https://picsum.photos/id/237/400/300.jpg"],
    "language": "en",
    "style": "concise",
}

# Run the Actor and wait for it to finish
run = client.actor("ntriqpro/alt-text-batch").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "urls": [
    "https://picsum.photos/id/237/400/300.jpg"
  ],
  "language": "en",
  "style": "concise"
}' |
apify call ntriqpro/alt-text-batch --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,ntriqpro/alt-text-batch"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/eFcJRSfLVupVJuSFh/builds/OSgg7qpLMSYJtVcjt/openapi.json
