# AI Translator - Bulk Translate Text, JSON, Subtitles & Pages (`tidytools/web-page-translator`) Actor

Translate texts, JSON and datasets, SRT/VTT subtitles and web pages into many languages at once. Glossary, tone, Markdown kept. A Google Translate / DeepL alternative, no API key. $1.50/1M chars.

- **URL**: https://apify.com/tidytools/web-page-translator.md
- **Developed by:** [Yukai Lin](https://apify.com/tidytools) (community)
- **Categories:** AI, Automation, Developer tools
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.20 / 1,000 1,000 source characters

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
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

### What does AI Translator do?

**Bulk AI translator for texts, JSON, datasets, SRT/VTT subtitles and web pages: several languages at once, glossary, Markdown and timestamps kept. $1.50 per 1M characters, no API key.**

**Input:** texts, JSON items, a dataset, SRT/VTT subtitles or page URLs, plus a target language. **Output:** one row per text, page, subtitle file or language (JSON items keep their shape with `<field>_<language>` added). **Price:** $0.0015 per 1,000 source characters ($1.50 per million), failed rows are free.

It works as a **JSON translator** (translate JSON fields and keep the structure), a **subtitle translator** and **SRT translator** (every timestamp kept), a **dataset translation** step for another Actor's results, and a web page translator that keeps the **Markdown structure intact**: headings, lists, tables, bold/italic, links (targets unchanged) and code blocks (not translated). Use it to **bulk translate** thousands of strings as a Google Translate / DeepL alternative.

- 🌍 **Any target language, several at once**: Spanish, German, Japanese, Brazilian Portuguese, Traditional Chinese (Taiwan)… in one run
- 🎬 **SRT and VTT subtitles**: translated cue by cue in batches, every timestamp kept, new subtitle files ready to download. Works directly with the subtitles of our [Audio & Video Transcriber](https://apify.com/tidytools/audio-transcriber)
- 🧾 **JSON and dataset translation**: translate chosen fields of products, reviews or listings; the structure is kept and `title_spanish`, `description_japanese`… are added next to the originals
- 📖 **Glossary and tone**: keep brand and product names exactly as written; pick neutral, formal, casual, marketing or technical wording
- 🧱 **Structure preserved**: in our tests, all headings and links of a documentation page came back intact
- 🔗 **Links and code stay working**: URLs, inline code and code blocks are not translated
- 🛡️ **Checks the AI's work**: if the model writes new copy instead of translating a short string, the string is translated again without the tone, then with the other engine
- ⚙️ **Two engines, same price**: *Fast* (Llama 4 Scout, the default; recommended for Chinese, Japanese and Korean) and *Quality* (Llama 3.3 70B)
- 💸 **$1.50 per 1 million characters, billed on the real total**: 10,000 product titles of 50 characters cost about **$0.75**

### How much does it cost?

| Event | Price |
|---|---|
| 1,000 source characters | **$0.0015** ($1.50 per million characters) |

**No start fee.** Characters are **added up over the whole run** and billed per full 1,000; the last partial 1,000 is billed once at the end. So 10,000 texts of 50 characters (500,000 characters) cost 500 units = **$0.75**, not one unit per text. In our test, 120 product titles (5,668 characters) were billed as 6 units. Each target language counts separately; in JSON mode, a string repeated in many items is translated and billed once. Pages that cannot be read are free. Higher Apify plans get volume discounts.

For comparison: Google Cloud Translation's standard price is $20 per million characters (checked September 2026), and the most-used translation Actor on Apify Store charges $0.02 per item (checked September 2026), which is $200 for the 10,000 product titles above.

#### Control your cost

- **What is charged**: source characters of every translation that was delivered, added up over the run, per 1,000 characters and per target language.
- **What is free**: lines that are not a URL, duplicate URLs, blank texts, pages that cannot be read (unknown domain, bad SSL certificate, private addresses, 404), the same page given twice under two spellings (such as `example.com` and `example.com/`: translated and billed once, the second is a free `duplicate` row), pages and subtitle files the site's `robots.txt` disallows, login walls, bot checks and cookie-consent pages (not translated), translations that fail, and repeated strings in JSON mode after their first translation. Every page, text and subtitle row has `charged: true/false`, and failed rows say "(not charged)".
- **Before the run starts**, the log and the status message show the plan for your texts (their characters are known); pages, subtitles and JSON items are counted as they are read. The plan is saved as `costPlan` in `SUMMARY`.
- **Long pages**: set *Max characters per page* (`maxCharactersPerPage`) to translate only the first part of each page; the row then has `contentTruncated: true` and `pageCharacters` (the full length). A long Wikipedia article can have 146,000 characters, about $0.22 per target language.
- **Max charge per run**: set *Maximum cost per run* in the run options (or `maxTotalChargeUsd` in the API). A translation is only started when the limit still covers it; when the limit is reached the run stops cleanly: `SUMMARY.status` is `LIMIT_REACHED`, `SUMMARY.notProcessed` lists the texts and pages that were not started, and `jsonItemsNotProcessed` counts JSON items that were not reached.
- **If Apify restarts the run** (server migration or Resurrect), texts, pages and subtitle files already translated (per language) and JSON batches already written are skipped, and characters already billed are not billed again (`SUMMARY.resumedSkipped`). JSON strings that also appeared in batches written before the restart are translated again for the later batches.

### How to use it

1. Add what to translate: **URLs**, **texts**, **subtitle files** (SRT/VTT links or pasted text), **JSON items**, or a **dataset ID** from another run.
2. Type the **target language**, and optionally **more target languages**.
3. Optional: **glossary** (terms never to translate), **tone**, **source language**, **engine**, and a **content selector** (e.g. `main`) for pages.
4. Click **Start**. Pages and texts give one row per language with `translatedMarkdown` (texts also get `translatedText`, the original as `sourceText` and its position as `index`); JSON items keep their shape with translated fields added. Page URLs go one per line; a line that is not a URL becomes one free `invalid_input` row.

### Three ready-to-run examples

Copy one of these into the JSON input tab and click **Start**.

#### 1. JSON translator: product titles into Spanish, German and Japanese

```json
{
    "items": [
        { "sku": "TR-01", "title": "Trail running shoes, waterproof" },
        { "sku": "SK-02", "title": "Merino wool socks, 3-pack" }
    ],
    "fields": ["title"],
    "targetLanguage": "Spanish",
    "targetLanguages": ["German", "Japanese"]
}
```

Each item keeps `sku` and `title` and gets `title_spanish`, `title_german` and `title_japanese` next to the original.

#### 2. SRT translator: subtitles into French

```json
{
    "subtitles": "1
00:00:01,000 --> 00:00:03,500
Welcome back to the show.

2
00:00:03,600 --> 00:00:06,000
Today we talk about remote work.
",
    "targetLanguage": "French"
}
```

The row has `translatedSubtitle` (the French SRT, same timestamps) and `translatedSubtitleUrl` (the file to download). For files, put their links in `subtitleUrls` instead.

#### 3. Dataset translation: translate another scraper's results

```json
{
    "datasetId": "YOUR_DATASET_ID",
    "fields": ["title", "reviewText"],
    "maxItems": 500,
    "targetLanguage": "English"
}
```

Use the dataset ID of any finished run (reviews, products, listings). Rows keep their shape with `title_english` and `reviewText_english` added; `maxItems` caps the cost while you test.

#### Input example: texts into two languages

```json
{
    "texts": ["Free shipping on orders over $50", "Add to cart"],
    "targetLanguage": "Spanish",
    "targetLanguages": ["Japanese"]
}
```

Real results:

```json
[
    { "source": "text-0", "success": true, "targetLanguage": "Spanish", "sourceCharacters": 32, "translatedMarkdown": "Envío gratuito en pedidos superiores a $50" },
    { "source": "text-0", "success": true, "targetLanguage": "Japanese", "sourceCharacters": 32, "translatedMarkdown": "$50を超える注文には無料配送" },
    { "source": "text-1", "success": true, "targetLanguage": "Spanish", "sourceCharacters": 11, "translatedMarkdown": "Agregar al carrito" },
    { "source": "text-1", "success": true, "targetLanguage": "Japanese", "sourceCharacters": 11, "translatedMarkdown": "カートに追加" }
]
```

#### Input example: JSON items with a glossary

```json
{
    "items": [
        { "id": "p1", "url": "https://shop.example/p1", "title": "Apify Trail Runner shoes", "description": "Lightweight running shoes with a grippy sole. Made with recycled materials.", "price": 89, "currency": "USD" },
        { "id": "p2", "url": "https://shop.example/p2", "title": "Merino wool socks", "description": "Soft, warm socks that do not itch.", "price": 12, "currency": "USD" }
    ],
    "targetLanguage": "Spanish",
    "targetLanguages": ["Japanese"],
    "glossary": ["Apify"],
    "tone": "marketing"
}
```

With *Fields to translate* empty, `title` and `description` were detected automatically (`id`, `url`, `price` and `currency` were left alone). Real result:

```json
{
    "id": "p1",
    "url": "https://shop.example/p1",
    "title": "Apify Trail Runner shoes",
    "description": "Lightweight running shoes with a grippy sole. Made with recycled materials.",
    "price": 89,
    "currency": "USD",
    "title_japanese": "Apify Trail Runner シューズ",
    "title_spanish": "Zapatillas de running Apify Trail Runner",
    "description_japanese": "グリップ力のあるソールを備えた軽量のランニングシューズ。リサイクル素材を使用しています。",
    "description_spanish": "Zapatillas de running ligeras con suela adherente. Fabricadas con materiales reciclados."
}
```

#### Translate another Actor's dataset

Set **Dataset ID** to the dataset of a run (for example a review or product scraper) and list the **Fields to translate** (dot paths such as `data.title` work for nested fields). Real result for a Hacker News dataset with `"fields": ["data.title"]` and `"targetLanguage": "Traditional Chinese"`:

```json
{ "url": "https://news.ycombinator.com/news?p=2", "itemIndex": 2, "data": { "num_comments": 21, "points": 43, "site_domain": "smithsonianmag.com", "title": "Who wrote Elizabeth I's most scathing letters?", "title_traditional_chinese": "誰寫了伊麗莎白一世最尖銳的信件？" } }
```

Large datasets are translated **1,000 items at a time**: each batch is written to the dataset as soon as it is done (so a stopped or timed-out run keeps the finished batches), and one run-wide list of unique strings makes sure a string repeated in later batches is translated and billed only once. Real result: 2,300 reviews made of 5 different sentences → 3 batches, 2,300 rows, 1 unit billed.

#### Subtitles (SRT / VTT)

```json
{
    "subtitleUrls": ["https://api.apify.com/v2/key-value-stores/.../records/0000-changelog-news-183.mp3.srt"],
    "targetLanguages": ["Traditional Chinese", "Spanish"]
}
```

Each subtitle file is translated into each language. The cues are sent in batches of up to 40, so a sentence that runs over two cues is translated with its context, and each translation goes back into its own cue. Up to 3 batches are translated at the same time (a 17-minute podcast's subtitles took about a minute per language in our test, 2 to 3 times faster than before). Timing lines are copied exactly; `Speaker 1:` labels and VTT voice tags (`<v Speaker 1>`) are kept; two-line cues are wrapped into two lines again; dialogue cues (`- Hi.` / `- Hello.`) keep one line per speaker. You can also paste one file's content into **Subtitle text**.

Real result: a 5-minute podcast transcript from our Audio & Video Transcriber (59 cues, 4,230 characters) into Traditional Chinese took 61 seconds and was billed as 5 units ($0.0075):

```json
{
    "source": "subtitle",
    "url": "https://api.apify.com/v2/key-value-stores/.../records/0000-changelog-news-183.mp3.srt",
    "format": "srt",
    "targetLanguage": "Traditional Chinese",
    "success": true,
    "cues": 59,
    "cuesTranslated": 59,
    "sourceCharacters": 4230,
    "translatedSubtitleUrl": "https://api.apify.com/v2/key-value-stores/.../records/0000-changelog-news-183.mp3-traditional_chinese.srt",
    "translatedSubtitle": "1\n00:00:04,160 --> 00:00:07,520\nSpeaker 1: 大家好，我是 Adam。ChangeLog 這裡迎來了一個大的一周。\n\n2\n00:00:07,520 --> 00:00:16,640\n各地都有一些大變化。..."
}
```

**Audio or video → subtitles in any language:** run the [Audio & Video Transcriber](https://apify.com/tidytools/audio-transcriber) (it saves SRT and VTT files for every recording, optionally with speaker labels), then pass its `srtUrl` or `vttUrl` values here as `subtitleUrls`. Only the characters of the subtitle text are billed, not the timing lines.

#### Web pages

```json
{
    "urlsText": "https://docs.apify.com/academy/web-scraping-for-beginners",
    "texts": ["Welcome to **Tidy Tools**. See [our store](https://apify.com/tidytools)."],
    "targetLanguage": "Traditional Chinese (Taiwan)"
}
```

A real result for the text above: `"歡迎使用 **Tidy Tools**。請參閱 [我們的商店](https://apify.com/tidytools)。"`: bold text and the link target are kept. Each page is read once and translated into every language you listed.

### Output fields

| Field | Meaning |
|---|---|
| `translatedMarkdown` | Pages and texts: the translation |
| `translatedText`, `sourceText`, `index` | Texts: the translation (same as `translatedMarkdown`), the original text (up to 2,000 characters) and its position in your list |
| `input`, `inputIndex` | Pages: the URL as you entered it and its position in your list |
| `contentTruncated`, `pageCharacters` | Pages: `true` when only the first *Max characters per page* were translated, and the page's full length |
| `charged` | Pages, texts and subtitles: whether this row was charged |
| `translatedSubtitleUrl`, `translatedSubtitle` | Subtitles: link to the translated SRT/VTT file in the run's key-value store, and its content (up to 200,000 characters) |
| `cues`, `cuesTranslated`, `format` | Subtitles: number of cues, cues translated (cues without letters, such as "♪", are kept as they are), `srt` or `vtt` |
| `targetLanguage`, `sourceCharacters` | Language of this row and the characters billed for it |
| `<field>_<language>` | JSON items: translation added next to each source field, e.g. `title_spanish`, `name_portuguese_brazil` |
| `translationErrors` | JSON items: fields that could not be translated (not charged) |
| `placeholderMismatch`, `placeholderIssues` | After translating, `{name}`, `{{name}}`, `%s` / `%1$s`, `%(name)d`, URLs, HTML tags and Markdown link targets are compared with the source. A string or page block where they differ is translated once more (at no extra charge) and the better result kept. If they still differ, `placeholderMismatch` is `true` and `placeholderIssues` lists up to 10 differences (`kind`, `missing`, `extra`; subtitles add `cue`, JSON items add `field` and `language`) so you can check those rows. Pages, texts and subtitles always have `placeholderMismatch`; JSON items only when `true` |
| `via` | Pages: fetched by `backend` (our servers), `direct` (Apify's network) or `browser` |
| `error`, `errorType` | For items that failed (not charged), e.g. `blocked` (the site refuses automated visitors: HTTP 401/403 or a bot check), `robots_disallowed` (the site's robots.txt disallows the page for TidyTools; not read), `not_found`, `unreachable` (unknown domain, server did not answer, or invalid SSL certificate), `duplicate` (the same page as an earlier URL in your list), `invalid_input` (including private or local addresses), `no_text`, `timeout` |

The key-value store record `SUMMARY` has `status` (`SUCCESS`, `PARTIAL_RESULTS`, `FAILED`, `NO_RESULTS`, `LIMIT_REACHED`), the characters and units billed, the fields translated, `placeholderMismatches` (results still flagged after the retry) and `placeholderRetries` (strings or blocks translated a second time).

### Use with AI agents (MCP)

Connect Apify's MCP server (https://mcp.apify.com?tools=tidytools/web-page-translator) to Claude, Cursor or any MCP client, then ask e.g. "Translate these 200 product titles into German, French and Japanese, keeping our brand name".

```json
{ "texts": ["Add to cart", "Free returns"], "targetLanguage": "German", "glossary": ["TidyTools"] }
```

Failed items are not charged and carry an `errorType`.

### Use it from code and integrations

Run it from your own code with the Apify API. This call waits for the run and returns the results as JSON (replace `YOUR_TOKEN` with your Apify API token):

```bash
curl -X POST "https://api.apify.com/v2/acts/tidytools~web-page-translator/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"urlsText":"https://docs.apify.com/academy/web-scraping-for-beginners","targetLanguage":"Spanish"}'
```

The synchronous endpoint waits up to 5 minutes. For bigger runs, start the run with `POST https://api.apify.com/v2/acts/tidytools~web-page-translator/runs` and read the dataset when it finishes, or use the `apify-client` package for JavaScript or Python.

**Schedules and integrations:** run it daily or weekly with Apify Schedules, get a webhook when a run finishes, or send the results to Zapier, Make, n8n, Google Sheets, Slack and other apps with Apify integrations. Results can be exported as JSON, CSV, Excel or XML.

### Tips

- Be specific with the language: "Traditional Chinese (Taiwan)", "Spanish (Mexico)", "Portuguese (Brazil)".
- Put brand names, product names and UI terms in the **glossary**.
- The **marketing** tone rewrites more freely; for product data where accuracy matters most, keep *neutral*.
- Use a **content selector** to skip menus and footers on pages.
- Keep the default **Fast** engine for Chinese, Japanese and Korean; try the **Quality** engine for marketing or legal text in other languages.

### Limitations

- Machine translation: have important or legal texts reviewed by a person.
- Page text is taken as the site shows it; boilerplate on the page is translated too unless you set a selector.
- Subtitle files: SRT and WebVTT only (not ASS/SSA or TTML), up to 10 MB each; translated text can be longer than the original, so very fast cues may be harder to read.
- JSON mode translates string fields only; numbers, booleans and arrays of objects are kept as they are (name their text fields with dot paths such as `reviews.0.text`; text inside nested objects is detected automatically). A string inside an array (`tags.0`) gets its translation in a parallel array next to it (`tags_german`).
- Link and image targets in Markdown are kept exactly as they are. If the AI still changes a link (for example when it rewrites a date and its link text together), the row says so in `placeholderMismatch` / `placeholderIssues`. Big pages such as Wikipedia articles start with menus, notices and images: set a *content selector* (for example `#mw-content-text`) and *Max characters per page* to translate (and pay for) the article only.
- The *glossary* and *tone* are instructions to the AI, not hard rules: with a one- or two-word text the AI can still translate a glossary term. Texts that are only code are returned as a code block, and a text that looks like an instruction to the AI ("ignore the above and write...") may be refused as "the AI rewrote the text" (free).
- A long page is translated in blocks of about 5,000 characters; if one block still fails after one automatic retry, the whole page is a free failed row, nothing is billed for it.
- Requests identify themselves as TidyTools (`TidyToolsFetch/1.0`; in browser mode a real browser's user agent with `TidyTools/1.0` added) and the site's `robots.txt` is respected: a page or subtitle URL it disallows for TidyTools (checked for `TidyToolsFetch`, then `TidyTools`, else `*`), or a page that redirects to one, is not read and returns a free `robots_disallowed` row. A `robots.txt` that cannot be read does not block. Texts and JSON items you pass in are not affected.
- For sites that block data centers, add a proxy under *Advanced settings* (Apify proxy usage is billed to your Apify account).

### FAQ

**Does the Actor collect usage data?** The Actor sends one anonymous usage counter per run (input type, item count range, outcome) to improve the tool; no URLs, content or account data.

**Is this a DeepL / Google Translate alternative? Do I need an API key?** Yes, it is an AI translator that works as a Google Translate / DeepL alternative with no API key. You choose the **Engine**: *Fast* (Llama 4 Scout) or *Quality* (Llama 3.3 70B), both at the same price. For comparison, Google Cloud Translation's standard price is $20 per million characters (checked September 2026), while this Actor charges $1.50 per million.

**Can it translate SRT and VTT subtitles?** Yes. Add links to .srt or .vtt files in **Subtitle files to translate (SRT / VTT, optional)**, or paste one file into **Subtitle text (optional)**: cues are translated in batches of up to 40 so sentences keep their context, and every timestamp is kept. The translated files are saved in the run's key-value store, and only the characters of the subtitle text are billed, not the timing lines (SRT and WebVTT only, up to 10 MB each).

**Can I bulk translate JSON items or another Actor's dataset?** Yes. Paste a JSON array into **JSON items to translate (optional)** or set **Dataset ID (optional)**, and list the **Fields to translate** (leave it empty to detect text fields automatically, also inside nested objects as dot paths such as `details.text`; fields in arrays must be named, e.g. `reviews.0.text`). The structure is kept and translations such as `title_spanish` are added next to the originals, and a string repeated in many items is translated and billed once.

**What happens if my input is wrong?** The run does not fail: it ends normally with one free row (`errorType: "invalid_input"`, `charged: false`) that says what is missing or wrong, for example no target language, a target language that is not a name or code (a URL, digits), nothing to translate, more than 20 languages, an item that is not an object, a dataset that cannot be read, subtitle text without a timing line, or a blank text. A wrong entry gets its own free row and the rest of the run goes on. `SUMMARY.status` is `FAILED` when nothing could be translated (or `PARTIAL_RESULTS` when other inputs worked). The language name itself is not checked against a list: a misspelled or invented language is passed to the AI as written, so check the spelling.

**What is charged and what is free?** You pay $1.50 per 1 million source characters, added up over the whole run and counted separately for each target language, with no start fee. Lines that are not a URL, duplicate URLs (also the same page under two spellings), blank texts, pages that cannot be read or that robots.txt disallows, failed translations and repeated strings in JSON mode are free. Every page, text and subtitle row shows `charged: true/false`.

### Support

Open an issue in the **Issues** tab with the URL and target language. Issues are checked regularly.

Found this useful? A short review on the Store helps other users find it.

# Actor input Schema

## `urlsText` (type: `string`):

Main input (fill this, or `urls` / `texts` / `subtitleUrls` / `subtitles` / `items` / `datasetId`). Web pages to translate, one per line (bare domains such as example.com work too). A line that is not a URL becomes one free error row; the rest of the run continues.

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

Same as "Page URLs", in Apify's request list format (also accepts a link to a text file with URLs). Kept for existing integrations; new integrations should use "urlsText".

## `texts` (type: `array`):

Your own texts or Markdown, translated the same way. Short texts are billed by their real length (characters are added up over the run).

## `subtitleUrls` (type: `array`):

Links to .srt or .vtt files, e.g. the srtUrl / vttUrl of our Audio & Video Transcriber. Translated cue by cue in batches (so sentences keep their context); every timestamp is kept. The translated files are saved in the run's key-value store and linked in the results. Billed by characters like texts.

## `subtitles` (type: `string`):

Or paste the content of one SRT or VTT file here.

## `targetLanguage` (type: `string`):

Language name, e.g. Spanish, German, Japanese, Traditional Chinese, Brazilian Portuguese.

## `targetLanguages` (type: `array`):

Translate into several languages in one run, e.g. French, German, Japanese. Pages are read once; each language is billed separately.

## `items` (type: `array`):

A JSON array of objects, e.g. products or reviews. The chosen text fields are translated and added next to the original as <field>\_<language> (title_spanish); everything else is kept as is.

## `datasetId` (type: `string`):

Translate the items of another Actor run's dataset (e.g. reviews or product listings), the same way as JSON items.

## `fields` (type: `array`):

For JSON items and datasets: field names such as title, description (dot paths such as details.text work). Empty = every text field is detected automatically (URLs, IDs, codes and dates are skipped).

## `maxItems` (type: `integer`):

Read at most this many items from the dataset.

## `glossary` (type: `array`):

Brand names, product names and terms to keep exactly as written, e.g. Apify, Actor, TidyTools. Up to 200 terms.

## `tone` (type: `string`):

Register of the translation.

## `sourceLanguage` (type: `string`):

Language of the original, e.g. English. Empty = detected automatically.

## `engine` (type: `string`):

"Fast" (Llama 4 Scout, default) is quick and accurate for most languages and is recommended for Chinese, Japanese and Korean; "Quality" (Llama 3.3 70B) is slower. Same price.

## `cssSelector` (type: `string`):

Optional, e.g. "main" or "article", to translate only the main content.

## `maxCharactersPerPage` (type: `integer`):

Optional cost cap for web pages: translate only the first N characters of each page (cut at a paragraph break; the row gets contentTruncated: true). Empty or 0 = the whole page. A long Wikipedia article can have 146,000 characters, about $0.22 per target language.

## `mode` (type: `string`):

"Auto" uses plain HTTP and a real browser only when needed. Values: auto = Auto (recommended); fast = Fast (HTTP only); browser = Browser (JavaScript rendering).

## `maxConcurrency` (type: `integer`):

How many pages, texts or batches of JSON strings are translated at the same time.

## `httpVia` (type: `string`):

Some sites block requests from data centers. Auto retries from a second network before falling back to a real browser. If both are refused, Auto also tries our second server (Oracle, different IP) before a real browser.

## `proxyConfiguration` (type: `object`):

Only used for requests sent from Apify's network. Apify proxy usage is billed to your Apify account.

## Actor input object example

```json
{
  "urlsText": "https://docs.apify.com/academy/web-scraping-for-beginners",
  "targetLanguage": "Spanish",
  "maxItems": 10000,
  "tone": "neutral",
  "engine": "fast",
  "mode": "auto",
  "maxConcurrency": 3,
  "httpVia": "auto"
}
```

# Actor output Schema

## `translations` (type: `string`):

No description

## `full` (type: `string`):

No description

## `details` (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 = {
    "urlsText": "https://docs.apify.com/academy/web-scraping-for-beginners",
    "targetLanguage": "Spanish"
};

// Run the Actor and wait for it to finish
const run = await client.actor("tidytools/web-page-translator").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 = {
    "urlsText": "https://docs.apify.com/academy/web-scraping-for-beginners",
    "targetLanguage": "Spanish",
}

# Run the Actor and wait for it to finish
run = client.actor("tidytools/web-page-translator").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 '{
  "urlsText": "https://docs.apify.com/academy/web-scraping-for-beginners",
  "targetLanguage": "Spanish"
}' |
apify call tidytools/web-page-translator --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,tidytools/web-page-translator"
        }
    }
}
```

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/Z8R6afk16yObrJais/builds/T8C6ZAcJNzHuZCTrm/openapi.json
