AI Translator - Bulk Translate Text, JSON, Subtitles & Pages avatar

AI Translator - Bulk Translate Text, JSON, Subtitles & Pages

Pricing

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

Go to Apify Store
AI Translator - Bulk Translate Text, JSON, Subtitles & Pages

AI Translator - Bulk Translate Text, JSON, Subtitles & Pages

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.

Pricing

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

Rating

0.0

(0)

Developer

Yukai Lin

Yukai Lin

Maintained by Community

Actor stats

0

Bookmarked

3

Total users

2

Monthly active users

5 hours ago

Last modified

Share

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
  • 🧾 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?

EventPrice
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

{
"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

{
"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

{
"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

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

Real results:

[
{ "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

{
"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:

{
"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":

{ "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)

{
"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):

{
"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 (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

{
"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

FieldMeaning
translatedMarkdownPages and texts: the translation
translatedText, sourceText, indexTexts: the translation (same as translatedMarkdown), the original text (up to 2,000 characters) and its position in your list
input, inputIndexPages: the URL as you entered it and its position in your list
contentTruncated, pageCharactersPages: true when only the first Max characters per page were translated, and the page's full length
chargedPages, texts and subtitles: whether this row was charged
translatedSubtitleUrl, translatedSubtitleSubtitles: link to the translated SRT/VTT file in the run's key-value store, and its content (up to 200,000 characters)
cues, cuesTranslated, formatSubtitles: number of cues, cues translated (cues without letters, such as "♪", are kept as they are), srt or vtt
targetLanguage, sourceCharactersLanguage 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
translationErrorsJSON items: fields that could not be translated (not charged)
placeholderMismatch, placeholderIssuesAfter 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
viaPages: fetched by backend (our servers), direct (Apify's network) or browser
error, errorTypeFor 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".

{ "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):

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.